/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.bootstrap;
import org.apache.lucene.index.MergePolicy;
import org.elasticsearch.test.ESTestCase;
import org.junit.Before;
import java.io.IOError;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import static org.hamcrest.CoreMatchers.equalTo;
public class ElasticsearchUncaughtExceptionHandlerTests extends ESTestCase {
private Map<Class<? extends Error>, Integer> expectedStatus;
@Before
public void setUp() throws Exception {
super.setUp();
Map<Class<? extends Error>, Integer> expectedStatus = new HashMap<>();
expectedStatus.put(InternalError.class, 128);
expectedStatus.put(OutOfMemoryError.class, 127);
expectedStatus.put(StackOverflowError.class, 126);
expectedStatus.put(UnknownError.class, 125);
expectedStatus.put(IOError.class, 124);
this.expectedStatus = Collections.unmodifiableMap(expectedStatus);
}
public void testUncaughtError() throws InterruptedException {
final Error error = randomFrom(
new InternalError(),
new OutOfMemoryError(),
new StackOverflowError(),
new UnknownError(),
new IOError(new IOException("fatal")),
new Error() {});
final Thread thread = new Thread(() -> { throw error; });
final String name = randomAlphaOfLength(10);
thread.setName(name);
final AtomicBoolean halt = new AtomicBoolean();
final AtomicInteger observedStatus = new AtomicInteger();
final AtomicReference<String> threadNameReference = new AtomicReference<>();
final AtomicReference<Throwable> throwableReference = new AtomicReference<>();
thread.setUncaughtExceptionHandler(new ElasticsearchUncaughtExceptionHandler(() -> "testUncaughtError") {
@Override
void halt(int status) {
halt.set(true);
observedStatus.set(status);
}
@Override
void onFatalUncaught(String threadName, Throwable t) {
threadNameReference.set(threadName);
throwableReference.set(t);
}
@Override
void onNonFatalUncaught(String threadName, Throwable t) {
fail();
}
});
thread.start();
thread.join();
assertTrue(halt.get());
final int status;
if (expectedStatus.containsKey(error.getClass())) {
status = expectedStatus.get(error.getClass());
} else {
status = 1;
}
assertThat(observedStatus.get(), equalTo(status));
assertThat(threadNameReference.get(), equalTo(name));
assertThat(throwableReference.get(), equalTo(error));
}
public void testUncaughtException() throws InterruptedException {
final RuntimeException e = new RuntimeException("boom");
final Thread thread = new Thread(() -> { throw e; });
final String name = randomAlphaOfLength(10);
thread.setName(name);
final AtomicReference<String> threadNameReference = new AtomicReference<>();
final AtomicReference<Throwable> throwableReference = new AtomicReference<>();
thread.setUncaughtExceptionHandler(new ElasticsearchUncaughtExceptionHandler(() -> "testUncaughtException") {
@Override
void halt(int status) {
fail();
}
@Override
void onFatalUncaught(String threadName, Throwable t) {
fail();
}
@Override
void onNonFatalUncaught(String threadName, Throwable t) {
threadNameReference.set(threadName);
throwableReference.set(t);
}
});
thread.start();
thread.join();
assertThat(threadNameReference.get(), equalTo(name));
assertThat(throwableReference.get(), equalTo(e));
}
public void testIsFatalCause() {
assertFatal(new MergePolicy.MergeException(new OutOfMemoryError(), null));
assertFatal(new OutOfMemoryError());
assertFatal(new StackOverflowError());
assertFatal(new InternalError());
assertFatal(new UnknownError());
assertFatal(new IOError(new IOException()));
assertNonFatal(new RuntimeException());
assertNonFatal(new UncheckedIOException(new IOException()));
}
private void assertFatal(Throwable cause) {
assertTrue(ElasticsearchUncaughtExceptionHandler.isFatalUncaught(cause));
}
private void assertNonFatal(Throwable cause) {
assertFalse(ElasticsearchUncaughtExceptionHandler.isFatalUncaught(cause));
}
}