org.dspace.app.util.IndexVersion Maven / Gradle / Ivy
Show all versions of dspace-api Show documentation
/**
* 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.
*
* You must pass it the full path of the index directory, e.g.
* {@code [dspace]/solr/statistics/data/index/}
*
* 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).
*
* This method returns -1 if firstVersion is less than secondVersion,
* 1 if firstVersion is greater than secondVersion, and 0 if equal.
*
* 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").
*
* 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();
}
}