`UIThreadManager.java`
`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 }`