`GriffonApplicationHelper.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 package org.codehaus.griffon.runtime.util; 017 018 import griffon.core.*; 019 import griffon.core.factories.AddonManagerFactory; 020 import griffon.core.factories.ArtifactManagerFactory; 021 import griffon.core.factories.MVCGroupManagerFactory; 022 import griffon.exceptions.GriffonException; 023 import griffon.util.*; 024 import groovy.lang.*; 025 import groovy.util.ConfigObject; 026 import groovy.util.ConfigSlurper; 027 import org.apache.log4j.LogManager; 028 import org.apache.log4j.helpers.LogLog; 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.logging.Log4jConfig; 034 import org.codehaus.groovy.runtime.InvokerHelper; 035 import org.slf4j.Logger; 036 import org.slf4j.LoggerFactory; 037 038 import java.io.IOException; 039 import java.io.InputStream; 040 import java.lang.reflect.Constructor; 041 import java.net.URL; 042 import java.util.Enumeration; 043 import java.util.LinkedHashMap; 044 import java.util.Map; 045 import java.util.Properties; 046 047 import static griffon.util.ConfigUtils.getConfigValueAsString; 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.DefaultGroovyMethods.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("macosx", "org.codehaus.griffon.runtime.util.DefaultMacOSXPlatformHandler") 065 .e("solaris", "org.codehaus.griffon.runtime.util.DefaultSolarisPlatformHandler") 066 .e("windows", "org.codehaus.griffon.runtime.util.DefaultWindowsPlatformHandler"); 067 068 static { 069 ExpandoMetaClassCreationHandle.enable(); 070 } 071 072 /** 073 * Creates, register and assigns an ExpandoMetaClass for a target class.<p> 074 * The newly created metaClass will accept changes after initialization. 075 * 076 * @param clazz the target class 077 * @return an ExpandoMetaClass 078 */ 079 public static MetaClass expandoMetaClassFor(Class clazz) { 080 MetaClass mc = GroovySystem.getMetaClassRegistry().getMetaClass(clazz); 081 if (!(mc instanceof ExpandoMetaClass)) { 082 mc = new ExpandoMetaClass(clazz, true, true); 083 mc.initialize(); 084 GroovySystem.getMetaClassRegistry().setMetaClass(clazz, mc); 085 } 086 return mc; 087 } 088 089 private static ConfigObject loadConfig(ConfigSlurper configSlurper, Class configClass, String configFileName) { 090 ConfigObject config = new ConfigObject(); 091 try { 092 if (configClass != null) { 093 config.merge(configSlurper.parse(configClass)); 094 } 095 InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(configFileName + ".properties"); 096 if (is != null) { 097 Properties p = new Properties(); 098 p.load(is); 099 config.merge(configSlurper.parse(p)); 100 } 101 } catch (Exception x) { 102 LogLog.warn("Cannot read configuration [class: " + configClass + ", file: " + configFileName + "]", sanitize(x)); 103 } 104 return config; 105 } 106 107 /** 108 * Setups an application.<p> 109 * This method performs the following tasks<ul> 110 * <li>Sets "griffon.start.dir" as system property.</li> 111 * <li>Calls the Initialize life cycle script.</li> 112 * <li>Reads runtime and builder configuration.</li> 113 * <li>Setups basic artifact handlers.</li> 114 * <li>Initializes available addons.</li> 115 * </ul> 116 * 117 * @param app the current Griffon application 118 */ 119 public static void prepare(GriffonApplication app) { 120 app.getBindings().setVariable("app", app); 121 122 Metadata.getCurrent().getGriffonStartDir(); 123 Metadata.getCurrent().getGriffonWorkingDir(); 124 125 readAndSetConfiguration(app); 126 app.event(GriffonApplication.Event.BOOTSTRAP_START.getName(), asList(app)); 127 128 applyPlatformTweaks(app); 129 runLifecycleHandler(GriffonApplication.Lifecycle.INITIALIZE.getName(), app); 130 initializeArtifactManager(app); 131 initializeMvcManager(app); 132 initializeAddonManager(app); 133 134 app.event(GriffonApplication.Event.BOOTSTRAP_END.getName(), asList(app)); 135 } 136 137 private static void readAndSetConfiguration(GriffonApplication app) { 138 ConfigSlurper configSlurper = new ConfigSlurper(Environment.getCurrent().getName()); 139 app.setConfig(loadConfig(configSlurper, app.getAppConfigClass(), GriffonApplication.Configuration.APPLICATION.getName())); 140 app.getConfig().merge(loadConfig(configSlurper, app.getConfigClass(), GriffonApplication.Configuration.CONFIG.getName())); 141 GriffonExceptionHandler.configure(app.getConfig().flatten(new LinkedHashMap())); 142 143 app.setBuilderConfig(loadConfig(configSlurper, app.getBuilderClass(), GriffonApplication.Configuration.BUILDER.getName())); 144 145 Object events = safeNewInstance(app.getEventsClass()); 146 if (events != null) { 147 app.setEventsConfig(events); 148 app.addApplicationEventListener(app.getEventsConfig()); 149 } 150 151 Object log4jConfig = app.getConfig().get("log4j"); 152 if (log4jConfig instanceof Closure) { 153 app.event(GriffonApplication.Event.LOG4J_CONFIG_START.getName(), asList(log4jConfig)); 154 LogManager.resetConfiguration(); 155 new Log4jConfig().configure((Closure) log4jConfig); 156 } 157 } 158 159 public static void applyPlatformTweaks(GriffonApplication app) { 160 String platform = GriffonApplicationUtils.platform; 161 String handlerClassName = getConfigValueAsString(app.getConfig(), "platform.handler." + platform, DEFAULT_PLATFORM_HANDLERS.get(platform)); 162 PlatformHandler platformHandler = (PlatformHandler) safeNewInstance(handlerClassName); 163 platformHandler.handle(app); 164 } 165 166 private static final String KEY_ARTIFACT_MANAGER_FACTORY = "app.artifactManager.factory"; 167 private static final String DEFAULT_ARTIFACT_MANAGER_FACTORY = "org.codehaus.griffon.runtime.core.factories.DefaultArtifactManagerFactory"; 168 169 private static void initializeArtifactManager(GriffonApplication app) { 170 if (app.getArtifactManager() == null) { 171 String className = getConfigValueAsString(app.getConfig(), KEY_ARTIFACT_MANAGER_FACTORY, DEFAULT_ARTIFACT_MANAGER_FACTORY); 172 if (LOG.isDebugEnabled()) { 173 LOG.debug("Using " + className + " as ArtifactManagerFactory"); 174 } 175 ArtifactManagerFactory factory = (ArtifactManagerFactory) safeNewInstance(className); 176 InvokerHelper.setProperty(app, "artifactManager", factory.create(app)); 177 } 178 179 // initialize default Artifact handlers 180 app.getArtifactManager().registerArtifactHandler(new ModelArtifactHandler(app)); 181 app.getArtifactManager().registerArtifactHandler(new ViewArtifactHandler(app)); 182 app.getArtifactManager().registerArtifactHandler(new ControllerArtifactHandler(app)); 183 app.getArtifactManager().registerArtifactHandler(new ServiceArtifactHandler(app)); 184 185 // load additional handlers 186 loadArtifactHandlers(app); 187 188 app.getArtifactManager().loadArtifactMetadata(); 189 } 190 191 private static final String KEY_ADDON_MANAGER_FACTORY = "app.addonManager.factory"; 192 private static final String DEFAULT_ADDON_MANAGER_FACTORY = "org.codehaus.griffon.runtime.core.factories.DefaultAddonManagerFactory"; 193 194 private static void initializeAddonManager(GriffonApplication app) { 195 if (app.getAddonManager() == null) { 196 String className = getConfigValueAsString(app.getConfig(), KEY_ADDON_MANAGER_FACTORY, DEFAULT_ADDON_MANAGER_FACTORY); 197 if (LOG.isDebugEnabled()) { 198 LOG.debug("Using " + className + " as AddonManagerFactory"); 199 } 200 AddonManagerFactory factory = (AddonManagerFactory) safeNewInstance(className); 201 InvokerHelper.setProperty(app, "addonManager", factory.create(app)); 202 } 203 app.getAddonManager().initialize(); 204 } 205 206 private static final String KEY_MVCGROUP_MANAGER_FACTORY = "app.mvcGroupManager.factory"; 207 private static final String DEFAULT_MVCGROUP_MANAGER_FACTORY = "org.codehaus.griffon.runtime.core.factories.DefaultMVCGroupManagerFactory"; 208 209 private static void initializeMvcManager(GriffonApplication app) { 210 if (app.getMvcGroupManager() == null) { 211 String className = getConfigValueAsString(app.getConfig(), KEY_MVCGROUP_MANAGER_FACTORY, DEFAULT_MVCGROUP_MANAGER_FACTORY); 212 if (LOG.isDebugEnabled()) { 213 LOG.debug("Using " + className + " as MVCGroupManagerFactory"); 214 } 215 MVCGroupManagerFactory factory = (MVCGroupManagerFactory) safeNewInstance(className); 216 InvokerHelper.setProperty(app, "mvcGroupManager", factory.create(app)); 217 } 218 219 Map<String, MVCGroupConfiguration> configurations = new LinkedHashMap<String, MVCGroupConfiguration>(); 220 Map<String, ConfigObject> mvcGroups = (Map<String, ConfigObject>) app.getConfig().get("mvcGroups"); 221 if (mvcGroups != null) { 222 for (Map.Entry<String, ConfigObject> groupEntry : mvcGroups.entrySet()) { 223 String type = groupEntry.getKey(); 224 if (LOG.isDebugEnabled()) { 225 LOG.debug("Adding MVC group " + type); 226 } 227 ConfigObject members = groupEntry.getValue(); 228 Map<String, Object> configMap = new LinkedHashMap<String, Object>(); 229 Map<String, String> membersCopy = new LinkedHashMap<String, String>(); 230 for (Object o : members.entrySet()) { 231 Map.Entry entry = (Map.Entry) o; 232 String key = String.valueOf(entry.getKey()); 233 if ("config".equals(key) && entry.getValue() instanceof Map) { 234 configMap = (Map<String, Object>) entry.getValue(); 235 } else { 236 membersCopy.put(key, String.valueOf(entry.getValue())); 237 } 238 } 239 configurations.put(type, app.getMvcGroupManager().newMVCGroupConfiguration(type, membersCopy, configMap)); 240 } 241 } 242 243 app.getMvcGroupManager().initialize(configurations); 244 } 245 246 private static void loadArtifactHandlers(final GriffonApplication app) { 247 Enumeration<URL> urls = null; 248 249 try { 250 urls = app.getClass().getClassLoader().getResources("META-INF/services/" + ArtifactHandler.class.getName()); 251 } catch (IOException ioe) { 252 return; 253 } 254 255 if (urls == null) return; 256 257 while (urls.hasMoreElements()) { 258 URL url = urls.nextElement(); 259 if (LOG.isDebugEnabled()) { 260 LOG.debug("Reading " + ArtifactHandler.class.getName() + " definitions from " + url); 261 } 262 263 try { 264 eachLine(url, new RunnableWithArgsClosure(new RunnableWithArgs() { 265 @Override 266 public void run(Object[] args) { 267 String line = (String) args[0]; 268 if (line.startsWith("#") || isBlank(line)) return; 269 try { 270 Class artifactHandlerClass = loadClass(line); 271 Constructor ctor = artifactHandlerClass.getDeclaredConstructor(GriffonApplication.class); 272 ArtifactHandler handler = null; 273 if (ctor != null) { 274 handler = (ArtifactHandler) ctor.newInstance(app); 275 } else { 276 handler = (ArtifactHandler) safeNewInstance(artifactHandlerClass); 277 } 278 app.getArtifactManager().registerArtifactHandler(handler); 279 } catch (Exception e) { 280 if (LOG.isWarnEnabled()) { 281 LOG.warn("Could not load ArtifactHandler with " + line, sanitize(e)); 282 } 283 } 284 } 285 })); 286 } catch (IOException e) { 287 if (LOG.isWarnEnabled()) { 288 LOG.warn("Could not load ArtifactHandler from " + url, sanitize(e)); 289 } 290 } 291 } 292 } 293 294 /** 295 * Executes a script inside the UI Thread.<p> 296 * On Swing this would be the Event Dispatch Thread. 297 * 298 * @deprecated use runLifecycleHandler instead 299 */ 300 @Deprecated 301 public static void runScriptInsideUIThread(String scriptName, GriffonApplication app) { 302 runLifecycleHandler(scriptName, app); 303 } 304 305 /** 306 * Executes a script inside the UI Thread.<p> 307 * On Swing this would be the Event Dispatch Thread. 308 */ 309 public static void runLifecycleHandler(String handlerName, GriffonApplication app) { 310 Class<?> handlerClass = null; 311 try { 312 handlerClass = loadClass(handlerName); 313 } catch (ClassNotFoundException cnfe) { 314 if (cnfe.getMessage().equals(handlerName)) { 315 // the script must not exist, do nothing 316 //LOGME - may be because of chained failures 317 return; 318 } else { 319 throw new GriffonException(cnfe); 320 } 321 } 322 323 if (Script.class.isAssignableFrom(handlerClass)) { 324 doRunScript(handlerName, handlerClass, app); 325 } else if (LifecycleHandler.class.isAssignableFrom(handlerClass)) { 326 doRunLifecycleHandler(handlerName, handlerClass, app); 327 } 328 } 329 330 private static void doRunScript(String scriptName, Class handlerClass, GriffonApplication app) { 331 Script script = (Script) safeNewInstance(handlerClass); 332 script.setBinding(app.getBindings()); 333 UIThreadManager.enhance(script); 334 if (LOG.isInfoEnabled()) { 335 LOG.info("Running lifecycle handler (script) '" + scriptName + "'"); 336 } 337 UIThreadManager.getInstance().executeSync(script); 338 } 339 340 private static void doRunLifecycleHandler(String handlerName, Class handlerClass, GriffonApplication app) { 341 LifecycleHandler handler = (LifecycleHandler) safeNewInstance(handlerClass); 342 if (LOG.isInfoEnabled()) { 343 LOG.info("Running lifecycle handler (class) '" + handlerName + "'"); 344 } 345 UIThreadManager.getInstance().executeSync(handler); 346 } 347 348 /** 349 * Creates a new instance of the specified class.<p> 350 * Publishes a <strong>NewInstance</strong> event with the following arguments<ul> 351 * <li>klass - the target Class</li> 352 * <li>type - the type of the instance (i.e, 'controller','service')</li> 353 * <li>instance - the newly created instance</li> 354 * </ul> 355 * 356 * @param app the current GriffonApplication 357 * @param klass the target Class from which the instance will be created 358 * @return a newly created instance of type klass 359 */ 360 public static Object newInstance(GriffonApplication app, Class klass) { 361 return newInstance(app, klass, ""); 362 } 363 364 /** 365 * Creates a new instance of the specified class.<p> 366 * Publishes a <strong>NewInstance</strong> event with the following arguments<ul> 367 * <li>klass - the target Class</li> 368 * <li>type - the type of the instance (i.e, 'controller','service')</li> 369 * <li>instance - the newly created instance</li> 370 * </ul> 371 * 372 * @param app the current GriffonApplication 373 * @param klass the target Class from which the instance will be created 374 * @param type optional type parameter, used when publishing a 'NewInstance' event 375 * @return a newly created instance of type klass 376 */ 377 public static Object newInstance(GriffonApplication app, Class klass, String type) { 378 if (isBlank(type)) type = ""; 379 if (LOG.isDebugEnabled()) { 380 LOG.debug("Instantiating " + klass.getName() + " with type '" + type + "'"); 381 } 382 383 Object instance = safeNewInstance(klass); 384 385 GriffonClass griffonClass = app.getArtifactManager().findGriffonClass(klass); 386 MetaClass mc = griffonClass != null ? griffonClass.getMetaClass() : expandoMetaClassFor(klass); 387 enhance(app, klass, mc, instance); 388 389 app.event(GriffonApplication.Event.NEW_INSTANCE.getName(), asList(klass, type, instance)); 390 return instance; 391 } 392 393 public static void enhance(GriffonApplication app, Class klass, MetaClass mc, Object instance) { 394 try { 395 InvokerHelper.invokeMethod(instance, "setApp", app); 396 } catch (MissingMethodException mme) { 397 try { 398 InvokerHelper.setProperty(instance, "app", app); 399 } catch (MissingPropertyException mpe) { 400 if (mc instanceof ExpandoMetaClass) { 401 ((ExpandoMetaClass) mc).registerBeanProperty("app", app); 402 } 403 } 404 } 405 406 if (!GriffonArtifact.class.isAssignableFrom(klass)) { 407 /* 408 mc.createMVCGroup = {Object...args - > 409 app.mvcGroupManager.createMVCGroup( * args) 410 } 411 mc.buildMVCGroup = {Object...args - > 412 app.mvcGroupManager.buildMVCGroup( * args) 413 } 414 mc.destroyMVCGroup = {String mvcName - > 415 app.mvcGroupManager.destroyMVCGroup(mvcName) 416 } 417 mc.withMVCGroup = {Object...args - > 418 app.mvcGroupManager.withMVCGroup( * args) 419 } 420 mc.newInstance = {Object...args - > 421 GriffonApplicationHelper.newInstance(app, * args) 422 } 423 */ 424 UIThreadManager.enhance(mc); 425 } 426 } 427 428 public static Class loadClass(String className) throws ClassNotFoundException { 429 ClassNotFoundException cnfe = null; 430 431 ClassLoader cl = GriffonApplicationHelper.class.getClassLoader(); 432 try { 433 return cl.loadClass(className); 434 } catch (ClassNotFoundException e) { 435 cnfe = e; 436 } 437 438 cl = Thread.currentThread().getContextClassLoader(); 439 try { 440 return cl.loadClass(className); 441 } catch (ClassNotFoundException e) { 442 cnfe = e; 443 } 444 445 if (cnfe != null) throw cnfe; 446 return null; 447 } 448 449 private static Object safeNewInstance(String className) { 450 try { 451 return loadClass(className).newInstance(); 452 } catch (Exception e) { 453 return null; 454 } 455 } 456 457 private static Object safeNewInstance(Class clazz) { 458 try { 459 return clazz.newInstance(); 460 } catch (Exception e) { 461 return null; 462 } 463 } 464 }`