AbstractUIThreadManager.java
001 /*
002  * Copyright 2008-2017 the original author or authors.
003  *
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  *
008  *     http://www.apache.org/licenses/LICENSE-2.0
009  *
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 package org.codehaus.griffon.runtime.core.threading;
017 
018 import griffon.core.ExceptionHandler;
019 import griffon.core.ExecutorServiceManager;
020 import griffon.core.threading.UIThreadManager;
021 import griffon.exceptions.GriffonException;
022 
023 import javax.annotation.Nonnull;
024 import javax.annotation.Nullable;
025 import javax.inject.Inject;
026 import javax.inject.Named;
027 import java.util.concurrent.Callable;
028 import java.util.concurrent.ExecutionException;
029 import java.util.concurrent.ExecutorService;
030 import java.util.concurrent.Future;
031 import java.util.concurrent.FutureTask;
032 
033 import static java.util.Objects.requireNonNull;
034 
035 /**
036  @author Andres Almiray
037  @since 2.0.0
038  */
039 public abstract class AbstractUIThreadManager implements UIThreadManager {
040     protected static final String ERROR_RUNNABLE_NULL = "Argument 'runnable' must not be null";
041     protected static final String ERROR_CALLABLE_NULL = "Argument 'callable' must not be null";
042 
043     private ExecutorServiceManager executorServiceManager;
044 
045     @Inject @Named("defaultExecutorService")
046     private ExecutorService executorService;
047 
048     @Inject
049     private ExceptionHandler exceptionHandler;
050 
051     @Inject
052     public void setExecutorServiceManager(@Nonnull ExecutorServiceManager executorServiceManager) {
053         requireNonNull(executorServiceManager, "Argument 'executorServiceManager' must not be null");
054         if (this.executorServiceManager != null) {
055             this.executorServiceManager.remove(executorService);
056         }
057         this.executorServiceManager = executorServiceManager;
058         this.executorServiceManager.add(executorService);
059     }
060 
061     /**
062      * Executes a code block as a Future on an ExecutorService.
063      *
064      @param callable a code block to be executed
065      *
066      @return a Future that contains the result of the execution
067      */
068     @Nonnull
069     @Override
070     public <R> Future<R> runFuture(@Nonnull Callable<R> callable) {
071         requireNonNull(callable, ERROR_CALLABLE_NULL);
072         return runFuture(executorService, callable);
073     }
074 
075     /**
076      * Executes a code block as a Future on an ExecutorService.
077      *
078      @param executorService the ExecutorService to use. Will use the default ExecutorService if null.
079      @param callable        a code block to be executed
080      *
081      @return a Future that contains the result of the execution
082      */
083     @Nonnull
084     @Override
085     public <R> Future<R> runFuture(@Nonnull ExecutorService executorService, @Nonnull Callable<R> callable) {
086         requireNonNull(executorService, "Argument 'executorService' must not be null");
087         requireNonNull(callable, ERROR_CALLABLE_NULL);
088         return executorService.submit(callable);
089     }
090 
091     @Override
092     public void runOutsideUI(@Nonnull final Runnable runnable) {
093         requireNonNull(runnable, ERROR_RUNNABLE_NULL);
094         if (!isUIThread()) {
095             runnable.run();
096         else {
097             executorService.submit(new Runnable() {
098                 public void run() {
099                     try {
100                         runnable.run();
101                     catch (Throwable throwable) {
102                         exceptionHandler.uncaughtException(Thread.currentThread(), throwable);
103                     }
104                 }
105             });
106         }
107     }
108 
109     @Override
110     public void runOutsideUIAsync(@Nonnull final Runnable runnable) {
111         requireNonNull(runnable, ERROR_RUNNABLE_NULL);
112 
113         executorService.submit(new Runnable() {
114             public void run() {
115                 try {
116                     runnable.run();
117                 catch (Throwable throwable) {
118                     exceptionHandler.uncaughtException(Thread.currentThread(), throwable);
119                 }
120             }
121         });
122     }
123 
124     @Nullable
125     @Override
126     public <R> R runInsideUISync(@Nonnull Callable<R> callable) {
127         requireNonNull(callable, ERROR_CALLABLE_NULL);
128         FutureTask<R> ft = new FutureTask<>(callable);
129         runInsideUISync(ft);
130         try {
131             return ft.get();
132         catch (InterruptedException | ExecutionException e) {
133             throw new GriffonException("An error occurred while executing a task inside the UI thread", e);
134         }
135     }
136 
137     @Nonnull
138     protected ExecutorServiceManager getExecutorServiceManager() {
139         return executorServiceManager;
140     }
141 
142     @Nonnull
143     protected ExecutorService getExecutorService() {
144         return executorService;
145     }
146 
147     @Nonnull
148     protected ExceptionHandler getExceptionHandler() {
149         return exceptionHandler;
150     }
151 }