Commit 6c2b0905 authored by Rajiv Prabhakar's avatar Rajiv Prabhakar
Browse files

v2.0.9 release:

- CheckedExceptionWrapper now passes the cause and message via constructor, instead of overriding the get methods. Latter approach does not always seem to work
- CheckedExceptionWrapper stack-trace now set using setStackTrace(), instead of trying to override the get methods. Latter approach does not seem to work in log4j2
- CheckedExceptionWrapper static builders added, and constructor marked as protected
- CheckedExceptionWrapper.wrapIfNeeded added, and used in various places
- ThreadUtilc.fork now uses a cached-thread-pool, to avoid creating a new thread every time where possible
- New ExecutorServiceDelegator interface added: enables easy composition using another ExecutorService, instead of inheriting from it
parent e656c6ca
Pipeline #30255826 passed with stage
in 1 minute and 38 seconds
......@@ -6,7 +6,7 @@
<groupId>com.rajivprab</groupId>
<artifactId>cava</artifactId>
<version>2.0.8</version>
<version>2.0.9</version>
<name>Cava: Clean Java</name>
<description>A library that enables users to write minimal, clean and simple Java</description>
......
......@@ -61,7 +61,7 @@ public class FileUtilc {
try {
return new File(ClassLoader.getSystemResource(path).toURI());
} catch (URISyntaxException e) {
throw new CheckedExceptionWrapper(e);
throw CheckedExceptionWrapper.wrapIfNeeded(e);
}
}
......
......@@ -23,11 +23,7 @@ public class Mapc {
try {
return cache.get(key, callable);
} catch (ExecutionException | UncheckedExecutionException e) {
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException) e.getCause();
} else {
throw new CheckedExceptionWrapper(e.getCause());
}
throw CheckedExceptionWrapper.wrapIfNeeded(e.getCause());
}
}
......
......@@ -14,6 +14,10 @@ import java.util.concurrent.*;
* Created by rprabhakar on 12/15/15.
*/
public class ThreadUtilc {
// Use a cached thread-pool to minimize overhead from creating a new thread every time
// Executor will auto-spawn new threads if a cached thread is not available
private static final ExecutorService CACHED_THREAD_POOL = Executors.newCachedThreadPool();
// ---------- Sleep ------------
public static void sleep(Duration duration) {
......@@ -30,15 +34,12 @@ public class ThreadUtilc {
// ---------- Fork ------------
public static FutureTask<Boolean> fork(Runnable runnable) {
return fork(() -> { runnable.run(); return true; });
public static Future<?> fork(Runnable runnable) {
return CACHED_THREAD_POOL.submit(runnable);
}
public static <T> FutureTask<T> fork(Callable<T> callable) {
FutureTask<T> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();
return futureTask;
public static <T> Future<T> fork(Callable<T> callable) {
return CACHED_THREAD_POOL.submit(callable);
}
// ---------- Get ------------
......@@ -46,10 +47,8 @@ public class ThreadUtilc {
public static <T> T call(Callable<T> callable) {
try {
return callable.call();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new CheckedExceptionWrapper(e);
throw CheckedExceptionWrapper.wrapIfNeeded(e);
}
}
......
......@@ -675,7 +675,7 @@ public class Validatec {
try {
return exceptionType.getConstructor(String.class).newInstance(Strings.nullToEmpty(message));
} catch (ReflectiveOperationException e) {
throw new CheckedExceptionWrapper(e);
throw CheckedExceptionWrapper.wrapIfNeeded(e);
}
}
}
package org.rajivprab.cava.delegator;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.*;
/**
* Enables defining a new ExecutorService which is a thin wrapper around another ExecutorService,
* except for a few overridden methods.
*
* Ie, composition over inheritance.
*
* Example usage: see Sava's CapacityLoggingThreadPoolExecutor
* TODO Insert Sava web-url above
*/
public interface ExecutorServiceDelegator extends ExecutorService {
ExecutorService getDelegate();
@Override
default void shutdown() {
getDelegate().shutdown();
}
@Override
default List<Runnable> shutdownNow() {
return getDelegate().shutdownNow();
}
@Override
default boolean isShutdown() {
return getDelegate().isShutdown();
}
@Override
default boolean isTerminated() {
return getDelegate().isTerminated();
}
@Override
default boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
return getDelegate().awaitTermination(timeout, unit);
}
@Override
default <T> Future<T> submit(Callable<T> task) {
return getDelegate().submit(task);
}
@Override
default <T> Future<T> submit(Runnable task, T result) {
return getDelegate().submit(task, result);
}
@Override
default Future<?> submit(Runnable task) {
return getDelegate().submit(task);
}
@Override
default <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
return getDelegate().invokeAll(tasks);
}
@Override
default <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException {
return getDelegate().invokeAll(tasks, timeout, unit);
}
@Override
default <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
return getDelegate().invokeAny(tasks);
}
@Override
default <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return getDelegate().invokeAny(tasks, timeout, unit);
}
@Override
default void execute(Runnable command) {
getDelegate().execute(command);
}
}
\ No newline at end of file
package org.rajivprab.cava.exception;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
* Wrapper for checked exceptions, that looks just like the underlying exception.
* Wrapper for checked exceptions, that looks as similar as reasonably possible to the underlying exception.
*
* Any stack-traces will show the stack-trace for where the underlying exception was thrown from,
* and not where the CheckedExceptionWrapper was thrown from.
* <p>
* and not where the CheckedExceptionWrapper was wrapped and thrown from.
* Thesis: Including the stack-trace for wrapping-and-rethrowing just adds noise, with minimal debug help.
*
* The cause will match the underlying exception's cause.
*
* toString() will prepend the current class-name, to the underlying's toString.
* This enables better debugging of the specific exception type that was thrown, and/or needs to be caught.
*
* Created by rprabhakar on 12/15/15.
*/
public class CheckedExceptionWrapper extends RuntimeException {
private final Throwable underlying;
public CheckedExceptionWrapper(Throwable underlying) {
this.underlying = underlying;
}
public Throwable getUnderlying() {
return underlying;
// If argument is a RunTimeException, returns it as is. Else, wraps it in a CheckedExceptionWrapper
public static RuntimeException wrapIfNeeded(Throwable throwable) {
return throwable instanceof RuntimeException ? (RuntimeException) throwable : wrap(throwable);
}
// Returns the underlying exception's cause, not the underlying exception itself
// To get the underlying exception, use getUnderlying()
@Override
public Throwable getCause() {
return underlying.getCause();
// Always wraps the argument in a CheckedExceptionWrapper. Useful for consistency in catching it
public static CheckedExceptionWrapper wrap(Throwable throwable) {
return new CheckedExceptionWrapper(throwable);
}
@Override
public String getLocalizedMessage() {
return underlying.getLocalizedMessage();
}
@Override
public String getMessage() {
return underlying.getMessage();
}
@Override
public StackTraceElement[] getStackTrace() {
return underlying.getStackTrace();
}
@Override
public void printStackTrace() {
underlying.printStackTrace();
}
@Override
public void printStackTrace(PrintStream s) {
underlying.printStackTrace(s);
protected CheckedExceptionWrapper(Throwable underlying) {
super(underlying.getMessage(), underlying.getCause(), true, true);
setStackTrace(underlying.getStackTrace());
this.underlying = underlying;
}
@Override
public void printStackTrace(PrintWriter s) {
underlying.printStackTrace(s);
// getCause() returns the underlying exception's cause,
// not the underlying exception itself. To get the underlying exception, use this method
public Throwable getUnderlying() {
return underlying;
}
@Override
......
......@@ -178,11 +178,11 @@ public class ThreadUtilcTest extends TestBase {
@Test
public void forkRunnable() {
checkRunTime(() -> {
FutureTask<Boolean> task = ThreadUtilc.fork(() -> ThreadUtilc.sleep(TEST_DURATION));
assertThat(task.isDone()).isFalse();
assertThat(ThreadUtilc.get(task)).isTrue();
}, TEST_DURATION, TEST_DURATION.multipliedBy(3).dividedBy(2));
Future<?> task = ThreadUtilc.fork(() -> ThreadUtilc.sleep(TEST_DURATION));
assertThat(task.isDone()).isFalse();
checkRunTime(() -> assertThat(ThreadUtilc.get(task)).isNull(),
TEST_DURATION.multipliedBy(95).dividedBy(100),
TEST_DURATION.multipliedBy(120).dividedBy(100));
}
@Test
......
package org.rajivprab.cava.exception;
import org.junit.Test;
import org.rajivprab.cava.TestBase;
// TODO Write tests
public class CheckedExceptionWrapperTest extends TestBase {
@Test
public void wrapIfNeeded_shouldWrapCheckedExceptions() {
}
@Test
public void wrapIfNeeded_shouldNotWrapRunTimeExceptions() {
}
@Test
public void wrap_shouldWrapCheckedExceptions() {
}
@Test
public void wrap_shouldWrapRunTimeExceptions() {
}
@Test
public void shouldHaveSameStackTraceAsUnderlying() {
}
@Test
public void shouldHaveSameMessageAsUnderlying() {
}
@Test
public void shouldHaveSameDetailedMessageAsUnderlying() {
}
@Test
public void shouldHaveSameCauseAsUnderlying() {
}
@Test
public void toString_shouldPrependClassName() {
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment