package eu.fbk.knowledgestore.internal; import java.io.Closeable; import java.io.File; import java.io.FilterInputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Type; import java.net.URL; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import javax.annotation.Nullable; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.google.common.io.Resources; import com.google.common.reflect.TypeToken; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableScheduledFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.ListeningScheduledExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; public final class Util { private static final Logger LOGGER = LoggerFactory.getLogger(Util.class); public static URL getURL(final String location) { URL url = null; try { url = Resources.getResource(location.startsWith("/") ? location.substring(1) : location); if (url != null) { return url; } } catch (final Exception ex) { // not a classpath resource - ignore } try { final File file = new File(location); if (file.exists() && file.isFile()) { return file.toURI().toURL(); } } catch (final Exception ex) { // not a file - ignore } try { return new URL(location); } catch (final Exception ex) { // not a valid URL throw new IllegalArgumentException("Cannot extract a URL from: " + location); } } public static String getResource(final Class<?> referenceClass, final String resourceName) { try { final URL url = referenceClass.getResource(resourceName); return Resources.toString(url, Charsets.UTF_8); } catch (final IOException ex) { throw new Error("Missing resource '" + resourceName + "': " + ex.getMessage(), ex); } } public static String getVersion(final String groupId, final String artifactId, final String defaultValue) { final URL url = Util.class.getClassLoader().getResource( "META-INF/maven/" + groupId + "/" + artifactId + "/pom.properties"); String version = defaultValue; if (url != null) { try { final InputStream stream = url.openStream(); try { final Properties properties = new Properties(); properties.load(stream); version = properties.getProperty("version").trim(); } finally { stream.close(); } } catch (final IOException ex) { version = "unknown"; } } return version; } @SuppressWarnings({ "unchecked", "rawtypes" }) public static String formatType(final Type type) { final TypeToken<?> token = TypeToken.of(type); final Class<?> clazz = token.getRawType(); String name = clazz.getSimpleName(); if (name.isEmpty()) { Class<?> parent = clazz.getSuperclass(); if (parent == null && clazz.getInterfaces().length > 0) { parent = clazz.getInterfaces()[0]; } if (parent != null) { name = token.getSupertype((Class) parent).toString(); } } return name; } @Nullable public static <T> T closeQuietly(@Nullable final T object) { if (object instanceof Closeable) { try { ((Closeable) object).close(); } catch (final Throwable ex) { LOGGER.error("Error closing " + object.getClass().getSimpleName(), ex); } } return object; } @Nullable public static InputStream interceptClose(@Nullable final InputStream stream, @Nullable final Runnable runnable) { if (stream == null || runnable == null) { return stream; } final Map<String, String> mdc = Logging.getMDC(); return new FilterInputStream(stream) { private boolean closed; @Override public void close() throws IOException { if (this.closed) { return; } final Map<String, String> oldMdc = Logging.getMDC(); try { Logging.setMDC(mdc); super.close(); runnable.run(); } finally { this.closed = true; Logging.setMDC(oldMdc); } } }; } @Nullable public static OutputStream interceptClose(@Nullable final OutputStream stream, @Nullable final Runnable runnable) { if (stream == null || runnable == null) { return stream; } final Map<String, String> mdc = Logging.getMDC(); return new FilterOutputStream(stream) { private boolean closed; @Override public void close() throws IOException { if (this.closed) { return; } final Map<String, String> oldMdc = Logging.getMDC(); try { Logging.setMDC(mdc); super.close(); runnable.run(); } finally { this.closed = true; Logging.setMDC(oldMdc); } } }; } public static ListeningScheduledExecutorService newScheduler(final int numThreads, final String nameFormat, final boolean daemon) { final ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(daemon) .setNameFormat(nameFormat) .setUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(final Thread thread, final Throwable ex) { LOGGER.error("Uncaught exception in thread " + thread.getName(), ex); } }).build(); return decorate(Executors.newScheduledThreadPool(numThreads, factory)); } public static ListeningExecutorService decorate(final ExecutorService executor) { Preconditions.checkNotNull(executor); if (executor instanceof MDCExecutorService) { return (MDCExecutorService) executor; } else if (executor instanceof ListeningExecutorService) { return new MDCExecutorService((ListeningExecutorService) executor); } else { return new MDCExecutorService(MoreExecutors.listeningDecorator(executor)); } } public static ListeningScheduledExecutorService decorate( final ScheduledExecutorService executor) { if (executor instanceof MDCScheduledExecutorService) { return (MDCScheduledExecutorService) executor; } else if (executor instanceof ListeningScheduledExecutorService) { return new MDCScheduledExecutorService((ListeningScheduledExecutorService) executor); } else { // return MoreExecutors.listeningDecorator(executor); return new MDCScheduledExecutorService(MoreExecutors.listeningDecorator(executor)); } } private static class MDCScheduledExecutorService extends MDCExecutorService implements ListeningScheduledExecutorService { MDCScheduledExecutorService(final ListeningScheduledExecutorService delegate) { super(Preconditions.checkNotNull(delegate)); } @Override ListeningScheduledExecutorService delegate() { return (ListeningScheduledExecutorService) super.delegate(); } @Override public ListenableScheduledFuture<?> schedule(final Runnable command, final long delay, final TimeUnit unit) { return delegate().schedule(wrap(command, MDC.getCopyOfContextMap()), delay, unit); } @Override public <V> ListenableScheduledFuture<V> schedule(final Callable<V> callable, final long delay, final TimeUnit unit) { return delegate().schedule(wrap(callable, MDC.getCopyOfContextMap()), delay, unit); } @Override public ListenableScheduledFuture<?> scheduleAtFixedRate(final Runnable command, final long initialDelay, final long period, final TimeUnit unit) { return delegate().scheduleAtFixedRate(wrap(command, MDC.getCopyOfContextMap()), initialDelay, period, unit); } @Override public ListenableScheduledFuture<?> scheduleWithFixedDelay(final Runnable command, final long initialDelay, final long delay, final TimeUnit unit) { return delegate().scheduleWithFixedDelay(wrap(command, MDC.getCopyOfContextMap()), initialDelay, delay, unit); } } private static class MDCExecutorService implements ListeningExecutorService { private final ListeningExecutorService delegate; MDCExecutorService(final ListeningExecutorService delegate) { this.delegate = Preconditions.checkNotNull(delegate); } ListeningExecutorService delegate() { return this.delegate; } Runnable wrap(final Runnable runnable, final Map<String, String> mdcMap) { return new Runnable() { @Override public void run() { Map<String, String> oldMap = null; try { if (mdcMap != null) { oldMap = MDC.getCopyOfContextMap(); MDC.setContextMap(mdcMap); } runnable.run(); } catch (final Throwable ex) { LOGGER.error("Uncaught exception in thread " + Thread.currentThread().getName() + ": " + ex.getMessage(), ex); throw Throwables.propagate(ex); } finally { if (oldMap != null) { MDC.setContextMap(oldMap); } } } }; } <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> mdcMap) { return new Callable<T>() { @Override public T call() throws Exception { Map<String, String> oldMap = null; try { if (mdcMap != null) { oldMap = MDC.getCopyOfContextMap(); MDC.setContextMap(mdcMap); } return callable.call(); } catch (final Throwable ex) { LOGGER.error("Uncaught exception in thread " + Thread.currentThread().getName() + ": " + ex.getMessage(), ex); Throwables.propagateIfPossible(ex, Exception.class); throw new RuntimeException(ex); } finally { if (oldMap != null) { MDC.setContextMap(oldMap); } } } }; } <T> Collection<Callable<T>> wrap(final Collection<? extends Callable<T>> callables, final Map<String, String> mdcMap) { final List<Callable<T>> result = Lists.newArrayListWithCapacity(callables.size()); for (final Callable<T> callable : callables) { result.add(wrap(callable, mdcMap)); } return result; } @Override public void shutdown() { delegate().shutdown(); } @Override public List<Runnable> shutdownNow() { return delegate().shutdownNow(); } @Override public boolean isShutdown() { return delegate().isShutdown(); } @Override public boolean isTerminated() { return delegate().isTerminated(); } @Override public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException { return delegate().awaitTermination(timeout, unit); } @Override public <T> T invokeAny(final Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { return delegate().invokeAny(wrap(tasks, MDC.getCopyOfContextMap())); } @Override public <T> T invokeAny(final Collection<? extends Callable<T>> tasks, final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return delegate().invokeAny(wrap(tasks, MDC.getCopyOfContextMap()), timeout, unit); } @Override public void execute(final Runnable command) { delegate().execute(wrap(command, MDC.getCopyOfContextMap())); } @Override public <T> ListenableFuture<T> submit(final Callable<T> task) { return delegate().submit(wrap(task, MDC.getCopyOfContextMap())); } @Override public ListenableFuture<?> submit(final Runnable task) { return delegate().submit(wrap(task, MDC.getCopyOfContextMap())); } @Override public <T> ListenableFuture<T> submit(final Runnable task, final T result) { return delegate().submit(wrap(task, MDC.getCopyOfContextMap()), result); } @Override public <T> List<Future<T>> invokeAll(final Collection<? extends Callable<T>> tasks) throws InterruptedException { return delegate().invokeAll(wrap(tasks, MDC.getCopyOfContextMap())); } @Override public <T> List<Future<T>> invokeAll(final Collection<? extends Callable<T>> tasks, final long timeout, final TimeUnit unit) throws InterruptedException { return delegate().invokeAll(wrap(tasks, MDC.getCopyOfContextMap()), timeout, unit); } } }