001 /*
002 * Copyright 2009-2012 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 griffon.core;
017
018 import griffon.util.*;
019 import groovy.lang.ExpandoMetaClass;
020 import groovy.lang.MetaClass;
021 import groovy.lang.MissingMethodException;
022 import groovy.lang.Script;
023 import org.slf4j.Logger;
024 import org.slf4j.LoggerFactory;
025
026 import java.util.concurrent.Callable;
027 import java.util.concurrent.ExecutorService;
028 import java.util.concurrent.Executors;
029 import java.util.concurrent.Future;
030
031 /**
032 * Helper class that can execute code inside the UI thread.
033 *
034 * @author Andres Almiray
035 */
036 public final class UIThreadManager {
037 // Shouldn't need to synchronize access to this field as setting its value
038 // should be done at boot time
039 private UIThreadHandler uiThreadHandler;
040 private static final ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
041 private static final Logger LOG = LoggerFactory.getLogger(UIThreadManager.class);
042
043 private static final UIThreadManager INSTANCE = new UIThreadManager();
044
045 public static UIThreadManager getInstance() {
046 return INSTANCE;
047 }
048
049 private UIThreadManager() {
050 if (LOG.isDebugEnabled()) {
051 LOG.debug("Default Executor set to run with " + Runtime.getRuntime().availableProcessors() + " processors");
052 }
053 }
054
055 private static abstract class ScriptOrRunnableRunner extends RunnableWithArgs {
056 public void run(Object[] args) {
057 if (args != null && args.length == 1) {
058 if (args[0] instanceof Script) {
059 withScript((Script) args[0]);
060 return;
061 } else if (args[0] instanceof Runnable) {
062 withRunnable((Runnable) args[0]);
063 return;
064 }
065 }
066 throw new MissingMethodException(getMethodName(), UIThreadManager.class, args);
067 }
068
069 protected abstract String getMethodName();
070
071 protected abstract void withScript(Script script);
072
073 protected abstract void withRunnable(Runnable runnable);
074 }
075
076 private static final String EXECUTE_INSIDE_UI_SYNC = "execInsideUISync";
077 private static final RunnableWithArgsClosure EXECUTE_INSIDE_UI_SYNC_RUNNER = new RunnableWithArgsClosure(INSTANCE,
078 new ScriptOrRunnableRunner() {
079 protected String getMethodName() {
080 return EXECUTE_INSIDE_UI_SYNC;
081 }
082
083 protected void withScript(Script script) {
084 INSTANCE.executeSync(script);
085 }
086
087 protected void withRunnable(Runnable runnable) {
088 INSTANCE.executeSync(runnable);
089 }
090 });
091
092 private static final String EXECUTE_INSIDE_UI_ASYNC = "execInsideUIAsync";
093 private static final RunnableWithArgsClosure EXECUTE_INSIDE_UI_ASYNC_RUNNER = new RunnableWithArgsClosure(INSTANCE,
094 new ScriptOrRunnableRunner() {
095 protected String getMethodName() {
096 return EXECUTE_INSIDE_UI_ASYNC;
097 }
098
099 protected void withScript(Script script) {
100 INSTANCE.executeAsync(script);
101 }
102
103 protected void withRunnable(Runnable runnable) {
104 INSTANCE.executeAsync(runnable);
105 }
106 });
107
108 private static final String EXECUTE_OUTSIDE_UI = "execOutsideUI";
109 private static final RunnableWithArgsClosure EXECUTE_OUTSIDE_UI_RUNNER = new RunnableWithArgsClosure(INSTANCE,
110 new ScriptOrRunnableRunner() {
111 protected String getMethodName() {
112 return EXECUTE_OUTSIDE_UI;
113 }
114
115 protected void withScript(Script script) {
116 INSTANCE.executeOutside(script);
117 }
118
119 protected void withRunnable(Runnable runnable) {
120 INSTANCE.executeOutside(runnable);
121 }
122 });
123
124 private static final String IS_UITHREAD = "isUIThread";
125 private static final CallableWithArgsClosure IS_UITHREAD_RUNNER = new CallableWithArgsClosure(INSTANCE,
126 new CallableWithArgs<Boolean>() {
127 public Boolean call(Object[] args) {
128 if (args.length == 0) {
129 return INSTANCE.isUIThread();
130 }
131 throw new MissingMethodException(IS_UITHREAD, UIThreadManager.class, args);
132 }
133 });
134
135 private static final String EXECUTE_FUTURE = "executeFuture";
136 private static final CallableWithArgsClosure EXECUTE_FUTURE_RUNNER = new CallableWithArgsClosure(INSTANCE,
137 new CallableWithArgs<Future>() {
138 public Future call(Object[] args) {
139 if (args.length == 1 && args[0] instanceof Callable) {
140 return INSTANCE.executeFuture((Callable) args[0]);
141 } else if (args.length == 2 && args[0] instanceof ExecutorService && args[1] instanceof Callable) {
142 return INSTANCE.executeFuture((ExecutorService) args[0], (Callable) args[1]);
143 }
144 throw new MissingMethodException(EXECUTE_FUTURE, UIThreadManager.class, args);
145 }
146 });
147
148 public static void enhance(Script script) {
149 if (script instanceof ThreadingHandler) return;
150 if (LOG.isTraceEnabled()) {
151 LOG.trace("Enhancing script " + script);
152 }
153 // TODO @deprecated - remove before 1.0
154 script.getBinding().setVariable("execSync", EXECUTE_INSIDE_UI_SYNC_RUNNER);
155 script.getBinding().setVariable("execAsync", EXECUTE_INSIDE_UI_ASYNC_RUNNER);
156 script.getBinding().setVariable("execOutside", EXECUTE_OUTSIDE_UI_RUNNER);
157
158 script.getBinding().setVariable(EXECUTE_INSIDE_UI_SYNC, EXECUTE_INSIDE_UI_SYNC_RUNNER);
159 script.getBinding().setVariable(EXECUTE_INSIDE_UI_SYNC, EXECUTE_INSIDE_UI_ASYNC_RUNNER);
160 script.getBinding().setVariable(EXECUTE_OUTSIDE_UI, EXECUTE_OUTSIDE_UI_RUNNER);
161 script.getBinding().setVariable(IS_UITHREAD, IS_UITHREAD_RUNNER);
162 script.getBinding().setVariable(EXECUTE_FUTURE, EXECUTE_FUTURE_RUNNER);
163 }
164
165 public static void enhance(MetaClass metaClass) {
166 if (metaClass instanceof ExpandoMetaClass) {
167 ExpandoMetaClass mc = (ExpandoMetaClass) metaClass;
168 if (LOG.isTraceEnabled()) {
169 LOG.trace("Enhancing metaClass " + metaClass);
170 }
171 // TODO @deprecated - remove before 1.0
172 mc.registerInstanceMethod("execSync", EXECUTE_INSIDE_UI_SYNC_RUNNER);
173 mc.registerInstanceMethod("execAsync", EXECUTE_INSIDE_UI_ASYNC_RUNNER);
174 mc.registerInstanceMethod("execOutside", EXECUTE_OUTSIDE_UI_RUNNER);
175
176 mc.registerInstanceMethod(EXECUTE_INSIDE_UI_SYNC, EXECUTE_INSIDE_UI_SYNC_RUNNER);
177 mc.registerInstanceMethod(EXECUTE_INSIDE_UI_ASYNC, EXECUTE_INSIDE_UI_ASYNC_RUNNER);
178 mc.registerInstanceMethod(EXECUTE_OUTSIDE_UI, EXECUTE_OUTSIDE_UI_RUNNER);
179 mc.registerInstanceMethod(IS_UITHREAD, IS_UITHREAD_RUNNER);
180 mc.registerInstanceMethod(EXECUTE_FUTURE, EXECUTE_FUTURE_RUNNER);
181 }
182 }
183
184 public void setUIThreadHandler(UIThreadHandler threadHandler) {
185 if (this.uiThreadHandler != null) {
186 if (LOG.isWarnEnabled()) {
187 LOG.warn("UIThreadHandler is already set, it can't be changed!");
188 }
189 } else {
190 this.uiThreadHandler = threadHandler;
191 }
192 }
193
194 public UIThreadHandler getUIThreadHandler() {
195 if (this.uiThreadHandler == null) {
196 try {
197 // attempt loading of default UIThreadHandler -> Swing
198 setUIThreadHandler((UIThreadHandler) getClass().getClassLoader().loadClass("griffon.swing.SwingUIThreadHandler").newInstance());
199 } catch (ClassNotFoundException e) {
200 throw new IllegalStateException("Can't locate a suitable UIThreadHandler.", e);
201 } catch (InstantiationException e) {
202 throw new IllegalStateException("Can't locate a suitable UIThreadHandler.", e);
203 } catch (IllegalAccessException e) {
204 throw new IllegalStateException("Can't locate a suitable UIThreadHandler.", e);
205 }
206 }
207 return this.uiThreadHandler;
208 }
209
210 /**
211 * True if the current thread is the UI thread.
212 *
213 * @return true if the current thread is the UI thread, false otherwise.
214 */
215 public boolean isUIThread() {
216 return getUIThreadHandler().isUIThread();
217 }
218
219 /**
220 * Executes a code block asynchronously on the UI thread.
221 *
222 * @param runnable a code block to be executed
223 */
224 public void executeAsync(Runnable runnable) {
225 getUIThreadHandler().executeAsync(runnable);
226 }
227
228 /**
229 * Executes a code block asynchronously on the UI thread.
230 *
231 * @param script a code block to be executed
232 */
233 public void executeAsync(final Script script) {
234 getUIThreadHandler().executeAsync(new Runnable() {
235 public void run() {
236 script.run();
237 }
238 });
239 }
240
241 /**
242 * Executes a code block synchronously on the UI thread.
243 *
244 * @param runnable a code block to be executed
245 */
246 public void executeSync(Runnable runnable) {
247 getUIThreadHandler().executeSync(runnable);
248 }
249
250 /**
251 * Executes a code block synchronously on the UI thread.
252 *
253 * @param script a code block to be executed
254 */
255 public void executeSync(final Script script) {
256 getUIThreadHandler().executeSync(new Runnable() {
257 public void run() {
258 script.run();
259 }
260 });
261 }
262
263 /**
264 * Executes a code block outside of the UI thread.
265 *
266 * @param runnable a code block to be executed
267 */
268 public void executeOutside(Runnable runnable) {
269 getUIThreadHandler().executeOutside(runnable);
270 }
271
272 /**
273 * Executes a code block outside of the UI thread.
274 *
275 * @param script a code block to be executed
276 */
277 public void executeOutside(final Script script) {
278 getUIThreadHandler().executeOutside(new Runnable() {
279 public void run() {
280 script.run();
281 }
282 });
283 }
284
285 /**
286 * Executes a code block as a Future on an ExecutorService.
287 *
288 * @param callable a code block to be executed
289 * @return a Future that contains the result of the execution
290 */
291 public Future executeFuture(Callable<?> callable) {
292 return executeFuture(DEFAULT_EXECUTOR_SERVICE, callable);
293 }
294
295 /**
296 * Executes a code block as a Future on an ExecutorService.
297 *
298 * @param executorService the ExecutorService to use. Will use the default ExecutorService if null.
299 * @param callable a code block to be executed
300 * @return a Future that contains the result of the execution
301 */
302 public Future executeFuture(ExecutorService executorService, Callable<?> callable) {
303 executorService = executorService != null ? executorService : DEFAULT_EXECUTOR_SERVICE;
304 return executorService.submit(callable);
305 }
306 }
|