[CORE] Support parsing lucene minor version strings

We parse the version that is shipped with the Lucene segments in order
to find the version of lucene that wrote a particular segment. Yet, some lucene
version ie:
 * 4.3.1 (Elasticsearch 0.90.2)
 * 4.5.1 (Elasticsearch 0.90.7)
 * 3.6.1 (pre Elasticsearch 0.90.0)

wrote illegal strings containing the minor version which causes IAE exceptions
being thrown from lucenes parsing method.

Closes #7055
This commit is contained in:
Simon Willnauer 2014-07-28 12:43:37 +02:00
parent 07c9b5b08d
commit d2493ea48a
9 changed files with 61 additions and 8 deletions

View File

@ -66,3 +66,6 @@ java.nio.channels.ReadableByteChannel#read(java.nio.ByteBuffer)
java.nio.channels.ScatteringByteChannel#read(java.nio.ByteBuffer[])
java.nio.channels.ScatteringByteChannel#read(java.nio.ByteBuffer[], int, int)
java.nio.channels.FileChannel#read(java.nio.ByteBuffer, long)
@defaultMessage Use Lucene.parseLenient instead it strips off minor version
org.apache.lucene.util.Version#parseLeniently(java.lang.String)

View File

@ -1169,6 +1169,9 @@
<!-- start exclude for Channels utility class -->
<exclude>org/elasticsearch/common/io/Channels.class</exclude>
<!-- end exclude for Channels -->
<!-- start exclude for Lucene utility class -->
<exclude>org/elasticsearch/common/lucene/Lucene$LenientParser.class</exclude>
<!-- end exclude for Lucene -->
</excludes>
<bundledSignatures>
<!-- This will automatically choose the right signatures based on 'targetVersion': -->

View File

@ -33,6 +33,7 @@ import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.logging.ESLogger;
@ -40,8 +41,10 @@ import org.elasticsearch.index.analysis.AnalyzerScope;
import org.elasticsearch.index.analysis.NamedAnalyzer;
import org.elasticsearch.index.fielddata.IndexFieldData;
import static org.elasticsearch.common.lucene.search.NoopCollector.NOOP_COLLECTOR;
import java.io.IOException;
import java.util.Locale;
import static org.elasticsearch.common.lucene.search.NoopCollector.NOOP_COLLECTOR;
/**
*
@ -537,4 +540,27 @@ public class Lucene {
public static boolean isCorruptionException(Throwable t) {
return ExceptionsHelper.unwrap(t, CorruptIndexException.class) != null;
}
/**
* Parses the version string lenient and returns the the default value if the given string is null or emtpy
*/
public static Version parseVersionLenient(String toParse, Version defaultValue) {
return LenientParser.parse(toParse, defaultValue);
}
private static final class LenientParser {
public static Version parse(String toParse, Version defaultValue) {
if (Strings.hasLength(toParse)) {
try {
return Version.parseLeniently(toParse);
} catch (IllegalArgumentException e) {
final String parsedMatchVersion = toParse
.toUpperCase(Locale.ROOT)
.replaceFirst("^(\\d+)\\.(\\d+).(\\d+)$", "LUCENE_$1_$2");
return Version.valueOf(parsedMatchVersion);
}
}
return defaultValue;
}
}
}

View File

@ -24,6 +24,7 @@ import org.apache.lucene.util.Version;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
@ -255,7 +256,7 @@ public class BlobStoreIndexShardSnapshot {
} else if ("part_size".equals(currentFieldName)) {
partSize = new ByteSizeValue(parser.longValue());
} else if ("written_by".equals(currentFieldName)) {
writtenBy = Version.parseLeniently(parser.text());
writtenBy = Lucene.parseVersionLenient(parser.text(), null);
} else {
throw new ElasticsearchParseException("unknown parameter [" + currentFieldName + "]");
}

View File

@ -448,7 +448,7 @@ public class Store extends AbstractIndexShardComponent implements CloseableIndex
Version maxVersion = Version.LUCENE_3_0; // we don't know which version was used to write so we take the max version.
Set<String> added = new HashSet<>();
for (SegmentCommitInfo info : segmentCommitInfos) {
final Version version = Version.parseLeniently(info.info.getVersion());
final Version version = Lucene.parseVersionLenient(info.info.getVersion(), Version.LUCENE_3_0);
if (version.onOrAfter(maxVersion)) {
maxVersion = version;
}

View File

@ -19,12 +19,12 @@
package org.elasticsearch.index.store;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.Version;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.lucene.Lucene;
import java.io.IOException;
@ -102,7 +102,7 @@ public class StoreFileMetaData implements Streamable {
checksum = in.readOptionalString();
if (in.getVersion().onOrAfter(org.elasticsearch.Version.V_1_3_0)) {
String versionString = in.readOptionalString();
writtenBy = versionString == null ? null : Version.parseLeniently(versionString);
writtenBy = Lucene.parseVersionLenient(versionString, null);
}
}

View File

@ -21,10 +21,10 @@ package org.elasticsearch.indices.recovery;
import org.apache.lucene.util.Version;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.store.StoreFileMetaData;
import org.elasticsearch.transport.TransportRequest;
@ -101,7 +101,7 @@ public final class RecoveryFileChunkRequest extends TransportRequest { // publi
Version writtenBy = null;
if (in.getVersion().onOrAfter(org.elasticsearch.Version.V_1_3_0)) {
String versionString = in.readOptionalString();
writtenBy = versionString == null ? null : Version.parseLeniently(versionString);
writtenBy = Lucene.parseVersionLenient(versionString, null);
}
metaData = new StoreFileMetaData(name, length, checksum, writtenBy);
}

View File

@ -33,6 +33,7 @@ import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.component.LifecycleComponent;
import org.elasticsearch.common.inject.Module;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
@ -571,7 +572,7 @@ public class PluginsService extends AbstractComponent {
String parts[] = luceneVersion.split("\\.");
// Should fail if the running node is too old!
org.apache.lucene.util.Version luceneExpectedVersion = org.apache.lucene.util.Version.parseLeniently(parts[0]+"."+parts[1]);
org.apache.lucene.util.Version luceneExpectedVersion = Lucene.parseVersionLenient(parts[0] + "." + parts[1], null);
if (Version.CURRENT.luceneVersion.equals(luceneExpectedVersion)) {
logger.debug("starting analysis plugin for Lucene [{}].", luceneExpectedVersion);

View File

@ -20,10 +20,14 @@
package org.elasticsearch;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.hamcrest.Matchers;
import org.junit.Test;
import java.util.Locale;
import static org.elasticsearch.Version.V_0_20_0;
import static org.elasticsearch.Version.V_0_90_0;
import static org.hamcrest.CoreMatchers.equalTo;
@ -114,4 +118,19 @@ public class VersionTests extends ElasticsearchTestCase {
assertThat(Version.V_1_0_0_RC2.minimumCompatibilityVersion(), equalTo(Version.V_1_0_0_RC2));
}
@Test
public void parseLenient() {
int numIters = randomIntBetween(10, 100);
for (int i = 0; i < numIters; i++) {
Version version = randomVersion(getRandom());
org.apache.lucene.util.Version luceneVersion = version.luceneVersion;
String string = luceneVersion.name().toUpperCase(Locale.ROOT)
.replaceFirst("^LUCENE_(\\d+)_(\\d+)$", "$1.$2");
if (randomBoolean()) {
string = string + "." + randomIntBetween(0, 100);
}
assertThat(luceneVersion, Matchers.equalTo(Lucene.parseVersionLenient(string, null)));
}
}
}