diff --git a/hazelcast/src/main/java/com/hazelcast/client/HazelcastClientOfflineException.java b/hazelcast/src/main/java/com/hazelcast/client/HazelcastClientOfflineException.java index f3e2366a08d2..96d5b66d80c0 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/HazelcastClientOfflineException.java +++ b/hazelcast/src/main/java/com/hazelcast/client/HazelcastClientOfflineException.java @@ -24,4 +24,8 @@ public class HazelcastClientOfflineException extends IllegalStateException { public HazelcastClientOfflineException() { super("No connection found to cluster"); } + + public HazelcastClientOfflineException(Throwable cause) { + super(cause); + } } diff --git a/hazelcast/src/main/java/com/hazelcast/internal/util/ExceptionUtil.java b/hazelcast/src/main/java/com/hazelcast/internal/util/ExceptionUtil.java index 80eb3fb414e8..5d0903b404a4 100644 --- a/hazelcast/src/main/java/com/hazelcast/internal/util/ExceptionUtil.java +++ b/hazelcast/src/main/java/com/hazelcast/internal/util/ExceptionUtil.java @@ -19,10 +19,14 @@ import com.hazelcast.core.HazelcastException; import com.hazelcast.instance.impl.OutOfMemoryErrorDispatcher; import com.hazelcast.logging.ILogger; +import com.hazelcast.spi.impl.operationservice.WrappableException; import javax.annotation.Nonnull; import java.io.PrintWriter; import java.io.StringWriter; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.InvocationTargetException; import java.util.concurrent.ExecutionException; import java.util.function.BiConsumer; @@ -33,6 +37,14 @@ */ public final class ExceptionUtil { + private static final MethodHandles.Lookup LOOKUP = MethodHandles.publicLookup(); + // new Throwable(String message, Throwable cause) + private static final MethodType MT_INIT_STRING_THROWABLE = MethodType.methodType(void.class, String.class, Throwable.class); + // new Throwable(Throwable cause) + private static final MethodType MT_INIT_THROWABLE = MethodType.methodType(void.class, Throwable.class); + // new Throwable(String message) + private static final MethodType MT_INIT_STRING = MethodType.methodType(void.class, String.class); + private static final BiFunction HAZELCAST_EXCEPTION_WRAPPER = (throwable, message) -> { if (message != null) { return new HazelcastException(message, throwable); @@ -99,7 +111,7 @@ public static Throwable peel(final Throwable t, Class a public static Throwable peel(final Throwable t, Class allowedType, String message, BiFunction exceptionWrapper) { if (t instanceof RuntimeException) { - return t; + return wrapException(t, message, exceptionWrapper); } if (t instanceof ExecutionException || t instanceof InvocationTargetException) { @@ -118,7 +130,20 @@ public static Throwable peel(final Throwable t, Class Throwable wrapException(Throwable t, String message, + BiFunction exceptionWrapper) { + if (t instanceof WrappableException) { + return ((WrappableException) t).wrap(); + } + Throwable wrapped = tryWrapInSameClass(t); + return wrapped == null ? exceptionWrapper.apply(t, message) : wrapped; + } + + public static RuntimeException wrapException(RuntimeException t) { + return (RuntimeException) wrapException(t, null, HAZELCAST_EXCEPTION_WRAPPER); + } + + public static RuntimeException rethrow(final Throwable t) { rethrowIfError(t); throw peel(t); } @@ -151,10 +176,15 @@ public static void rethrowIfError(final Throwable t) { if (t instanceof OutOfMemoryError) { OutOfMemoryErrorDispatcher.onOutOfMemory((OutOfMemoryError) t); } - throw (Error) t; + throw wrapError((Error) t); } } + public static Error wrapError(Error cause) { + Error result = tryWrapInSameClass(cause); + return result == null ? cause : result; + } + public static RuntimeException rethrowAllowInterrupted(final Throwable t) throws InterruptedException { return rethrow(t, InterruptedException.class); } @@ -194,4 +224,27 @@ public static RuntimeException sneakyThrow(@Nonnull Throwa } }; } + + public static T tryWrapInSameClass(T cause) { + Class exceptionClass = cause.getClass(); + MethodHandle constructor; + try { + constructor = LOOKUP.findConstructor(exceptionClass, MT_INIT_STRING_THROWABLE); + return (T) constructor.invokeWithArguments(cause.getMessage(), cause); + } catch (Throwable ignored) { + } + try { + constructor = LOOKUP.findConstructor(exceptionClass, MT_INIT_THROWABLE); + return (T) constructor.invokeWithArguments(cause); + } catch (Throwable ignored) { + } + try { + constructor = LOOKUP.findConstructor(exceptionClass, MT_INIT_STRING); + T result = (T) constructor.invokeWithArguments(cause.getMessage()); + result.initCause(cause); + return result; + } catch (Throwable ignored) { + } + return null; + } } diff --git a/hazelcast/src/main/java/com/hazelcast/ringbuffer/StaleSequenceException.java b/hazelcast/src/main/java/com/hazelcast/ringbuffer/StaleSequenceException.java index 2039004fb54f..0c2b130ca5e4 100644 --- a/hazelcast/src/main/java/com/hazelcast/ringbuffer/StaleSequenceException.java +++ b/hazelcast/src/main/java/com/hazelcast/ringbuffer/StaleSequenceException.java @@ -17,13 +17,15 @@ package com.hazelcast.ringbuffer; import com.hazelcast.spi.exception.SilentException; +import com.hazelcast.spi.impl.operationservice.WrappableException; /** * An {@link RuntimeException} that is thrown when accessing an item in the {@link Ringbuffer} using a sequence that is smaller * than the current head sequence and that the ringbuffer store is disabled. This means that the item isn't available in the * ringbuffer and it cannot be loaded from the store either, thus being completely unavailable. */ -public class StaleSequenceException extends RuntimeException implements SilentException { +public class StaleSequenceException extends RuntimeException + implements SilentException, WrappableException { private final long headSeq; @@ -47,4 +49,11 @@ public StaleSequenceException(String message, long headSeq) { public long getHeadSeq() { return headSeq; } + + @Override + public StaleSequenceException wrap() { + StaleSequenceException staleSequenceException = new StaleSequenceException(getMessage(), headSeq); + staleSequenceException.initCause(this); + return staleSequenceException; + } } diff --git a/hazelcast/src/main/java/com/hazelcast/spi/impl/AbstractInvocationFuture.java b/hazelcast/src/main/java/com/hazelcast/spi/impl/AbstractInvocationFuture.java index 35e9e77830d0..690eb886224d 100644 --- a/hazelcast/src/main/java/com/hazelcast/spi/impl/AbstractInvocationFuture.java +++ b/hazelcast/src/main/java/com/hazelcast/spi/impl/AbstractInvocationFuture.java @@ -22,15 +22,10 @@ import com.hazelcast.instance.impl.OutOfMemoryErrorDispatcher; import com.hazelcast.internal.util.executor.UnblockableThread; import com.hazelcast.logging.ILogger; -import com.hazelcast.spi.impl.operationservice.WrappableException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.invoke.MethodType; import java.lang.reflect.InvocationTargetException; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; @@ -51,6 +46,8 @@ import static com.hazelcast.internal.util.ConcurrencyUtil.DEFAULT_ASYNC_EXECUTOR; import static com.hazelcast.internal.util.ExceptionUtil.sneakyThrow; +import static com.hazelcast.internal.util.ExceptionUtil.wrapError; +import static com.hazelcast.internal.util.ExceptionUtil.wrapException; import static java.util.Objects.requireNonNull; import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater; import static java.util.concurrent.locks.LockSupport.park; @@ -59,6 +56,7 @@ /** * Custom implementation of {@link java.util.concurrent.CompletableFuture}. + * * @param */ @SuppressFBWarnings(value = "DLS_DEAD_STORE_OF_CLASS_LITERAL", justification = "Recommended way to prevent classloading bug") @@ -71,13 +69,6 @@ public String toString() { return "UNRESOLVED"; } }; - private static final Lookup LOOKUP = MethodHandles.publicLookup(); - // new Throwable(String message, Throwable cause) - private static final MethodType MT_INIT_STRING_THROWABLE = MethodType.methodType(void.class, String.class, Throwable.class); - // new Throwable(Throwable cause) - private static final MethodType MT_INIT_THROWABLE = MethodType.methodType(void.class, Throwable.class); - // new Throwable(String message) - private static final MethodType MT_INIT_STRING = MethodType.methodType(void.class, String.class); private static final AtomicReferenceFieldUpdater STATE_UPDATER = newUpdater(AbstractInvocationFuture.class, Object.class, "state"); @@ -1912,7 +1903,7 @@ static Throwable wrapOrPeel(Throwable cause) { return cause; } if (cause instanceof RuntimeException) { - return wrapRuntimeException((RuntimeException) cause); + return wrapException((RuntimeException) cause); } if ((cause instanceof ExecutionException || cause instanceof InvocationTargetException) && cause.getCause() != null) { @@ -1927,39 +1918,4 @@ static Throwable wrapOrPeel(Throwable cause) { return new HazelcastException(cause); } - private static RuntimeException wrapRuntimeException(RuntimeException cause) { - if (cause instanceof WrappableException) { - return ((WrappableException) cause).wrap(); - } - RuntimeException wrapped = tryWrapInSameClass(cause); - return wrapped == null ? new HazelcastException(cause) : wrapped; - } - - private static Error wrapError(Error cause) { - Error result = tryWrapInSameClass(cause); - return result == null ? cause : result; - } - - private static T tryWrapInSameClass(T cause) { - Class exceptionClass = cause.getClass(); - MethodHandle constructor; - try { - constructor = LOOKUP.findConstructor(exceptionClass, MT_INIT_STRING_THROWABLE); - return (T) constructor.invokeWithArguments(cause.getMessage(), cause); - } catch (Throwable ignored) { - } - try { - constructor = LOOKUP.findConstructor(exceptionClass, MT_INIT_THROWABLE); - return (T) constructor.invokeWithArguments(cause); - } catch (Throwable ignored) { - } - try { - constructor = LOOKUP.findConstructor(exceptionClass, MT_INIT_STRING); - T result = (T) constructor.invokeWithArguments(cause.getMessage()); - result.initCause(cause); - return result; - } catch (Throwable ignored) { - } - return null; - } } diff --git a/hazelcast/src/test/java/com/hazelcast/client/usercodedeployment/ClientUserCodeDeploymentExceptionTest.java b/hazelcast/src/test/java/com/hazelcast/client/usercodedeployment/ClientUserCodeDeploymentExceptionTest.java index d4b945f10384..55b5b1d4f6c3 100644 --- a/hazelcast/src/test/java/com/hazelcast/client/usercodedeployment/ClientUserCodeDeploymentExceptionTest.java +++ b/hazelcast/src/test/java/com/hazelcast/client/usercodedeployment/ClientUserCodeDeploymentExceptionTest.java @@ -22,13 +22,13 @@ import com.hazelcast.config.Config; import com.hazelcast.core.HazelcastException; import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.internal.util.FilteringClassLoader; import com.hazelcast.map.IMap; import com.hazelcast.nio.serialization.HazelcastSerializationException; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.HazelcastTestSupport; import com.hazelcast.test.annotation.ParallelJVMTest; import com.hazelcast.test.annotation.QuickTest; -import com.hazelcast.internal.util.FilteringClassLoader; import org.junit.After; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -38,8 +38,6 @@ import java.io.FileNotFoundException; import static java.util.Collections.singletonList; -import static junit.framework.TestCase.fail; -import static org.junit.Assert.assertEquals; @RunWith(HazelcastParallelClassRunner.class) @Category({QuickTest.class, ParallelJVMTest.class}) @@ -52,7 +50,7 @@ public void tearDown() throws Exception { factory.terminateAll(); } - @Test + @Test(expected = HazelcastSerializationException.class) public void testUserCodeDeploymentIsDisabledByDefaultOnClient() { // this test also validate the EP is filtered locally and has to be loaded from the other member ClientConfig clientConfig = new ClientConfig(); @@ -64,12 +62,7 @@ public void testUserCodeDeploymentIsDisabledByDefaultOnClient() { HazelcastInstance client = factory.newHazelcastClient(clientConfig); IMap map = client.getMap(randomName()); - try { - map.executeOnEntries(incrementingEntryProcessor); - fail(); - } catch (HazelcastSerializationException e) { - assertEquals(ClassNotFoundException.class, e.getCause().getClass()); - } + map.executeOnEntries(incrementingEntryProcessor); } private Config createNodeConfig() { diff --git a/hazelcast/src/test/java/com/hazelcast/internal/util/ExceptionUtilTest.java b/hazelcast/src/test/java/com/hazelcast/internal/util/ExceptionUtilTest.java index 3af582c9b19e..30a980364405 100644 --- a/hazelcast/src/test/java/com/hazelcast/internal/util/ExceptionUtilTest.java +++ b/hazelcast/src/test/java/com/hazelcast/internal/util/ExceptionUtilTest.java @@ -54,14 +54,16 @@ public void testToString() { public void testPeel_whenThrowableIsRuntimeException_thenReturnOriginal() { RuntimeException result = ExceptionUtil.peel(throwable); - assertEquals(throwable, result); + assertEquals(throwable.getMessage(), result.getMessage()); + assertEquals(throwable.getClass(), result.getClass()); } @Test public void testPeel_whenThrowableIsExecutionException_thenReturnCause() { RuntimeException result = ExceptionUtil.peel(new ExecutionException(throwable)); - assertEquals(throwable, result); + assertEquals(throwable.getMessage(), result.getMessage()); + assertEquals(throwable.getClass(), result.getClass()); } @Test diff --git a/hazelcast/src/test/java/com/hazelcast/map/impl/mapstore/writebehind/TransactionsWithWriteBehind_whenNoCoalescingQueueIsFullTest.java b/hazelcast/src/test/java/com/hazelcast/map/impl/mapstore/writebehind/TransactionsWithWriteBehind_whenNoCoalescingQueueIsFullTest.java index 845d11957ffe..cc1e99b137a3 100644 --- a/hazelcast/src/test/java/com/hazelcast/map/impl/mapstore/writebehind/TransactionsWithWriteBehind_whenNoCoalescingQueueIsFullTest.java +++ b/hazelcast/src/test/java/com/hazelcast/map/impl/mapstore/writebehind/TransactionsWithWriteBehind_whenNoCoalescingQueueIsFullTest.java @@ -55,7 +55,6 @@ import static com.hazelcast.transaction.TransactionOptions.TransactionType.ONE_PHASE; import static com.hazelcast.transaction.TransactionOptions.TransactionType.TWO_PHASE; import static java.util.concurrent.TimeUnit.SECONDS; -import static org.hamcrest.core.Is.isA; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -69,7 +68,6 @@ public class TransactionsWithWriteBehind_whenNoCoalescingQueueIsFullTest extends @Test public void prepare_step_throws_reached_max_size_exception_when_two_phase() { expectedException.expect(TransactionException.class); - expectedException.expectCause(isA(ReachedMaxSizeException.class)); String mapName = "map"; long maxWbqCapacity = 100; diff --git a/hazelcast/src/test/java/com/hazelcast/spi/impl/proxyservice/impl/DistributedObjectFutureTest.java b/hazelcast/src/test/java/com/hazelcast/spi/impl/proxyservice/impl/DistributedObjectFutureTest.java index fead90f3e1c7..8599559ee075 100644 --- a/hazelcast/src/test/java/com/hazelcast/spi/impl/proxyservice/impl/DistributedObjectFutureTest.java +++ b/hazelcast/src/test/java/com/hazelcast/spi/impl/proxyservice/impl/DistributedObjectFutureTest.java @@ -89,7 +89,7 @@ public void get_throwsGivenException_whenUncheckedExceptionSet() throws Exceptio try { future.get(); } catch (Exception e) { - assertSame(error, e); + assertSame(error.getClass(), e.getClass()); } }