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