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 != null) configFileName = 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 != null) configFileName = 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 = (LogManagerFactory) safeNewInstance(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 = (MessageSourceFactory) safeNewInstance(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 = (ResourceResolverFactory) safeNewInstance(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 = (ResourcesInjectorFactory) safeNewInstance(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 == null) return;
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 = (String) args[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 = (EventRouterFactory) safeNewInstance(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((Locale) localeValue);
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 = (PlatformHandler) safeNewInstance(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 = (ArtifactManagerFactory) safeNewInstance(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 = (AddonManagerFactory) safeNewInstance(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 = (GriffonControllerActionManagerFactory) safeNewInstance(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 = (String) args[1];
444 if (GriffonControllerClass.TYPE.equals(type)) {
445 GriffonController controller = (GriffonController) args[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 = (MVCGroupConfiguration) args[0];
454 MVCGroup group = (MVCGroup) args[1];
455 GriffonController controller = group.getController();
456 if (controller == null) return;
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 = (MVCGroupManagerFactory) safeNewInstance(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.Entry) o;
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 == null) return;
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 = (String) args[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 = (ArtifactHandler) ctor.newInstance(app);
537 } else {
538 handler = (ArtifactHandler) safeNewInstance(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 = (Script) safeNewInstance(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 = (LifecycleHandler) safeNewInstance(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 ((ExpandoMetaClass) mc).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 className) throws 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 className) throws 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 != null) throw 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 (logException) handleThrowable(e);
742 return null;
743 }
744 }
745 }
|