001 /*
002 * Copyright 2008-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
017 package org.codehaus.griffon.runtime.core;
018
019 import griffon.core.*;
020 import griffon.util.ApplicationHolder;
021 import griffon.util.ConfigUtils;
022 import griffon.util.Metadata;
023 import griffon.util.RunnableWithArgs;
024 import groovy.lang.Binding;
025 import groovy.lang.Closure;
026 import groovy.util.ConfigObject;
027 import groovy.util.FactoryBuilderSupport;
028 import org.codehaus.griffon.runtime.util.GriffonApplicationHelper;
029 import org.codehaus.griffon.runtime.util.MVCGroupExceptionHandler;
030 import org.slf4j.Logger;
031 import org.slf4j.LoggerFactory;
032
033 import java.io.InputStream;
034 import java.net.URL;
035 import java.util.*;
036 import java.util.concurrent.Callable;
037 import java.util.concurrent.CountDownLatch;
038 import java.util.concurrent.ExecutorService;
039 import java.util.concurrent.Future;
040
041 import static java.util.Arrays.asList;
042
043 /**
044 * Implements the basics for a skeleton GriffonApplication.<p>
045 *
046 * @author Danno Ferrin
047 * @author Andres Almiray
048 */
049 public abstract class AbstractGriffonApplication extends AbstractObservable implements GriffonApplication {
050 private Binding bindings = new Binding();
051 private ConfigObject config;
052 private ConfigObject builderConfig;
053 private Object eventsConfig;
054 private AddonManager addonManager;
055 private ArtifactManager artifactManager;
056 private MVCGroupManager mvcGroupManager;
057 private ServiceManager serviceManager;
058
059 private Locale locale = Locale.getDefault();
060 public static final String[] EMPTY_ARGS = new String[0];
061 protected final Object lock = new Object();
062 private ApplicationPhase phase = ApplicationPhase.INITIALIZE;
063
064 private final EventRouter eventRouter = new EventRouter();
065 private final ResourceLocator resourceLocator = new ResourceLocator();
066 private final List<ShutdownHandler> shutdownHandlers = new ArrayList<ShutdownHandler>();
067 private final String[] startupArgs;
068 private final Object shutdownLock = new Object();
069 private final Logger log;
070
071 public AbstractGriffonApplication() {
072 this(EMPTY_ARGS);
073 }
074
075 public AbstractGriffonApplication(String[] args) {
076 startupArgs = new String[args.length];
077 System.arraycopy(args, 0, startupArgs, 0, args.length);
078 ApplicationHolder.setApplication(this);
079 log = LoggerFactory.getLogger(getClass());
080 MVCGroupExceptionHandler.registerWith(this);
081 }
082
083 public Binding getBindings() {
084 return bindings;
085 }
086
087 public void setBindings(Binding bindings) {
088 this.bindings = bindings;
089 }
090
091 public ConfigObject getConfig() {
092 return config;
093 }
094
095 public void setConfig(ConfigObject config) {
096 this.config = config;
097 }
098
099 public ConfigObject getBuilderConfig() {
100 return builderConfig;
101 }
102
103 public void setBuilderConfig(ConfigObject builderConfig) {
104 this.builderConfig = builderConfig;
105 }
106
107 public Object getEventsConfig() {
108 return eventsConfig;
109 }
110
111 public void setEventsConfig(Object eventsConfig) {
112 this.eventsConfig = eventsConfig;
113 }
114
115 public Map<String, ? extends GriffonModel> getModels() {
116 return getMvcGroupManager().getModels();
117 }
118
119 public Map<String, ? extends GriffonView> getViews() {
120 return getMvcGroupManager().getViews();
121 }
122
123 public Map<String, ? extends GriffonController> getControllers() {
124 return getMvcGroupManager().getControllers();
125 }
126
127 public Map<String, ? extends FactoryBuilderSupport> getBuilders() {
128 return getMvcGroupManager().getBuilders();
129 }
130
131 public Map<String, MVCGroup> getGroups() {
132 return getMvcGroupManager().getGroups();
133 }
134
135 public AddonManager getAddonManager() {
136 return addonManager;
137 }
138
139 public void setAddonManager(AddonManager addonManager) {
140 this.addonManager = addonManager;
141 }
142
143 public ArtifactManager getArtifactManager() {
144 return artifactManager;
145 }
146
147 public void setArtifactManager(ArtifactManager artifactManager) {
148 this.artifactManager = artifactManager;
149 }
150
151 public MVCGroupManager getMvcGroupManager() {
152 return mvcGroupManager;
153 }
154
155 public void setMvcGroupManager(MVCGroupManager mvcGroupManager) {
156 this.mvcGroupManager = mvcGroupManager;
157 }
158
159 public ServiceManager getServiceManager() {
160 return serviceManager;
161 }
162
163 public void setServiceManager(ServiceManager serviceManager) {
164 this.serviceManager = serviceManager;
165 }
166
167 public Map<String, ? extends GriffonService> getServices() {
168 return serviceManager.getServices();
169 }
170
171 public Locale getLocale() {
172 return locale;
173 }
174
175 public String[] getStartupArgs() {
176 return startupArgs;
177 }
178
179 public Logger getLog() {
180 return log;
181 }
182
183 public void setLocale(Locale locale) {
184 firePropertyChange("locale", this.locale, this.locale = locale);
185 }
186
187 public Metadata getMetadata() {
188 return Metadata.getCurrent();
189 }
190
191 public Class getAppConfigClass() {
192 return loadClass(GriffonApplication.Configuration.APPLICATION.getName());
193 }
194
195 public Class getConfigClass() {
196 return loadClass(GriffonApplication.Configuration.CONFIG.getName());
197 }
198
199 public Class getBuilderClass() {
200 return loadClass(GriffonApplication.Configuration.BUILDER.getName());
201 }
202
203 public Class getEventsClass() {
204 return loadClass(GriffonApplication.Configuration.EVENTS.getName());
205 }
206
207 public void initialize() {
208 if (phase == ApplicationPhase.INITIALIZE) {
209 GriffonApplicationHelper.prepare(this);
210 }
211 }
212
213 public void ready() {
214 if (phase != ApplicationPhase.STARTUP) return;
215
216 phase = ApplicationPhase.READY;
217 event(GriffonApplication.Event.READY_START.getName(), asList(this));
218 GriffonApplicationHelper.runLifecycleHandler(GriffonApplication.Lifecycle.READY.getName(), this);
219 event(GriffonApplication.Event.READY_END.getName(), asList(this));
220 phase = ApplicationPhase.MAIN;
221 }
222
223 public boolean canShutdown() {
224 event(GriffonApplication.Event.SHUTDOWN_REQUESTED.getName(), asList(this));
225 synchronized (shutdownLock) {
226 for (ShutdownHandler handler : shutdownHandlers) {
227 if (!handler.canShutdown(this)) {
228 event(GriffonApplication.Event.SHUTDOWN_ABORTED.getName(), asList(this));
229 if (log.isDebugEnabled()) {
230 try {
231 log.debug("Shutdown aborted by " + handler);
232 } catch (UnsupportedOperationException uoe) {
233 log.debug("Shutdown aborted by a handler");
234 }
235 }
236 return false;
237 }
238 }
239 }
240 return true;
241 }
242
243 public boolean shutdown() {
244 // avoids reentrant calls to shutdown()
245 // once permission to quit has been granted
246 if (phase == ApplicationPhase.SHUTDOWN) return false;
247
248 if (!canShutdown()) return false;
249 log.info("Shutdown is in process");
250
251 // signal that shutdown is in process
252 phase = ApplicationPhase.SHUTDOWN;
253
254 // stage 1 - alert all app event handlers
255 // wait for all handlers to complete before proceeding
256 // with stage #2 if and only if the current thread is
257 // the ui thread
258 log.debug("Shutdown stage 1: notify all event listeners");
259 if (isEventPublishingEnabled()) {
260 final CountDownLatch latch = new CountDownLatch(isUIThread() ? 1 : 0);
261 addApplicationEventListener(GriffonApplication.Event.SHUTDOWN_START.getName(), new RunnableWithArgs() {
262 @Override
263 public void run(Object[] args) {
264 latch.countDown();
265 }
266 });
267 event(GriffonApplication.Event.SHUTDOWN_START.getName(), asList(this));
268 try {
269 latch.await();
270 } catch (InterruptedException e) {
271 // ignore
272 }
273 }
274
275 // stage 2 - alert all shutdown handlers
276 log.debug("Shutdown stage 2: notify all shutdown handlers");
277 synchronized (shutdownLock) {
278 for (ShutdownHandler handler : shutdownHandlers) {
279 handler.onShutdown(this);
280 }
281 }
282
283 // stage 3 - destroy all mvc groups
284 log.debug("Shutdown stage 3: destroy all MVC groups");
285 List<String> mvcNames = new ArrayList<String>();
286 if (getMvcGroupManager() != null) {
287 mvcNames.addAll(getMvcGroupManager().getGroups().keySet());
288 for (String name : mvcNames) {
289 destroyMVCGroup(name);
290 }
291 }
292
293 // stage 4 - call shutdown script
294 log.debug("Shutdown stage 4: execute Shutdown script");
295 GriffonApplicationHelper.runLifecycleHandler(GriffonApplication.Lifecycle.SHUTDOWN.getName(), this);
296
297 return true;
298 }
299
300 public void startup() {
301 if (phase != ApplicationPhase.INITIALIZE) return;
302
303 phase = ApplicationPhase.STARTUP;
304 event(GriffonApplication.Event.STARTUP_START.getName(), asList(this));
305
306 Object startupGroups = ConfigUtils.getConfigValue(getConfig(), "application.startupGroups");
307 if (startupGroups instanceof List) {
308 if (log.isInfoEnabled()) {
309 log.info("Initializing all startup groups: " + startupGroups);
310 }
311
312 for (String groupName : (List<String>) startupGroups) {
313 createMVCGroup(groupName);
314 }
315 } else if (startupGroups != null && startupGroups.getClass().isArray()) {
316 Object[] groups = (Object[]) startupGroups;
317 if (log.isInfoEnabled()) {
318 log.info("Initializing all startup groups: " + Arrays.toString(groups));
319 }
320
321 for (Object groupName : groups) {
322 createMVCGroup(String.valueOf(groupName));
323 }
324 }
325
326 GriffonApplicationHelper.runLifecycleHandler(GriffonApplication.Lifecycle.STARTUP.getName(), this);
327
328 event(GriffonApplication.Event.STARTUP_END.getName(), asList(this));
329 }
330
331 public void event(String eventName) {
332 eventRouter.publish(eventName, Collections.emptyList());
333 }
334
335 public void event(String eventName, List params) {
336 eventRouter.publish(eventName, params);
337 }
338
339 // TODO @deprecated - remove before 1.0
340 public void eventOutside(String eventName) {
341 eventOutsideUI(eventName, Collections.emptyList());
342 }
343
344 // TODO @deprecated - remove before 1.0
345 public void eventOutside(String eventName, List params) {
346 eventOutsideUI(eventName, params);
347 }
348
349 public void eventOutsideUI(String eventName) {
350 eventRouter.publishOutsideUI(eventName, Collections.emptyList());
351 }
352
353 public void eventOutsideUI(String eventName, List params) {
354 eventRouter.publishOutsideUI(eventName, params);
355 }
356
357 public void eventAsync(String eventName) {
358 eventRouter.publishAsync(eventName, Collections.emptyList());
359 }
360
361 public void eventAsync(String eventName, List params) {
362 eventRouter.publishAsync(eventName, params);
363 }
364
365 public void addApplicationEventListener(Object listener) {
366 eventRouter.addEventListener(listener);
367 }
368
369 public void removeApplicationEventListener(Object listener) {
370 eventRouter.removeEventListener(listener);
371 }
372
373 public void addApplicationEventListener(String eventName, Closure listener) {
374 eventRouter.addEventListener(eventName, listener);
375 }
376
377 public void removeApplicationEventListener(String eventName, Closure listener) {
378 eventRouter.removeEventListener(eventName, listener);
379 }
380
381 public void addApplicationEventListener(String eventName, RunnableWithArgs listener) {
382 eventRouter.addEventListener(eventName, listener);
383 }
384
385 public void removeApplicationEventListener(String eventName, RunnableWithArgs listener) {
386 eventRouter.removeEventListener(eventName, listener);
387 }
388
389 public boolean isEventPublishingEnabled() {
390 return eventRouter.isEnabled();
391 }
392
393 public void setEventPublishingEnabled(boolean enabled) {
394 eventRouter.setEnabled(enabled);
395 }
396
397 public Object createApplicationContainer() {
398 return null;
399 }
400
401 public void addShutdownHandler(ShutdownHandler handler) {
402 if (handler != null && !shutdownHandlers.contains(handler)) shutdownHandlers.add(handler);
403 }
404
405 public void removeShutdownHandler(ShutdownHandler handler) {
406 if (handler != null) shutdownHandlers.remove(handler);
407 }
408
409 public ApplicationPhase getPhase() {
410 synchronized (lock) {
411 return this.phase;
412 }
413 }
414
415 protected void setPhase(ApplicationPhase phase) {
416 synchronized (lock) {
417 this.phase = phase;
418 }
419 }
420
421 // -----------------------
422
423 public boolean isUIThread() {
424 return UIThreadManager.getInstance().isUIThread();
425 }
426
427 // TODO @deprecated - remove before 1.0
428 public void execAsync(Runnable runnable) {
429 execInsideUIAsync(runnable);
430 }
431
432 // TODO @deprecated - remove before 1.0
433 public void execSync(Runnable runnable) {
434 execInsideUIAsync(runnable);
435 }
436
437 // TODO @deprecated - remove before 1.0
438 public void execOutside(Runnable runnable) {
439 execOutsideUI(runnable);
440 }
441
442 public void execInsideUIAsync(Runnable runnable) {
443 UIThreadManager.getInstance().executeAsync(runnable);
444 }
445
446 public void execInsideUISync(Runnable runnable) {
447 UIThreadManager.getInstance().executeSync(runnable);
448 }
449
450 public void execOutsideUI(Runnable runnable) {
451 UIThreadManager.getInstance().executeOutside(runnable);
452 }
453
454 public Future execFuture(ExecutorService executorService, Closure closure) {
455 return UIThreadManager.getInstance().executeFuture(executorService, closure);
456 }
457
458 public Future execFuture(Closure closure) {
459 return UIThreadManager.getInstance().executeFuture(closure);
460 }
461
462 public Future execFuture(ExecutorService executorService, Callable callable) {
463 return UIThreadManager.getInstance().executeFuture(executorService, callable);
464 }
465
466 public Future execFuture(Callable callable) {
467 return UIThreadManager.getInstance().executeFuture(callable);
468 }
469
470 public Object newInstance(Class clazz, String type) {
471 return GriffonApplicationHelper.newInstance(this, clazz, type);
472 }
473
474 public MVCGroup buildMVCGroup(String mvcType) {
475 return getMvcGroupManager().buildMVCGroup(mvcType, null, Collections.<String, Object>emptyMap());
476 }
477
478 public MVCGroup buildMVCGroup(String mvcType, String mvcName) {
479 return getMvcGroupManager().buildMVCGroup(mvcType, mvcName, Collections.<String, Object>emptyMap());
480 }
481
482 public MVCGroup buildMVCGroup(Map<String, Object> args, String mvcType) {
483 return getMvcGroupManager().buildMVCGroup(mvcType, null, args);
484 }
485
486 public MVCGroup buildMVCGroup(String mvcType, Map<String, Object> args) {
487 return getMvcGroupManager().buildMVCGroup(mvcType, null, args);
488 }
489
490 public MVCGroup buildMVCGroup(Map<String, Object> args, String mvcType, String mvcName) {
491 return getMvcGroupManager().buildMVCGroup(mvcType, mvcName, args);
492 }
493
494 public MVCGroup buildMVCGroup(String mvcType, String mvcName, Map<String, Object> args) {
495 return getMvcGroupManager().buildMVCGroup(mvcType, mvcName, args);
496 }
497
498 public List<? extends GriffonMvcArtifact> createMVCGroup(String mvcType) {
499 return getMvcGroupManager().createMVCGroup(mvcType, null, Collections.<String, Object>emptyMap());
500 }
501
502 public List<? extends GriffonMvcArtifact> createMVCGroup(Map<String, Object> args, String mvcType) {
503 return getMvcGroupManager().createMVCGroup(mvcType, null, args);
504 }
505
506 public List<? extends GriffonMvcArtifact> createMVCGroup(String mvcType, Map<String, Object> args) {
507 return getMvcGroupManager().createMVCGroup(mvcType, null, args);
508 }
509
510 public List<? extends GriffonMvcArtifact> createMVCGroup(String mvcType, String mvcName) {
511 return getMvcGroupManager().createMVCGroup(mvcType, mvcName, Collections.<String, Object>emptyMap());
512 }
513
514 public List<? extends GriffonMvcArtifact> createMVCGroup(Map<String, Object> args, String mvcType, String mvcName) {
515 return getMvcGroupManager().createMVCGroup(mvcType, mvcName, args);
516 }
517
518 public List<? extends GriffonMvcArtifact> createMVCGroup(String mvcType, String mvcName, Map<String, Object> args) {
519 return getMvcGroupManager().createMVCGroup(mvcType, mvcName, args);
520 }
521
522 public void destroyMVCGroup(String mvcName) {
523 getMvcGroupManager().destroyMVCGroup(mvcName);
524 }
525
526 public void withMVCGroup(String mvcType, Closure handler) {
527 getMvcGroupManager().withMVCGroup(mvcType, null, Collections.<String, Object>emptyMap(), handler);
528 }
529
530 public void withMVCGroup(String mvcType, String mvcName, Closure handler) {
531 getMvcGroupManager().withMVCGroup(mvcType, mvcName, Collections.<String, Object>emptyMap(), handler);
532 }
533
534 public void withMVCGroup(String mvcType, Map<String, Object> args, Closure handler) {
535 getMvcGroupManager().withMVCGroup(mvcType, null, args, handler);
536 }
537
538 public void withMVCGroup(Map<String, Object> args, String mvcType, Closure handler) {
539 getMvcGroupManager().withMVCGroup(mvcType, null, args, handler);
540 }
541
542 public void withMVCGroup(String mvcType, String mvcName, Map<String, Object> args, Closure handler) {
543 getMvcGroupManager().withMVCGroup(mvcType, mvcName, args, handler);
544 }
545
546 public void withMVCGroup(Map<String, Object> args, String mvcType, String mvcName, Closure handler) {
547 getMvcGroupManager().withMVCGroup(mvcType, mvcName, args, handler);
548 }
549
550 public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVCGroup(String mvcType, MVCClosure<M, V, C> handler) {
551 getMvcGroupManager().withMVCGroup(mvcType, null, Collections.<String, Object>emptyMap(), handler);
552 }
553
554 public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVCGroup(String mvcType, String mvcName, MVCClosure<M, V, C> handler) {
555 getMvcGroupManager().withMVCGroup(mvcType, mvcName, Collections.<String, Object>emptyMap(), handler);
556 }
557
558 public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVCGroup(String mvcType, Map<String, Object> args, MVCClosure<M, V, C> handler) {
559 getMvcGroupManager().withMVCGroup(mvcType, null, args, handler);
560 }
561
562 public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVCGroup(Map<String, Object> args, String mvcType, MVCClosure<M, V, C> handler) {
563 getMvcGroupManager().withMVCGroup(mvcType, null, args, handler);
564 }
565
566 public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVCGroup(String mvcType, String mvcName, Map<String, Object> args, MVCClosure<M, V, C> handler) {
567 getMvcGroupManager().withMVCGroup(mvcType, mvcName, args, handler);
568 }
569
570 public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVCGroup(Map<String, Object> args, String mvcType, String mvcName, MVCClosure<M, V, C> handler) {
571 getMvcGroupManager().withMVCGroup(mvcType, mvcName, args, handler);
572 }
573
574 private Class loadClass(String className) {
575 try {
576 return getClass().getClassLoader().loadClass(className);
577 } catch (ClassNotFoundException e) {
578 // ignored
579 }
580 return null;
581 }
582
583 public InputStream getResourceAsStream(String name) {
584 return resourceLocator.getResourceAsStream(name);
585 }
586
587 public URL getResourceAsURL(String name) {
588 return resourceLocator.getResourceAsURL(name);
589 }
590
591 public List<URL> getResources(String name) {
592 return resourceLocator.getResources(name);
593 }
594 }
|