GriffonApplicationHelper.java
001 /*
002  * Copyright 2008-2013 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 org.codehaus.griffon.runtime.util;
017 
018 import griffon.core.*;
019 import griffon.core.controller.GriffonControllerAction;
020 import griffon.core.controller.GriffonControllerActionManager;
021 import griffon.core.factories.*;
022 import griffon.core.resources.ResourcesInjector;
023 import griffon.exceptions.GriffonException;
024 import griffon.util.*;
025 import griffon.util.logging.LogManager;
026 import groovy.lang.*;
027 import groovy.util.ConfigObject;
028 import groovy.util.FactoryBuilderSupport;
029 import org.codehaus.griffon.runtime.core.ControllerArtifactHandler;
030 import org.codehaus.griffon.runtime.core.ModelArtifactHandler;
031 import org.codehaus.griffon.runtime.core.ServiceArtifactHandler;
032 import org.codehaus.griffon.runtime.core.ViewArtifactHandler;
033 import org.codehaus.griffon.runtime.core.controller.NoopGriffonControllerActionManager;
034 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
035 import org.codehaus.groovy.runtime.InvokerHelper;
036 import org.slf4j.Logger;
037 import org.slf4j.LoggerFactory;
038 
039 import java.beans.PropertyEditor;
040 import java.beans.PropertyEditorManager;
041 import java.io.IOException;
042 import java.lang.reflect.Constructor;
043 import java.net.URL;
044 import java.util.*;
045 
046 import static griffon.util.ConfigUtils.*;
047 import static griffon.util.GriffonExceptionHandler.handleThrowable;
048 import static griffon.util.GriffonExceptionHandler.sanitize;
049 import static griffon.util.GriffonNameUtils.isBlank;
050 import static java.util.Arrays.asList;
051 import static org.codehaus.groovy.runtime.ResourceGroovyMethods.eachLine;
052 
053 /**
054  * Utility class for bootstrapping an application and handling of MVC groups.</p>
055  *
056  @author Danno Ferrin
057  @author Andres Almiray
058  */
059 public class GriffonApplicationHelper {
060     private static final Logger LOG = LoggerFactory.getLogger(GriffonApplicationHelper.class);
061 
062     private static final Map<String, String> DEFAULT_PLATFORM_HANDLERS = CollectionUtils.<String, String>map()
063         .e("linux""org.codehaus.griffon.runtime.util.DefaultLinuxPlatformHandler")
064         .e("linux64""org.codehaus.griffon.runtime.util.DefaultLinuxPlatformHandler")
065         .e("macosx""org.codehaus.griffon.runtime.util.DefaultMacOSXPlatformHandler")
066         .e("macosx64""org.codehaus.griffon.runtime.util.DefaultMacOSXPlatformHandler")
067         .e("solaris""org.codehaus.griffon.runtime.util.DefaultSolarisPlatformHandler")
068         .e("windows""org.codehaus.griffon.runtime.util.DefaultWindowsPlatformHandler")
069         .e("windows64""org.codehaus.griffon.runtime.util.DefaultWindowsPlatformHandler");
070 
071     private static final String LOCATION_CLASSPATH = "classpath:";
072     private static final String LOCATION_FILE = "file:";
073     private static final String PROPERTIES_SUFFIX = ".properties";
074     private static final String GROOVY_SUFFIX = ".groovy";
075 
076     private static final String KEY_MESSAGE_SOURCE_FACTORY = "app.messageSource.factory";
077     private static final String KEY_RESOURCES_INJECTOR_FACTORY = "app.resourceInjector.factory";
078     private static final String KEY_EVENT_ROUTER_FACTORY = "app.eventRouter.factory";
079     private static final String KEY_ADDON_MANAGER_FACTORY = "app.addonManager.factory";
080     private static final String KEY_ARTIFACT_MANAGER_FACTORY = "app.artifactManager.factory";
081     private static final String KEY_ACTION_MANAGER_FACTORY = "app.actionManager.factory";
082     private static final String KEY_MVCGROUP_MANAGER_FACTORY = "app.mvcGroupManager.factory";
083     private static final String KEY_RESOURCE_RESOLVER_FACTORY = "app.resourceResolver.factory";
084     private static final String KEY_LOG_MANAGER_FACTORY = "app.logManager.factory";
085 
086     private static final String KEY_APP_LIFECYCLE_HANDLER_DISABLE = "app.lifecycle.handler.disable";
087     private static final String KEY_GRIFFON_ACTION_MANAGER_DISABLE = "griffon.action.manager.disable";
088 
089     private static final String DEFAULT_MESSAGE_SOURCE_FACTORY = "org.codehaus.griffon.runtime.core.factories.DefaultMessageSourceFactory";
090     private static final String DEFAULT_RESOURCES_INJECTOR_FACTORY = "org.codehaus.griffon.runtime.core.factories.DefaultResourcesInjectorFactory";
091     private static final String DEFAULT_EVENT_ROUTER_FACTORY = "org.codehaus.griffon.runtime.core.factories.DefaultEventRouterFactory";
092     private static final String DEFAULT_ADDON_MANAGER_FACTORY = "org.codehaus.griffon.runtime.core.factories.DefaultAddonManagerFactory";
093     private static final String DEFAULT_ARTIFACT_MANAGER_FACTORY = "org.codehaus.griffon.runtime.core.factories.DefaultArtifactManagerFactory";
094     private static final String DEFAULT_MVCGROUP_MANAGER_FACTORY = "org.codehaus.griffon.runtime.core.factories.DefaultMVCGroupManagerFactory";
095     private static final String DEFAULT_RESOURCE_RESOLVER_FACTORY = "org.codehaus.griffon.runtime.core.factories.DefaultResourceResolverFactory";
096     private static final String DEFAULT_LOG_MANAGER_FACTORY = "org.codehaus.griffon.runtime.core.factories.DefaultLogManagerFactory";
097 
098     static {
099         ExpandoMetaClassCreationHandle.enable();
100     }
101 
102     /**
103      * Creates, register and assigns an ExpandoMetaClass for a target class.<p>
104      * The newly created metaClass will accept changes after initialization.
105      *
106      @param clazz the target class
107      @return an ExpandoMetaClass
108      */
109     public static MetaClass expandoMetaClassFor(Class clazz) {
110         MetaClass mc = GroovySystem.getMetaClassRegistry().getMetaClass(clazz);
111         if (!(mc instanceof ExpandoMetaClass)) {
112             mc = new ExpandoMetaClass(clazz, true, true);
113             mc.initialize();
114             GroovySystem.getMetaClassRegistry().setMetaClass(clazz, mc);
115         }
116         return mc;
117     }
118 
119     /**
120      * Setups an application.<p>
121      * This method performs the following tasks<ul>
122      <li>Sets "griffon.start.dir" as system property.</li>
123      <li>Calls the Initialize life cycle script.</li>
124      <li>Reads runtime and builder configuration.</li>
125      <li>Setups basic artifact handlers.</li>
126      <li>Initializes available addons.</li>
127      </ul>
128      *
129      @param app the current Griffon application
130      */
131     public static void prepare(GriffonApplication app) {
132         app.getBindings().setVariable("app", app);
133 
134         Metadata.getCurrent().getGriffonStartDir();
135         Metadata.getCurrent().getGriffonWorkingDir();
136 
137         readAndSetConfiguration(app);
138         app.event(GriffonApplication.Event.BOOTSTRAP_START.getName(), asList(app));
139 
140         initializeMessageSource(app);
141         initializeResourceResolver(app);
142         initializeResourcesInjector(app);
143         initializePropertyEditors(app);
144 
145         applyPlatformTweaks(app);
146         runLifecycleHandler(GriffonApplication.Lifecycle.INITIALIZE.getName(), app);
147         initializeArtifactManager(app);
148         initializeMvcManager(app);
149         initializeAddonManager(app);
150         initializeActionManager(app);
151 
152         app.event(GriffonApplication.Event.BOOTSTRAP_END.getName(), asList(app));
153     }
154 
155     private static ConfigObject doLoadConfig(ConfigReader configReader, Class configClass, String configFileName) {
156         if (configClass != nullconfigFileName = configClass.getSimpleName();
157         return loadConfig(configReader, configClass, configFileName);
158     }
159 
160     private static ConfigObject doLoadConfigWithI18n(Locale locale, ConfigReader configReader, Class configClass, String configFileName) {
161         if (configClass != nullconfigFileName = configClass.getSimpleName();
162         return loadConfigWithI18n(locale, configReader, configClass, configFileName);
163     }
164 
165     private static void readAndSetConfiguration(final GriffonApplication app) {
166         ConfigReader configReader = createConfigReader();
167 
168         ConfigObject appConfig = doLoadConfig(configReader, app.getAppConfigClass(), GriffonApplication.Configuration.APPLICATION.getName());
169         setApplicationLocale(app, getConfigValue(appConfig, "application.locale", Locale.getDefault()));
170         appConfig = doLoadConfigWithI18n(app.getLocale(), configReader, app.getAppConfigClass(), GriffonApplication.Configuration.APPLICATION.getName());
171         app.setConfig(appConfig);
172         app.getConfig().merge(doLoadConfigWithI18n(app.getLocale(), configReader, app.getConfigClass(), GriffonApplication.Configuration.CONFIG.getName()));
173 
174         initializeLogManager(app);
175 
176         loadExternalConfig(app, configReader);
177         GriffonExceptionHandler.configure(app.getConfig().flatten(new LinkedHashMap()));
178 
179         app.setBuilderConfig(doLoadConfigWithI18n(app.getLocale(), configReader, app.getBuilderClass(), GriffonApplication.Configuration.BUILDER.getName()));
180 
181         initializeEventRouter(app);
182 
183         Object events = safeNewInstance(app.getEventsClass()false);
184         if (events != null) {
185             app.setEventsConfig(events);
186             app.addApplicationEventListener(app.getEventsConfig());
187         }
188     }
189 
190     private static void loadExternalConfig(GriffonApplication app, ConfigReader configReader) {
191         List<String> locations = (List<String>getConfigValue(app.getConfig()"griffon.config.locations", Collections.emptyList());
192         for (String location : locations) {
193             boolean groovyScriptAllowed = false;
194 
195             String parsedLocation = location;
196             if (location.startsWith(LOCATION_CLASSPATH)) {
197                 parsedLocation = location.substring(LOCATION_CLASSPATH.length()).trim();
198             else if (location.startsWith(LOCATION_FILE)) {
199                 parsedLocation = location.substring(LOCATION_FILE.length()).trim();
200             else {
201                 // assume it's a class definition
202                 groovyScriptAllowed = true;
203             }
204 
205             if (groovyScriptAllowed) {
206                 Class locationScriptClass = safeLoadClass(parsedLocation);
207                 if (locationScriptClass != null) {
208                     if (LOG.isDebugEnabled()) {
209                         LOG.debug("Loading external configuration location '" + location + "'.");
210                     }
211                     app.getConfig().merge(loadConfigWithI18n(app.getLocale(), configReader, locationScriptClass, null));
212                 else {
213                     // invalid location. Log & skip
214                     if (LOG.isWarnEnabled()) {
215                         LOG.warn("Skipping invalid external configuration location '" + location + "'.");
216                     }
217                 }
218             else if (parsedLocation.endsWith(PROPERTIES_SUFFIX|| parsedLocation.endsWith(GROOVY_SUFFIX)) {
219                 if (LOG.isDebugEnabled()) {
220                     LOG.debug("Loading external configuration location '" + location + "'.");
221                 }
222                 app.getConfig().merge(loadConfigWithI18n(app.getLocale(), configReader, null, parsedLocation));
223             else {
224                 // invalid location. Log & skip
225                 if (LOG.isWarnEnabled()) {
226                     LOG.warn("Skipping invalid external configuration location '" + location + "'.");
227                 }
228             }
229         }
230     }
231 
232     private static void initializeLogManager(GriffonApplication app) {
233         String className = getConfigValueAsString(app.getConfig(), KEY_LOG_MANAGER_FACTORY, DEFAULT_LOG_MANAGER_FACTORY);
234         LogManagerFactory factory = (LogManagerFactorysafeNewInstance(className);
235         LogManager logManager = factory.create(app);
236         logManager.configure(app.getConfig());
237     }
238 
239     private static void initializeMessageSource(GriffonApplication app) {
240         String className = getConfigValueAsString(app.getConfig(), KEY_MESSAGE_SOURCE_FACTORY, DEFAULT_MESSAGE_SOURCE_FACTORY);
241         if (LOG.isDebugEnabled()) {
242             LOG.debug("Using " + className + " as MessageSourceFactory");
243         }
244         MessageSourceFactory factory = (MessageSourceFactorysafeNewInstance(className);
245         InvokerHelper.setProperty(app, "messageSource", factory.create(app));
246     }
247 
248     private static void initializeResourceResolver(GriffonApplication app) {
249         String className = getConfigValueAsString(app.getConfig(), KEY_RESOURCE_RESOLVER_FACTORY, DEFAULT_RESOURCE_RESOLVER_FACTORY);
250         if (LOG.isDebugEnabled()) {
251             LOG.debug("Using " + className + " as ResourceResolverFactory");
252         }
253         ResourceResolverFactory factory = (ResourceResolverFactorysafeNewInstance(className);
254         InvokerHelper.setProperty(app, "resourceResolver", factory.create(app));
255     }
256 
257     private static void initializeResourcesInjector(GriffonApplication app) {
258         String className = getConfigValueAsString(app.getConfig(), KEY_RESOURCES_INJECTOR_FACTORY, DEFAULT_RESOURCES_INJECTOR_FACTORY);
259         if (LOG.isDebugEnabled()) {
260             LOG.debug("Using " + className + " as ResourcesInjectorFactory");
261         }
262         ResourcesInjectorFactory factory = (ResourcesInjectorFactorysafeNewInstance(className);
263         final ResourcesInjector injector = factory.create(app);
264         app.addApplicationEventListener(GriffonApplication.Event.NEW_INSTANCE.getName()new RunnableWithArgs() {
265             public void run(Object[] args) {
266                 Object instance = args[2];
267                 injector.injectResources(instance);
268             }
269         });
270     }
271 
272     private static void initializePropertyEditors(GriffonApplication app) {
273         Enumeration<URL> urls = null;
274 
275         try {
276             urls = ApplicationClassLoader.get().getResources("META-INF/services/" + PropertyEditor.class.getName());
277         catch (IOException ioe) {
278             return;
279         }
280 
281         if (urls == nullreturn;
282 
283         while (urls.hasMoreElements()) {
284             URL url = urls.nextElement();
285             if (LOG.isDebugEnabled()) {
286                 LOG.debug("Reading " + PropertyEditor.class.getName() " definitions from " + url);
287             }
288 
289             try {
290                 eachLine(url, new RunnableWithArgsClosure(new RunnableWithArgs() {
291                     @Override
292                     public void run(Object[] args) {
293                         String line = (Stringargs[0];
294                         if (line.startsWith("#"|| isBlank(line)) return;
295                         try {
296                             String[] parts = line.trim().split("=");
297                             Class targetType = loadClass(parts[0].trim());
298                             Class editorClass = loadClass(parts[1].trim());
299                             if (LOG.isDebugEnabled()) {
300                                 LOG.debug("Registering " + editorClass.getName() " as editor for " + targetType.getName());
301                             }
302                             PropertyEditorManager.registerEditor(targetType, editorClass);
303                         catch (Exception e) {
304                             if (LOG.isWarnEnabled()) {
305                                 LOG.warn("Could not load PropertyEditor with " + line, sanitize(e));
306                             }
307                         }
308                     }
309                 }));
310             catch (IOException e) {
311                 if (LOG.isWarnEnabled()) {
312                     LOG.warn("Could not load PropertyEditor definitions from " + url, sanitize(e));
313                 }
314             }
315         }
316     }
317 
318     private static void initializeEventRouter(GriffonApplication app) {
319         InvokerHelper.setProperty(app, "eventRouter", createEventRouter(app));
320     }
321 
322     public static EventRouter createEventRouter(GriffonApplication app) {
323         String className = getConfigValueAsString(app.getConfig(), KEY_EVENT_ROUTER_FACTORY, DEFAULT_EVENT_ROUTER_FACTORY);
324         if (LOG.isDebugEnabled()) {
325             LOG.debug("Using " + className + " as EventRouterFactory");
326         }
327         EventRouterFactory factory = (EventRouterFactorysafeNewInstance(className);
328         return factory.create(app);
329     }
330 
331     private static void setApplicationLocale(GriffonApplication app, Object localeValue) {
332         if (localeValue instanceof Locale) {
333             app.setLocale((LocalelocaleValue);
334         else if (localeValue instanceof CharSequence) {
335             app.setLocale(parseLocale(String.valueOf(localeValue)));
336         }
337     }
338 
339     public static Locale parseLocale(String locale) {
340         if (isBlank(locale)) return Locale.getDefault();
341         String[] parts = locale.split("_");
342         switch (parts.length) {
343             case 1:
344                 return new Locale(parts[0]);
345             case 2:
346                 return new Locale(parts[0], parts[1]);
347             case 3:
348                 return new Locale(parts[0], parts[1], parts[2]);
349             default:
350                 return Locale.getDefault();
351         }
352     }
353 
354     public static void applyPlatformTweaks(GriffonApplication app) {
355         String platform = GriffonApplicationUtils.platform;
356         String handlerClassName = getConfigValueAsString(app.getConfig()"platform.handler." + platform, DEFAULT_PLATFORM_HANDLERS.get(platform));
357         PlatformHandler platformHandler = (PlatformHandlersafeNewInstance(handlerClassName);
358         platformHandler.handle(app);
359     }
360 
361     private static void initializeArtifactManager(GriffonApplication app) {
362         if (app.getArtifactManager() == null) {
363             String className = getConfigValueAsString(app.getConfig(), KEY_ARTIFACT_MANAGER_FACTORY, DEFAULT_ARTIFACT_MANAGER_FACTORY);
364             if (LOG.isDebugEnabled()) {
365                 LOG.debug("Using " + className + " as ArtifactManagerFactory");
366             }
367             ArtifactManagerFactory factory = (ArtifactManagerFactorysafeNewInstance(className);
368             InvokerHelper.setProperty(app, "artifactManager", factory.create(app));
369         }
370 
371         // initialize default Artifact handlers
372         app.getArtifactManager().registerArtifactHandler(new ModelArtifactHandler(app));
373         app.getArtifactManager().registerArtifactHandler(new ViewArtifactHandler(app));
374         app.getArtifactManager().registerArtifactHandler(new ControllerArtifactHandler(app));
375         if (!ServiceArtifactHandler.isBasicInjectionDisabled()) {
376             app.getArtifactManager().registerArtifactHandler(new ServiceArtifactHandler(app));
377         }
378 
379         // load additional handlers
380         loadArtifactHandlers(app);
381 
382         app.getArtifactManager().loadArtifactMetadata();
383     }
384 
385     private static void initializeAddonManager(GriffonApplication app) {
386         if (app.getAddonManager() == null) {
387             String className = getConfigValueAsString(app.getConfig(), KEY_ADDON_MANAGER_FACTORY, DEFAULT_ADDON_MANAGER_FACTORY);
388             if (LOG.isDebugEnabled()) {
389                 LOG.debug("Using " + className + " as AddonManagerFactory");
390             }
391             AddonManagerFactory factory = (AddonManagerFactorysafeNewInstance(className);
392             InvokerHelper.setProperty(app, "addonManager", factory.create(app));
393         }
394         app.getAddonManager().initialize();
395     }
396 
397     private static void initializeActionManager(GriffonApplication app) {
398         InvokerHelper.setProperty(app, "actionManager"new NoopGriffonControllerActionManager(app));
399 
400         boolean disableActionManager = getConfigValueAsBoolean(app.getConfig(), KEY_GRIFFON_ACTION_MANAGER_DISABLE, false);
401         if (disableActionManager) {
402             if (LOG.isInfoEnabled()) {
403                 LOG.info("GriffonControllerActionManager is disabled.");
404             }
405             return;
406         }
407 
408         String className = getConfigValueAsString(app.getConfig(), KEY_ACTION_MANAGER_FACTORY, null);
409         if (isBlank(className|| "null".equals(className)) {
410             URL url = ApplicationClassLoader.get().getResource("META-INF/services/" + GriffonControllerActionManagerFactory.class.getName());
411             if (null == url) {
412                 if (LOG.isInfoEnabled()) {
413                     LOG.info("GriffonControllerActionManager is disabled.");
414                 }
415                 return;
416             }
417             try {
418                 className = DefaultGroovyMethods.getText(url).trim();
419             catch (IOException e) {
420                 if (LOG.isDebugEnabled()) {
421                     LOG.debug("Cannot read GriffonControllerActionManager definition from " + url, sanitize(e));
422                     className = null;
423                 }
424             }
425         }
426 
427         if (isBlank(className)) {
428             if (LOG.isInfoEnabled()) {
429                 LOG.info("GriffonControllerActionManager is disabled.");
430             }
431             return;
432         }
433 
434         if (LOG.isDebugEnabled()) {
435             LOG.debug("Using " + className + " as GriffonControllerActionManagerFactory");
436         }
437         GriffonControllerActionManagerFactory factory = (GriffonControllerActionManagerFactorysafeNewInstance(className);
438         final GriffonControllerActionManager actionManager = factory.create(app);
439         InvokerHelper.setProperty(app, "actionManager", actionManager);
440 
441         app.addApplicationEventListener(GriffonApplication.Event.NEW_INSTANCE.getName()new RunnableWithArgs() {
442             public void run(Object[] args) {
443                 String type = (Stringargs[1];
444                 if (GriffonControllerClass.TYPE.equals(type)) {
445                     GriffonController controller = (GriffonControllerargs[2];
446                     actionManager.createActions(controller);
447                 }
448             }
449         });
450 
451         app.addApplicationEventListener(GriffonApplication.Event.INITIALIZE_MVC_GROUP.getName()new RunnableWithArgs() {
452             public void run(Object[] args) {
453                 MVCGroupConfiguration groupConfig = (MVCGroupConfigurationargs[0];
454                 MVCGroup group = (MVCGroupargs[1];
455                 GriffonController controller = group.getController();
456                 if (controller == nullreturn;
457                 FactoryBuilderSupport builder = group.getBuilder();
458                 Map<String, GriffonControllerAction> actions = actionManager.actionsFor(controller);
459                 for (Map.Entry<String, GriffonControllerAction> action : actions.entrySet()) {
460                     String actionKey = actionManager.normalizeName(action.getKey()) + GriffonControllerActionManager.ACTION;
461                     if (LOG.isTraceEnabled()) {
462                         LOG.trace("Adding action " + actionKey + " to " + groupConfig.getMvcType() ":" + group.getMvcId() ":builder");
463                     }
464                     builder.setVariable(actionKey, action.getValue().getToolkitAction());
465                 }
466             }
467         });
468     }
469 
470 
471     private static void initializeMvcManager(GriffonApplication app) {
472         if (app.getMvcGroupManager() == null) {
473             String className = getConfigValueAsString(app.getConfig(), KEY_MVCGROUP_MANAGER_FACTORY, DEFAULT_MVCGROUP_MANAGER_FACTORY);
474             if (LOG.isDebugEnabled()) {
475                 LOG.debug("Using " + className + " as MVCGroupManagerFactory");
476             }
477             MVCGroupManagerFactory factory = (MVCGroupManagerFactorysafeNewInstance(className);
478             InvokerHelper.setProperty(app, "mvcGroupManager", factory.create(app));
479         }
480 
481         Map<String, MVCGroupConfiguration> configurations = new LinkedHashMap<String, MVCGroupConfiguration>();
482         Map<String, Map<String, Object>> mvcGroups = (Map<String, Map<String, Object>>app.getConfig().get("mvcGroups");
483         if (mvcGroups != null) {
484             for (Map.Entry<String, Map<String, Object>> groupEntry : mvcGroups.entrySet()) {
485                 String type = groupEntry.getKey();
486                 if (LOG.isDebugEnabled()) {
487                     LOG.debug("Adding MVC group " + type);
488                 }
489                 Map<String, Object> members = groupEntry.getValue();
490                 Map<String, Object> configMap = new LinkedHashMap<String, Object>();
491                 Map<String, String> membersCopy = new LinkedHashMap<String, String>();
492                 for (Object o : members.entrySet()) {
493                     Map.Entry entry = (Map.Entryo;
494                     String key = String.valueOf(entry.getKey());
495                     if ("config".equals(key&& entry.getValue() instanceof Map) {
496                         configMap = (Map<String, Object>entry.getValue();
497                     else {
498                         membersCopy.put(key, String.valueOf(entry.getValue()));
499                     }
500                 }
501                 configurations.put(type, app.getMvcGroupManager().newMVCGroupConfiguration(type, membersCopy, configMap));
502             }
503         }
504 
505         app.getMvcGroupManager().initialize(configurations);
506     }
507 
508     private static void loadArtifactHandlers(final GriffonApplication app) {
509         Enumeration<URL> urls = null;
510 
511         try {
512             urls = ApplicationClassLoader.get().getResources("META-INF/services/" + ArtifactHandler.class.getName());
513         catch (IOException ioe) {
514             return;
515         }
516 
517         if (urls == nullreturn;
518 
519         while (urls.hasMoreElements()) {
520             URL url = urls.nextElement();
521             if (LOG.isDebugEnabled()) {
522                 LOG.debug("Reading " + ArtifactHandler.class.getName() " definitions from " + url);
523             }
524 
525             try {
526                 eachLine(url, new RunnableWithArgsClosure(new RunnableWithArgs() {
527                     @Override
528                     public void run(Object[] args) {
529                         String line = (Stringargs[0];
530                         if (line.startsWith("#"|| isBlank(line)) return;
531                         try {
532                             Class artifactHandlerClass = loadClass(line);
533                             Constructor ctor = artifactHandlerClass.getDeclaredConstructor(GriffonApplication.class);
534                             ArtifactHandler handler = null;
535                             if (ctor != null) {
536                                 handler = (ArtifactHandlerctor.newInstance(app);
537                             else {
538                                 handler = (ArtifactHandlersafeNewInstance(artifactHandlerClass);
539                             }
540                             app.getArtifactManager().registerArtifactHandler(handler);
541                         catch (Exception e) {
542                             if (LOG.isWarnEnabled()) {
543                                 LOG.warn("Could not load ArtifactHandler with " + line, sanitize(e));
544                             }
545                         }
546                     }
547                 }));
548             catch (IOException e) {
549                 if (LOG.isWarnEnabled()) {
550                     LOG.warn("Could not load ArtifactHandler from " + url, sanitize(e));
551                 }
552             }
553         }
554     }
555 
556     /**
557      * Executes a script inside the UI Thread.<p>
558      * On Swing this would be the Event Dispatch Thread.
559      */
560     public static void runLifecycleHandler(String handlerName, GriffonApplication app) {
561         boolean skipHandler = getConfigValueAsBoolean(app.getConfig(), KEY_APP_LIFECYCLE_HANDLER_DISABLE, false);
562         if(skipHandler) {
563             if (LOG.isDebugEnabled()) {
564                 LOG.info("Lifecycle handler '" + handlerName + "' has been disabled. SKIPPING.");
565             }
566             return;
567         }
568 
569         Class<?> handlerClass = null;
570         try {
571             handlerClass = loadConfigurationalClass(handlerName);
572         catch (ClassNotFoundException cnfe) {
573             if (cnfe.getMessage().equals(handlerName)) {
574                 // the script must not exist, do nothing
575                 //LOGME - may be because of chained failures
576                 return;
577             else {
578                 throw new GriffonException(cnfe);
579             }
580         }
581 
582         if (Script.class.isAssignableFrom(handlerClass)) {
583             doRunScript(handlerName, handlerClass, app);
584         else if (LifecycleHandler.class.isAssignableFrom(handlerClass)) {
585             doRunLifecycleHandler(handlerName, handlerClass, app);
586         }
587     }
588 
589     private static void doRunScript(String scriptName, Class handlerClass, GriffonApplication app) {
590         Script script = (ScriptsafeNewInstance(handlerClass);
591         script.setBinding(app.getBindings());
592         UIThreadManager.enhance(script);
593         if (LOG.isInfoEnabled()) {
594             LOG.info("Running lifecycle handler (script) '" + scriptName + "'");
595         }
596         UIThreadManager.getInstance().executeSync(script);
597     }
598 
599     private static void doRunLifecycleHandler(String handlerName, Class handlerClass, GriffonApplication app) {
600         LifecycleHandler handler = (LifecycleHandlersafeNewInstance(handlerClass);
601         if (LOG.isInfoEnabled()) {
602             LOG.info("Running lifecycle handler (class) '" + handlerName + "'");
603         }
604         UIThreadManager.getInstance().executeSync(handler);
605     }
606 
607     /**
608      * Creates a new instance of the specified class.<p>
609      * Publishes a <strong>NewInstance</strong> event with the following arguments<ul>
610      <li>klass - the target Class</li>
611      <li>type - the type of the instance (i.e, 'controller','service')</li>
612      <li>instance - the newly created instance</li>
613      </ul>
614      *
615      @param app   the current GriffonApplication
616      @param klass the target Class from which the instance will be created
617      @return a newly created instance of type klass
618      */
619     public static Object newInstance(GriffonApplication app, Class klass) {
620         return newInstance(app, klass, "");
621     }
622 
623     /**
624      * Creates a new instance of the specified class.<p>
625      * Publishes a <strong>NewInstance</strong> event with the following arguments<ul>
626      <li>klass - the target Class</li>
627      <li>type - the type of the instance (i.e, 'controller','service')</li>
628      <li>instance - the newly created instance</li>
629      </ul>
630      *
631      @param app   the current GriffonApplication
632      @param klass the target Class from which the instance will be created
633      @param type  optional type parameter, used when publishing a 'NewInstance' event
634      @return a newly created instance of type klass
635      */
636     public static Object newInstance(GriffonApplication app, Class klass, String type) {
637         if (isBlank(type)) type = "";
638         if (LOG.isDebugEnabled()) {
639             LOG.debug("Instantiating " + klass.getName() " with type '" + type + "'");
640         }
641 
642         Object instance = null;
643         try {
644             instance = klass.newInstance();
645         catch (InstantiationException e) {
646             throw new GriffonException(e);
647         catch (IllegalAccessException e) {
648             throw new GriffonException(e);
649         }
650 
651         // GRIFFON-535
652         if (instance != null) {
653             GriffonClass griffonClass = app.getArtifactManager().findGriffonClass(klass);
654             MetaClass mc = griffonClass != null ? griffonClass.getMetaClass() : expandoMetaClassFor(klass);
655             enhance(app, klass, mc, instance);
656             app.event(GriffonApplication.Event.NEW_INSTANCE.getName(), asList(klass, type, instance));
657         }
658         return instance;
659     }
660 
661     public static void enhance(GriffonApplication app, Class klass, MetaClass mc, Object instance) {
662         try {
663             InvokerHelper.invokeMethod(instance, "setApp", app);
664         catch (MissingMethodException mme) {
665             try {
666                 InvokerHelper.setProperty(instance, "app", app);
667             catch (MissingPropertyException mpe) {
668                 if (mc instanceof ExpandoMetaClass) {
669                     ((ExpandoMetaClassmc).registerBeanProperty("app", app);
670                 }
671             }
672         }
673 
674         if (!GriffonArtifact.class.isAssignableFrom(klass)) {
675             UIThreadManager.enhance(mc);
676         }
677     }
678 
679     public static Class<?> loadConfigurationalClass(String classNamethrows ClassNotFoundException {
680         if (!className.contains(".")) {
681             String fixedClassName = "config." + className;
682             try {
683                 return loadClass(fixedClassName);
684             catch (ClassNotFoundException cnfe) {
685                 if (cnfe.getMessage().equals(fixedClassName)) {
686                     return loadClass(className);
687                 else {
688                     throw new GriffonException(cnfe);
689                 }
690             }
691         }
692         return loadClass(className);
693     }
694 
695     public static Class<?> loadClass(String classNamethrows ClassNotFoundException {
696         ClassNotFoundException cnfe = null;
697 
698         ClassLoader cl = GriffonApplicationHelper.class.getClassLoader();
699         try {
700             return cl.loadClass(className);
701         catch (ClassNotFoundException e) {
702             cnfe = e;
703         }
704 
705         cl = ApplicationClassLoader.get();
706         try {
707             return cl.loadClass(className);
708         catch (ClassNotFoundException e) {
709             cnfe = e;
710         }
711 
712         if (cnfe != nullthrow cnfe;
713         return null;
714     }
715 
716     public static Class<?> safeLoadClass(String className) {
717         try {
718             return loadClass(className);
719         catch (ClassNotFoundException e) {
720             return null;
721         }
722     }
723 
724     public static Object safeNewInstance(String className) {
725         try {
726             return loadClass(className).newInstance();
727         catch (Exception e) {
728             handleThrowable(e);
729             return null;
730         }
731     }
732 
733     public static Object safeNewInstance(Class<?> clazz) {
734         return safeNewInstance(clazz, true);
735     }
736 
737     public static Object safeNewInstance(Class<?> clazz, boolean logException) {
738         try {
739             return clazz.newInstance();
740         catch (Exception e) {
741             if (logExceptionhandleThrowable(e);
742             return null;
743         }
744     }
745 }