-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
IndexVersion.java
268 lines (240 loc) · 11.6 KB
/
IndexVersion.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.util;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import org.apache.lucene.index.SegmentCommitInfo;
import org.apache.lucene.index.SegmentInfos;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
/**
* This utility class simply determines the version of a given Solr/Lucene index,
* so that they can be upgraded to the latest version.
* <p>
* You must pass it the full path of the index directory, e.g.
* {@code [dspace]/solr/statistics/data/index/}
* <p>
* The response is simply a version number (e.g. 4.4), as this is utilized by
* the {@code ant update_solr_indexes} target in {@code [src]/dspace/src/main/config/build.xml}
*
* @author tdonohue
*/
public class IndexVersion {
/**
* Default constructor
*/
private IndexVersion() { }
public static void main(String[] argv)
throws IOException {
// Usage checks
if (argv.length < 1) {
System.out.println("\nRequired Solr/Lucene index directory is missing.");
System.out.println("Minimally, pass in the full path of the Solr/Lucene Index directory to analyze");
System.out.println("Usage: IndexVersion [full-path-to-solr-index] ([version-to-compare])");
System.out.println(" - [full-path-to-index] is REQUIRED (e.g. [dspace.dir]/solr/statistics/data/index/)");
System.out.println(" - [version-to-compare] is optional. When specified, this command will return:");
System.out.println(" -1 if index dir version < version-to-compare");
System.out.println(" 0 if index dir version = version-to-compare");
System.out.println(" 1 if index dir version > version-to-compare");
System.out
.println("\nOptionally, passing just '-v' will return the version of Solr/Lucene in use by DSpace.");
System.exit(1);
}
// If "-v" passed on commandline, just return the current version of Solr/Lucene
if (argv[0].equalsIgnoreCase("-v")) {
System.out.println(getLatestVersion());
System.exit(0);
}
// First argument is the Index path. Determine its version
String indexVersion = getIndexVersion(argv[0]);
// Second argumet is an optional version number to compare to
String compareToVersion = argv.length > 1 ? argv[1] : null;
// If indexVersion comes back as null, then it is not a valid index directory.
// So, exit immediately.
if (indexVersion == null) {
System.out.println("\nRequired Solr/Lucene index directory is invalid.");
System.out.println("The following path does NOT seem to be a valid index directory:");
System.out.println(argv[0]);
System.out.println("Please pass in the full path of the Solr/Lucene Index directory to analyze");
System.out.println("(e.g. [dspace.dir]/solr/statistics/data/index/)\n");
System.exit(1);
}
// If empty string is returned as the indexVersion, this means it's an
// empty index directory. So, it's technically "compatible" with any version of Lucene.
if (indexVersion.equals("")) {
indexVersion = getLatestVersion();
}
// If a compare-to-version was passed in, print the result of this comparison
if (compareToVersion != null && !compareToVersion.isEmpty()) {
// If the string "LATEST" is passed, determine which version of Lucene API we are using.
if (compareToVersion.equalsIgnoreCase("LATEST")) {
compareToVersion = getLatestVersion();
}
System.out.println(compareSoftwareVersions(indexVersion, compareToVersion));
} else {
// Otherwise, we'll just print the version of this index directory
System.out.println(indexVersion);
}
System.exit(0);
}
/**
* Determine the version of Solr/Lucene which was used to create a given index directory.
*
* @param indexDirPath Full path of the Solr/Lucene index directory
* @return version as a string (e.g. "4.4"), empty string ("") if index directory is empty,
* or null if directory doesn't exist.
* @throws IOException if IO error
*/
public static String getIndexVersion(String indexDirPath)
throws IOException {
String indexVersion = null;
// Make sure this directory exists
File dir = new File(indexDirPath);
if (dir.exists() && dir.isDirectory()) {
// Check if this index directory has any contents
String[] dirContents = dir.list();
// If this directory is empty, return an empty string.
// It is a valid directory, but it's an empty index.
if (dirContents != null && dirContents.length == 0) {
return "";
}
// Open this index directory in Lucene
Directory indexDir = FSDirectory.open(dir.toPath());
// Get info on the Lucene segment file(s) in index directory
SegmentInfos sis;
try {
sis = SegmentInfos.readLatestCommit(indexDir);
} catch (IOException ie) {
// Wrap default IOException, providing more info about which directory cannot be read
throw new IOException("Could not read Lucene segments files in " + dir.getAbsolutePath(), ie);
}
if (null == sis) {
throw new IOException("Could not read Lucene segments files in " + dir.getAbsolutePath());
}
// If we have a valid Solr index dir, but it has no existing segments
// then just return an empty string. It's a valid but empty index.
if (sis.size() == 0) {
return "";
}
// Loop through our Lucene segment files to locate the OLDEST
// version. It is possible for individual segment files to be
// created by different versions of Lucene. So, we just need
// to find the oldest version of Lucene which created these
// index segment files.
// This logic borrowed from Lucene v.4.10 CheckIndex class:
// https://github.com/apache/lucene-solr/blob/lucene_solr_4_10/lucene/core/src/java/org/apache/lucene
// /index/CheckIndex.java#L426
// WARNING: It MAY require updating whenever we upgrade the
// "lucene.version" in our DSpace Parent POM
Version oldest = null;
Version oldSegment = null;
for (SegmentCommitInfo si : sis) {
// Get the version of Lucene which created this segment file
Version version = si.info.getVersion();
if (version == null) {
// If null, then this is a pre-3.1 segment file.
// For our purposes, we will just assume it is "3.0",
// This lets us know we will need to upgrade it to 3.5
// before upgrading to Solr/Lucene 4.x or above
try {
oldSegment = Version.parse("3.0");
} catch (ParseException pe) {
throw new IOException(pe);
}
} else if (oldest == null || version.onOrAfter(oldest) == false) {
// else if this segment is older than our oldest thus far
// We have a new oldest segment version
oldest = version;
}
}
// If we found a really old segment, compare it to the oldest
// to see which is actually older
if (oldSegment != null && oldSegment.onOrAfter(oldest) == false) {
oldest = oldSegment;
}
// At this point, we should know what version of Lucene created our
// oldest segment file. We will return this as the Index version
// as it's the oldest segment we will need to upgrade.
if (oldest != null) {
indexVersion = oldest.toString();
}
}
return indexVersion;
}
/**
* Compare two software version numbers to see which is greater. ONLY does
* a comparison of *major* and *minor* versions (any sub-minor versions are
* stripped and ignored).
* <P>
* This method returns -1 if firstVersion is less than secondVersion,
* 1 if firstVersion is greater than secondVersion, and 0 if equal.
* <P>
* However, since we ignore sub-minor versions, versions "4.2.1" and "4.2.5"
* will be seen as EQUAL (as "4.2" = "4.2").
* <P>
* NOTE: In case it is not obvious, software version numbering does NOT
* behave like normal decimal numbers. For example, in software versions
* the following statement is TRUE: {@code 4.1 < 4.4 < 4.5 < 4.10 < 4.21 < 4.51}
*
* @param firstVersion First version to compare, as a String
* @param secondVersion Second version to compare as a String
* @return -1 if first less than second, 1 if first greater than second, 0 if equal
* @throws IOException if IO error
*/
public static int compareSoftwareVersions(String firstVersion, String secondVersion)
throws IOException {
// Constants which represent our various return values for this comparison
int GREATER_THAN = 1;
int EQUAL = 0;
int LESS_THAN = -1;
// "null" is less than anything
if (firstVersion == null) {
return LESS_THAN;
}
// Anything is newer than "null"
if (secondVersion == null) {
return GREATER_THAN;
}
//Split the first version into it's parts (i.e. major & minor versions)
String[] firstParts = firstVersion.split("\\.");
String[] secondParts = secondVersion.split("\\.");
// Get major / minor version numbers. Default to "0" if unspecified
// NOTE: We are specifically IGNORING any sub-minor version numbers
int firstMajor = firstParts.length >= 1 ? Integer.parseInt(firstParts[0]) : 0;
int firstMinor = firstParts.length >= 2 ? Integer.parseInt(firstParts[1]) : 0;
int secondMajor = secondParts.length >= 1 ? Integer.parseInt(secondParts[0]) : 0;
int secondMinor = secondParts.length >= 2 ? Integer.parseInt(secondParts[1]) : 0;
// Check for equality
if (firstMajor == secondMajor && firstMinor == secondMinor) {
return EQUAL;
} else if (firstMajor > secondMajor) {
// If first major version is greater than second
return GREATER_THAN;
} else if (firstMajor < secondMajor) {
// If first major version is less than second
return LESS_THAN;
} else if (firstMinor > secondMinor) {
// If we get here, major versions must be EQUAL. Now, time to check our minor versions
return GREATER_THAN;
} else {
return LESS_THAN;
}
}
/**
* Determine the version of Solr/Lucene which DSpace is currently running.
* This is the latest version of Solr/Lucene which we can upgrade the index to.
*
* @return version as a string (e.g. "4.4")
*/
public static String getLatestVersion() {
// The current version of lucene is in the "LATEST" constant
return org.apache.lucene.util.Version.LATEST.toString();
}
}