diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java index 39d587163b..ed2e7ac7dc 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java @@ -46,15 +46,16 @@ import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.game.ItemManager; import net.runelite.client.menus.MenuManager; import net.runelite.client.plugins.PluginManager; -import net.runelite.client.rs.ClientUpdateCheckMode; import net.runelite.client.rs.ClientLoader; +import net.runelite.client.rs.ClientUpdateCheckMode; import net.runelite.client.task.Scheduler; import net.runelite.client.util.DeferredEventBus; +import net.runelite.client.util.ExecutorServiceExceptionLogger; import net.runelite.client.util.QueryRunner; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import net.runelite.http.api.RuneLiteAPI; import okhttp3.OkHttpClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Slf4j public class RuneLiteModule extends AbstractModule @@ -73,7 +74,7 @@ public class RuneLiteModule extends AbstractModule { bindConstant().annotatedWith(Names.named("updateCheckMode")).to(updateCheckMode); bindConstant().annotatedWith(Names.named("developerMode")).to(developerMode); - bind(ScheduledExecutorService.class).toInstance(Executors.newSingleThreadScheduledExecutor()); + bind(ScheduledExecutorService.class).toInstance(new ExecutorServiceExceptionLogger(Executors.newSingleThreadScheduledExecutor())); bind(OkHttpClient.class).toInstance(RuneLiteAPI.CLIENT); bind(QueryRunner.class); bind(MenuManager.class); diff --git a/runelite-client/src/main/java/net/runelite/client/util/CallableExceptionLogger.java b/runelite-client/src/main/java/net/runelite/client/util/CallableExceptionLogger.java new file mode 100644 index 0000000000..bb1aad4cf3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/util/CallableExceptionLogger.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018, Tomas Slusny + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.util; + +import java.util.concurrent.Callable; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RequiredArgsConstructor +public class CallableExceptionLogger implements Callable +{ + private final Callable callable; + + @Override + public V call() throws Exception + { + try + { + return callable.call(); + } + catch (Throwable ex) + { + log.warn("Uncaught exception in callable {}", callable, ex); + throw ex; + } + } + + public static CallableExceptionLogger wrap(Callable callable) + { + return new CallableExceptionLogger<>(callable); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/util/ExecutorServiceExceptionLogger.java b/runelite-client/src/main/java/net/runelite/client/util/ExecutorServiceExceptionLogger.java new file mode 100644 index 0000000000..af5f13c89f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/util/ExecutorServiceExceptionLogger.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2018, Tomas Slusny + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.util; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * Wrapper for ${@link ScheduledExecutorService} that will log all uncaught exceptions as warning to console + */ +@Slf4j +@RequiredArgsConstructor +public class ExecutorServiceExceptionLogger implements ScheduledExecutorService +{ + private final ScheduledExecutorService service; + + private static Runnable monitor(final Runnable command) + { + return RunnableExceptionLogger.wrap(command); + } + + private static Callable monitor(final Callable command) + { + return CallableExceptionLogger.wrap(command); + } + + @Override + public Future submit(Callable task) + { + return service.submit(monitor(task)); + } + + @Override + public Future submit(Runnable task, T result) + { + return service.submit(monitor(task), result); + } + + @Override + public Future submit(Runnable task) + { + return service.submit(monitor(task)); + } + + @Override + public void execute(Runnable command) + { + service.execute(monitor(command)); + } + + // Everything below is direct proxy to provided executor service + + @Override + public void shutdown() + { + service.shutdown(); + } + + @Override + public List shutdownNow() + { + return service.shutdownNow(); + } + + @Override + public boolean isShutdown() + { + return service.isShutdown(); + } + + @Override + public boolean isTerminated() + { + return service.isTerminated(); + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException + { + return service.awaitTermination(timeout, unit); + } + + @Override + public List> invokeAll(Collection> tasks) throws InterruptedException + { + return service.invokeAll(tasks); + } + + @Override + public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException + { + return service.invokeAll(tasks, timeout, unit); + } + + @Override + public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException + { + return service.invokeAny(tasks); + } + + @Override + public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException + { + return service.invokeAny(tasks, timeout, unit); + } + + @Override + public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) + { + return service.schedule(command, delay, unit); + } + + @Override + public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) + { + return service.schedule(callable, delay, unit); + } + + @Override + public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) + { + return service.scheduleAtFixedRate(command, initialDelay, period, unit); + } + + @Override + public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) + { + return service.scheduleWithFixedDelay(command, initialDelay, delay, unit); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/util/RunnableExceptionLogger.java b/runelite-client/src/main/java/net/runelite/client/util/RunnableExceptionLogger.java index cce51a4ca3..58c7247cdd 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/RunnableExceptionLogger.java +++ b/runelite-client/src/main/java/net/runelite/client/util/RunnableExceptionLogger.java @@ -24,18 +24,15 @@ */ package net.runelite.client.util; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j +@RequiredArgsConstructor public class RunnableExceptionLogger implements Runnable { private final Runnable runnable; - private RunnableExceptionLogger(Runnable runnable) - { - this.runnable = runnable; - } - @Override public void run() { @@ -46,6 +43,7 @@ public class RunnableExceptionLogger implements Runnable catch (Throwable ex) { log.warn("Uncaught exception in runnable {}", runnable, ex); + throw ex; } }