Fix initialization issue in ElasticsearchException
If a test touches ElasticsearchExceptionHandle before the class initialzer for ElasticsearchException has run, a circular class initialization problem can arise. Namely, the class initializer for ElasticsearchExceptionHandle depends on the class initializer for ElasticsearchExceptionHandle which depends on the class initializer for all the classes that extend ElasticsearchException, but these classes can not be loaded because ElasticsearchException has not finished its class initializer. There are tests that can trigger this before ElasticsearchException has been loaded due to an unlucky ordering of test execution. This commit addresses this issue by making ElasticsearchExceptionHandle private, and then exposing methods that provide the necessary values from ElasticsearchExceptionHandle. Touching these methods will force the class initializer for ElasticsearchException to run first.
This commit is contained in:
parent
3274eab41d
commit
6234a49fb3
|
@ -23,6 +23,7 @@ import org.elasticsearch.action.support.replication.ReplicationOperation;
|
|||
import org.elasticsearch.cluster.action.shard.ShardStateAction;
|
||||
import org.elasticsearch.common.CheckedFunction;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
|
@ -712,7 +713,7 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
|
|||
* in id order below. If you want to remove an exception leave a tombstone comment and mark the id as null in
|
||||
* ExceptionSerializationTests.testIds.ids.
|
||||
*/
|
||||
enum ElasticsearchExceptionHandle {
|
||||
private enum ElasticsearchExceptionHandle {
|
||||
INDEX_SHARD_SNAPSHOT_FAILED_EXCEPTION(org.elasticsearch.index.snapshots.IndexShardSnapshotFailedException.class,
|
||||
org.elasticsearch.index.snapshots.IndexShardSnapshotFailedException::new, 0, UNKNOWN_VERSION_ADDED),
|
||||
DFS_PHASE_EXECUTION_EXCEPTION(org.elasticsearch.search.dfs.DfsPhaseExecutionException.class,
|
||||
|
@ -1006,6 +1007,30 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all registered handle IDs. These are the IDs for every registered
|
||||
* exception.
|
||||
*
|
||||
* @return an array of all registered handle IDs
|
||||
*/
|
||||
static int[] ids() {
|
||||
return Arrays.stream(ElasticsearchExceptionHandle.values()).mapToInt(h -> h.id).toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all registered pairs of handle IDs and exception classes. These pairs are
|
||||
* provided for every registered exception.
|
||||
*
|
||||
* @return an array of all registered pairs of handle IDs and exception classes
|
||||
*/
|
||||
static Tuple<Integer, Class<? extends ElasticsearchException>>[] classes() {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Tuple<Integer, Class<? extends ElasticsearchException>>[] ts =
|
||||
Arrays.stream(ElasticsearchExceptionHandle.values())
|
||||
.map(h -> Tuple.tuple(h.id, h.exceptionClass)).toArray(Tuple[]::new);
|
||||
return ts;
|
||||
}
|
||||
|
||||
static {
|
||||
ID_TO_SUPPLIER = unmodifiableMap(Arrays
|
||||
.stream(ElasticsearchExceptionHandle.values()).collect(Collectors.toMap(e -> e.id, e -> e.constructor)));
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.elasticsearch.common.UUIDs;
|
|||
import org.elasticsearch.common.breaker.CircuitBreakingException;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.io.PathUtils;
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.io.stream.NotSerializableExceptionWrapper;
|
||||
|
@ -680,15 +681,15 @@ public class ExceptionSerializationTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testThatIdsArePositive() {
|
||||
for (ElasticsearchException.ElasticsearchExceptionHandle handle : ElasticsearchException.ElasticsearchExceptionHandle.values()) {
|
||||
assertThat("negative id", handle.id, greaterThanOrEqualTo(0));
|
||||
for (final int id : ElasticsearchException.ids()) {
|
||||
assertThat("negative id", id, greaterThanOrEqualTo(0));
|
||||
}
|
||||
}
|
||||
|
||||
public void testThatIdsAreUnique() {
|
||||
Set<Integer> ids = new HashSet<>();
|
||||
for (ElasticsearchException.ElasticsearchExceptionHandle handle : ElasticsearchException.ElasticsearchExceptionHandle.values()) {
|
||||
assertTrue("duplicate id", ids.add(handle.id));
|
||||
final Set<Integer> ids = new HashSet<>();
|
||||
for (final int id: ElasticsearchException.ids()) {
|
||||
assertTrue("duplicate id", ids.add(id));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -848,8 +849,9 @@ public class ExceptionSerializationTests extends ESTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
for (ElasticsearchException.ElasticsearchExceptionHandle handle : ElasticsearchException.ElasticsearchExceptionHandle.values()) {
|
||||
assertEquals((int) reverse.get(handle.exceptionClass), handle.id);
|
||||
for (final Tuple<Integer, Class<? extends ElasticsearchException>> tuple : ElasticsearchException.classes()) {
|
||||
assertNotNull(tuple.v1());
|
||||
assertEquals((int) reverse.get(tuple.v2()), (int)tuple.v1());
|
||||
}
|
||||
|
||||
for (Map.Entry<Integer, Class<? extends ElasticsearchException>> entry : ids.entrySet()) {
|
||||
|
|
Loading…
Reference in New Issue