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, 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 IndexAnalysisSettings implements Serializable { 45 46 private static final long serialVersionUID = 1172403004716059609L; 47 48 private static final ObjectInputFilter serialFilter = new WhitelistObjectInputFilter(IndexAnalysisSettings.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 * Gets the project name to be used to distinguish different instances of 76 * {@link IndexAnalysisSettings} that might be returned by a Lucene 77 * {@code MultiReader} search across projects. 78 * @return projectName 79 */ getProjectName()80 public String getProjectName() { 81 return projectName; 82 } 83 84 /** 85 * Sets the project name to be used to distinguish different instances of 86 * {@link IndexAnalysisSettings} that might be returned by a Lucene 87 * {@code MultiReader} search across projects. 88 * @param value project name 89 */ setProjectName(String value)90 public void setProjectName(String value) { 91 this.projectName = value; 92 } 93 getTabSize()94 public Integer getTabSize() { 95 return tabSize; 96 } 97 setTabSize(Integer value)98 public void setTabSize(Integer value) { 99 this.tabSize = value; 100 } 101 getAnalyzerGuruVersion()102 public Long getAnalyzerGuruVersion() { 103 return analyzerGuruVersion; 104 } 105 setAnalyzerGuruVersion(Long value)106 public void setAnalyzerGuruVersion(Long value) { 107 this.analyzerGuruVersion = value; 108 } 109 110 /** 111 * Gets the version number for the specified file type name if it exists. 112 * @param fileTypeName name of the file type 113 * @return a defined value or {@code null} if unknown 114 */ getAnalyzerVersion(String fileTypeName)115 public Long getAnalyzerVersion(String fileTypeName) { 116 return analyzersVersions.get(fileTypeName); 117 } 118 119 /** 120 * Gets an unmodifiable view of the map of file type names to version 121 * numbers. 122 * @return a defined instance 123 */ getAnalyzersVersions()124 public Map<String, Long> getAnalyzersVersions() { 125 return Collections.unmodifiableMap(analyzersVersions); 126 } 127 128 /** 129 * Replaces the contents of the instance's map with the {@code values}. 130 * @param values a defined instance 131 */ setAnalyzersVersions(Map<String, Long> values)132 public void setAnalyzersVersions(Map<String, Long> values) { 133 analyzersVersions.clear(); 134 analyzersVersions.putAll(values); 135 } 136 137 /** 138 * Creates a binary representation of this object. 139 * @return a byte array representing this object 140 * @throws IOException Any exception thrown by the underlying 141 * OutputStream. 142 */ serialize()143 public byte[] serialize() throws IOException { 144 try (ByteArrayOutputStream bytes = new ByteArrayOutputStream(); var oos = new ObjectOutputStream(bytes)) { 145 oos.writeObject(this); 146 return bytes.toByteArray(); 147 } 148 } 149 150 /** 151 * De-serializes a binary representation of an {@link IndexAnalysisSettings} 152 * object. 153 * @param bytes a byte array containing the serialization 154 * @return a defined instance 155 * @throws IOException Any of the usual Input/Output related exceptions. 156 * @throws ClassNotFoundException Class of a serialized object cannot be 157 * found. 158 * @throws ClassCastException if the array contains an object of another 159 * type than {@code IndexAnalysisSettings} 160 */ deserialize(byte[] bytes)161 public static IndexAnalysisSettings deserialize(byte[] bytes) throws IOException, ClassNotFoundException { 162 try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes))) { 163 in.setObjectInputFilter(serialFilter); 164 return (IndexAnalysisSettings) in.readObject(); 165 } 166 } 167 readObject(ObjectInputStream in)168 private void readObject(ObjectInputStream in) throws ClassNotFoundException, 169 IOException { 170 171 boolean hasValue = in.readBoolean(); 172 String vstring = in.readUTF(); 173 projectName = hasValue ? vstring : null; 174 175 hasValue = in.readBoolean(); 176 int vint = in.readInt(); 177 tabSize = hasValue ? vint : null; 178 179 hasValue = in.readBoolean(); 180 long vlong = in.readLong(); 181 analyzerGuruVersion = hasValue ? vlong : null; 182 183 /** 184 * De-serialization circumvents normal construction, so the following 185 * field could be null. 186 */ 187 if (analyzersVersions == null) { 188 analyzersVersions = new HashMap<>(); 189 } 190 int analyzerCount = in.readInt(); 191 for (int i = 0; i < analyzerCount; ++i) { 192 vstring = in.readUTF(); 193 vlong = in.readLong(); 194 analyzersVersions.put(vstring, vlong); 195 } 196 } 197 writeObject(ObjectOutputStream out)198 private void writeObject(ObjectOutputStream out) throws IOException { 199 out.writeBoolean(projectName != null); // hasValue 200 out.writeUTF(projectName == null ? "" : projectName); 201 202 out.writeBoolean(tabSize != null); // hasValue 203 out.writeInt(tabSize == null ? 0 : tabSize); 204 205 out.writeBoolean(analyzerGuruVersion != null); // hasValue 206 out.writeLong(analyzerGuruVersion == null ? 0 : analyzerGuruVersion); 207 208 int analyzerCount = analyzersVersions.size(); 209 out.writeInt(analyzerCount); 210 for (Map.Entry<String, Long> entry : analyzersVersions.entrySet()) { 211 out.writeUTF(entry.getKey()); 212 out.writeLong(entry.getValue()); 213 --analyzerCount; 214 } 215 if (analyzerCount != 0) { 216 throw new IllegalStateException("analyzersVersions were modified"); 217 } 218 } 219 } 220