1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * See LICENSE.txt included in this distribution for the specific 9 * language governing permissions and limitations under the License. 10 * 11 * When distributing Covered Code, include this CDDL HEADER in each 12 * file and include the License file at LICENSE.txt. 13 * If applicable, add the following below this CDDL HEADER, with the 14 * fields enclosed by brackets "[]" replaced with your own identifying 15 * information: Portions Copyright [yyyy] [name of copyright owner] 16 * 17 * CDDL HEADER END 18 */ 19 20 /* 21 * Copyright (c) 2018, 2019, Chris Fraire <cfraire@me.com>. 22 */ 23 package org.opengrok.indexer.index; 24 25 import org.opengrok.indexer.util.WhitelistObjectInputFilter; 26 27 import java.io.ByteArrayInputStream; 28 import java.io.ByteArrayOutputStream; 29 import java.io.IOException; 30 import java.io.ObjectInputFilter; 31 import java.io.ObjectInputStream; 32 import java.io.ObjectOutputStream; 33 import java.io.Serializable; 34 import java.util.Collections; 35 import java.util.HashMap; 36 import java.util.Map; 37 38 /** 39 * Represents a serializable gathering of some top-level metadata concerning the 40 * operation of {@link IndexDatabase} -- and persisted therein too -- which are 41 * re-compared upon each indexing run since changes to them might require 42 * re-indexing particular files or in certain cases all files. 43 */ 44 public final class IndexAnalysisSettings3 implements Serializable { 45 46 private static final long serialVersionUID = -4700122690707062276L; 47 48 private static final ObjectInputFilter serialFilter = new WhitelistObjectInputFilter(IndexAnalysisSettings3.class); 49 50 private String projectName; 51 52 /** 53 * Nullable to allow easing this object into existing OpenGrok indexes 54 * without forcing a re-indexing. 55 * @serial 56 */ 57 private Integer tabSize; 58 59 /** 60 * Nullable to allow easing this object into existing OpenGrok indexes 61 * without forcing a re-indexing. 62 * @serial 63 */ 64 private Long analyzerGuruVersion; 65 66 /** 67 * Nullable because otherwise custom de-serialization does not work, as a 68 * {@code final} initialized value may not actually happen because Java 69 * de-serialization circumvents normal construction. 70 * @serial 71 */ 72 private Map<String, Long> analyzersVersions = new HashMap<>(); 73 74 /** 75 * Nullable because otherwise custom de-serialization does not work, as a 76 * {@code final} initialized value may not actually happen because Java 77 * de-serialization circumvents normal construction. We don't bother with 78 * anything but a simple {@link HashMap} here. 79 * @serial 80 */ 81 private Map<String, IndexedSymlink> indexedSymlinks = new HashMap<>(); 82 83 /** 84 * Gets the project name to be used to distinguish different instances of 85 * {@link IndexAnalysisSettings3} that might be returned by a Lucene 86 * {@code MultiReader} search across projects. 87 * @return projectName 88 */ getProjectName()89 public String getProjectName() { 90 return projectName; 91 } 92 93 /** 94 * Sets the project name to be used to distinguish different instances of 95 * {@link IndexAnalysisSettings3} that might be returned by a Lucene 96 * {@code MultiReader} search across projects. 97 * @param value project name 98 */ setProjectName(String value)99 public void setProjectName(String value) { 100 this.projectName = value; 101 } 102 getTabSize()103 public Integer getTabSize() { 104 return tabSize; 105 } 106 setTabSize(Integer value)107 public void setTabSize(Integer value) { 108 this.tabSize = value; 109 } 110 getAnalyzerGuruVersion()111 public Long getAnalyzerGuruVersion() { 112 return analyzerGuruVersion; 113 } 114 setAnalyzerGuruVersion(Long value)115 public void setAnalyzerGuruVersion(Long value) { 116 this.analyzerGuruVersion = value; 117 } 118 119 /** 120 * Gets the version number for the specified file type name if it exists. 121 * @param fileTypeName name of the file type 122 * @return a defined value or {@code null} if unknown 123 */ getAnalyzerVersion(String fileTypeName)124 public Long getAnalyzerVersion(String fileTypeName) { 125 return analyzersVersions.get(fileTypeName); 126 } 127 128 /** 129 * Gets an unmodifiable view of the map of file type names to version 130 * numbers. 131 * @return a defined instance 132 */ getAnalyzersVersions()133 public Map<String, Long> getAnalyzersVersions() { 134 return Collections.unmodifiableMap(analyzersVersions); 135 } 136 137 /** 138 * Replaces the contents of the instance's map with the {@code values}. 139 * @param values a defined instance 140 */ setAnalyzersVersions(Map<String, Long> values)141 public void setAnalyzersVersions(Map<String, Long> values) { 142 analyzersVersions.clear(); 143 analyzersVersions.putAll(values); 144 } 145 146 /** 147 * Gets an unmodifiable view of the map of canonical file names to symlinks. 148 * @return a defined instance 149 */ getIndexedSymlinks()150 public Map<String, IndexedSymlink> getIndexedSymlinks() { 151 return Collections.unmodifiableMap(indexedSymlinks); 152 } 153 154 /** 155 * Replaces the contents of the instance's map with the {@code values}. 156 * @param values a defined instance 157 */ setIndexedSymlinks(Map<String, IndexedSymlink> values)158 public void setIndexedSymlinks(Map<String, IndexedSymlink> values) { 159 indexedSymlinks.clear(); 160 indexedSymlinks.putAll(values); 161 } 162 163 /** 164 * Creates a binary representation of this object. 165 * @return a byte array representing this object 166 * @throws IOException Any exception thrown by the underlying 167 * OutputStream. 168 */ serialize()169 public byte[] serialize() throws IOException { 170 try (ByteArrayOutputStream bytes = new ByteArrayOutputStream(); var oos = new ObjectOutputStream(bytes)) { 171 oos.writeObject(this); 172 return bytes.toByteArray(); 173 } 174 } 175 176 /** 177 * De-serializes a binary representation of an {@link IndexAnalysisSettings3} 178 * object. 179 * @param bytes a byte array containing the serialization 180 * @return a defined instance 181 * @throws IOException Any of the usual Input/Output related exceptions. 182 * @throws ClassNotFoundException Class of a serialized object cannot be 183 * found. 184 * @throws ClassCastException if the array contains an object of another 185 * type than {@code IndexAnalysisSettings} 186 */ deserialize(byte[] bytes)187 public static IndexAnalysisSettings3 deserialize(byte[] bytes) throws IOException, ClassNotFoundException { 188 try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes))) { 189 in.setObjectInputFilter(serialFilter); 190 return (IndexAnalysisSettings3) in.readObject(); 191 } 192 } 193 194 @SuppressWarnings("Duplicates") readObject(ObjectInputStream in)195 private void readObject(ObjectInputStream in) throws ClassNotFoundException, 196 IOException { 197 198 boolean hasValue = in.readBoolean(); 199 String vString = in.readUTF(); 200 projectName = hasValue ? vString : null; 201 202 hasValue = in.readBoolean(); 203 int vInteger = in.readInt(); 204 tabSize = hasValue ? vInteger : null; 205 206 hasValue = in.readBoolean(); 207 long vLong = in.readLong(); 208 analyzerGuruVersion = hasValue ? vLong : null; 209 210 /* 211 * De-serialization circumvents normal construction, so the following 212 * field could be null. 213 */ 214 if (analyzersVersions == null) { 215 analyzersVersions = new HashMap<>(); 216 } 217 int analyzerCount = in.readInt(); 218 for (int i = 0; i < analyzerCount; ++i) { 219 vString = in.readUTF(); 220 vLong = in.readLong(); 221 analyzersVersions.put(vString, vLong); 222 } 223 224 /* 225 * De-serialization circumvents normal construction, so the following 226 * field could be null. 227 */ 228 if (indexedSymlinks == null) { 229 indexedSymlinks = new HashMap<>(); 230 } 231 int symlinkCount = in.readInt(); 232 for (int i = 0; i < symlinkCount; ++i) { 233 String absolute = in.readUTF(); 234 String canonical = in.readUTF(); 235 boolean isLocal = in.readBoolean(); 236 IndexedSymlink indexed = new IndexedSymlink(absolute, canonical, isLocal); 237 indexedSymlinks.put(canonical, indexed); 238 } 239 } 240 241 @SuppressWarnings("Duplicates") writeObject(ObjectOutputStream out)242 private void writeObject(ObjectOutputStream out) throws IOException { 243 out.writeBoolean(projectName != null); // hasValue 244 out.writeUTF(projectName == null ? "" : projectName); 245 246 out.writeBoolean(tabSize != null); // hasValue 247 out.writeInt(tabSize == null ? 0 : tabSize); 248 249 out.writeBoolean(analyzerGuruVersion != null); // hasValue 250 out.writeLong(analyzerGuruVersion == null ? 0 : analyzerGuruVersion); 251 252 int collectionCount = analyzersVersions.size(); 253 out.writeInt(collectionCount); 254 for (Map.Entry<String, Long> entry : analyzersVersions.entrySet()) { 255 out.writeUTF(entry.getKey()); 256 out.writeLong(entry.getValue()); 257 --collectionCount; 258 } 259 if (collectionCount != 0) { 260 throw new IllegalStateException("analyzersVersions were modified"); 261 } 262 263 collectionCount = indexedSymlinks.size(); 264 out.writeInt(collectionCount); 265 for (IndexedSymlink entry : indexedSymlinks.values()) { 266 out.writeUTF(entry.getAbsolute()); 267 out.writeUTF(entry.getCanonical()); 268 out.writeBoolean(entry.isLocal()); 269 --collectionCount; 270 } 271 if (collectionCount != 0) { 272 throw new IllegalStateException("indexedSymlinks were modified"); 273 } 274 } 275 } 276