AbstractGriffonApplication.java
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.STARTUPreturn;
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.SHUTDOWNreturn 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() 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.INITIALIZEreturn;
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 != nullshutdownHandlers.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 }