/* * Copyright 2008-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.codehaus.griffon.runtime.core; import griffon.core.ApplicationClassLoader; import griffon.core.ApplicationConfigurer; import griffon.core.ApplicationEvent; import griffon.core.GriffonApplication; import griffon.core.LifecycleHandler; import griffon.core.PlatformHandler; import griffon.core.RunnableWithArgs; import griffon.core.artifact.ArtifactHandler; import griffon.core.artifact.ArtifactManager; import griffon.core.artifact.GriffonController; import griffon.core.controller.ActionHandler; import griffon.core.controller.ActionInterceptor; import griffon.core.editors.PropertyEditorResolver; import griffon.core.env.Lifecycle; import griffon.core.event.EventHandler; import griffon.core.injection.Injector; import griffon.core.mvc.MVCGroupConfiguration; import griffon.core.resources.ResourceInjector; import griffon.util.ServiceLoaderUtils; import org.codehaus.griffon.runtime.core.controller.NoopActionManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; import javax.inject.Inject; import java.beans.PropertyEditor; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static griffon.core.GriffonExceptionHandler.sanitize; import static griffon.util.AnnotationUtils.named; import static griffon.util.AnnotationUtils.sortByDependencies; import static java.util.Arrays.asList; import static java.util.Objects.requireNonNull; /** * Utility class for bootstrapping an application. * * @author Danno Ferrin * @author Andres Almiray */ public class DefaultApplicationConfigurer implements ApplicationConfigurer { private static final Logger LOG = LoggerFactory.getLogger(DefaultApplicationConfigurer.class); private static final String ERROR_APPLICATION_NULL = "Argument 'application' must not be null"; private static final String KEY_APP_LIFECYCLE_HANDLER_DISABLE = "application.lifecycle.handler.disable"; private static final String KEY_GRIFFON_CONTROLLER_ACTION_HANDLER_ORDER = "griffon.controller.action.handler.order"; private final Object lock = new Object(); private final GriffonApplication application; @GuardedBy("lock") private boolean initialized; @Inject public DefaultApplicationConfigurer(@Nonnull GriffonApplication application) { this.application = requireNonNull(application, ERROR_APPLICATION_NULL); } @Override public final void init() { synchronized (lock) { if (!initialized) { doInitialize(); initialized = true; } } } @Override public void runLifecycleHandler(@Nonnull Lifecycle lifecycle) { requireNonNull(lifecycle, "Argument 'lifecycle' must not be null"); boolean skipHandler = application.getConfiguration().getAsBoolean(KEY_APP_LIFECYCLE_HANDLER_DISABLE, false); if (skipHandler) { LOG.info("Lifecycle handler '{}' has been disabled. SKIPPING.", lifecycle.getName()); return; } LifecycleHandler handler; try { handler = application.getInjector().getInstance(LifecycleHandler.class, named(lifecycle.getName())); } catch (Exception e) { // the script must not exist, do nothing //LOGME - may be because of chained failures return; } handler.execute(); } protected void doInitialize() { initializeEventHandler(); event(ApplicationEvent.BOOTSTRAP_START, asList(application)); initializePropertyEditors(); initializeResourcesInjector(); runLifecycleHandler(Lifecycle.INITIALIZE); applyPlatformTweaks(); initializeAddonManager(); initializeMvcManager(); initializeActionManager(); initializeArtifactManager(); event(ApplicationEvent.BOOTSTRAP_END, asList(application)); } protected void initializeEventHandler() { Collection<EventHandler> handlerInstances = application.getInjector().getInstances(EventHandler.class); Map<String, EventHandler> sortedHandlers = sortByDependencies(handlerInstances, "EventHandler", "handler"); for (EventHandler handler : sortedHandlers.values()) { application.getEventRouter().addEventListener(handler); } } protected void event(@Nonnull ApplicationEvent event, @Nullable List<?> args) { application.getEventRouter().publishEvent(event.getName(), args); } protected void initializePropertyEditors() { ServiceLoaderUtils.load(applicationClassLoader().get(), "META-INF/editors/", PropertyEditor.class, new ServiceLoaderUtils.LineProcessor() { @Override @SuppressWarnings("unchecked") public void process(@Nonnull ClassLoader classLoader, @Nonnull Class<?> type, @Nonnull String line) { try { String[] parts = line.trim().split("="); Class<?> targetType = loadClass(parts[0].trim(), classLoader); Class<? extends PropertyEditor> editorClass = (Class<? extends PropertyEditor>) loadClass(parts[1].trim(), classLoader); // Editor must have a no-args constructor // CCE means the class can not be used editorClass.newInstance(); PropertyEditorResolver.registerEditor(targetType, editorClass); LOG.debug("Registering {} as editor for {}", editorClass.getName(), targetType.getName()); } catch (Exception e) { if (LOG.isWarnEnabled()) { LOG.warn("Could not load " + type.getName() + " with " + line, sanitize(e)); } } } }); Class<?>[][] pairs = new Class<?>[][]{ new Class<?>[]{Boolean.class, Boolean.TYPE}, new Class<?>[]{Byte.class, Byte.TYPE}, new Class<?>[]{Short.class, Short.TYPE}, new Class<?>[]{Integer.class, Integer.TYPE}, new Class<?>[]{Long.class, Long.TYPE}, new Class<?>[]{Float.class, Float.TYPE}, new Class<?>[]{Double.class, Double.TYPE} }; for (Class<?>[] pair : pairs) { PropertyEditor editor = PropertyEditorResolver.findEditor(pair[0]); LOG.debug("Registering {} as editor for {}", editor.getClass().getName(), pair[1].getName()); PropertyEditorResolver.registerEditor(pair[1], editor.getClass()); } } protected void initializeResourcesInjector() { final ResourceInjector injector = application.getResourceInjector(); application.getEventRouter().addEventListener(ApplicationEvent.NEW_INSTANCE.getName(), new RunnableWithArgs() { public void run(@Nullable Object... args) { injector.injectResources(args[1]); } }); } protected void initializeArtifactManager() { Injector<?> injector = application.getInjector(); ArtifactManager artifactManager = application.getArtifactManager(); for (ArtifactHandler<?> artifactHandler : injector.getInstances(ArtifactHandler.class)) { artifactManager.registerArtifactHandler(artifactHandler); } artifactManager.loadArtifactMetadata(); } protected void applyPlatformTweaks() { PlatformHandler platformHandler = application.getInjector().getInstance(PlatformHandler.class); platformHandler.handle(application); } protected void initializeAddonManager() { application.getAddonManager().initialize(); } @SuppressWarnings("unchecked") protected void initializeMvcManager() { Map<String, MVCGroupConfiguration> configurations = new LinkedHashMap<>(); Map<String, Map<String, Object>> mvcGroups = application.getConfiguration().get("mvcGroups", Collections.<String, Map<String, Object>>emptyMap()); if (mvcGroups != null) { for (Map.Entry<String, Map<String, Object>> groupEntry : mvcGroups.entrySet()) { String type = groupEntry.getKey(); LOG.debug("Adding MVC group {}", type); Map<String, Object> members = groupEntry.getValue(); Map<String, Object> configMap = new LinkedHashMap<>(); Map<String, String> membersCopy = new LinkedHashMap<>(); for (Map.Entry<String, Object> entry : members.entrySet()) { String key = String.valueOf(entry.getKey()); if ("config".equals(key) && entry.getValue() instanceof Map) { configMap = (Map<String, Object>) entry.getValue(); } else { membersCopy.put(key, String.valueOf(entry.getValue())); } } configurations.put(type, application.getMvcGroupManager().newMVCGroupConfiguration(type, membersCopy, configMap)); } } application.getMvcGroupManager().initialize(configurations); } protected void initializeActionManager() { if (application.getActionManager() instanceof NoopActionManager) { return; } application.getEventRouter().addEventListener(ApplicationEvent.NEW_INSTANCE.getName(), new RunnableWithArgs() { public void run(@Nullable Object... args) { Class<?> klass = (Class) args[0]; if (GriffonController.class.isAssignableFrom(klass)) { application.getActionManager().createActions((GriffonController) args[1]); } } }); Injector<?> injector = application.getInjector(); Collection<ActionHandler> handlerInstances = injector.getInstances(ActionHandler.class); List<String> handlerOrder = application.getConfiguration().get(KEY_GRIFFON_CONTROLLER_ACTION_HANDLER_ORDER, Collections.<String>emptyList()); Map<String, ActionHandler> sortedHandlers = sortByDependencies(handlerInstances, ActionHandler.SUFFIX, "handler", handlerOrder); for (ActionHandler handler : sortedHandlers.values()) { application.getActionManager().addActionHandler(handler); } Collection<ActionInterceptor> interceptorInstances = injector.getInstances(ActionInterceptor.class); if (!interceptorInstances.isEmpty()) { application.getLog().error(ActionInterceptor.class.getName() + " has been deprecated and is no longer supported"); throw new UnsupportedOperationException(ActionInterceptor.class.getName() + " has been deprecated and is no longer supported"); } } protected Class<?> loadClass(@Nonnull String className, @Nonnull ClassLoader classLoader) throws ClassNotFoundException { ClassNotFoundException cnfe; ClassLoader cl = DefaultApplicationConfigurer.class.getClassLoader(); try { return cl.loadClass(className); } catch (ClassNotFoundException e) { cnfe = e; } cl = classLoader; try { return cl.loadClass(className); } catch (ClassNotFoundException e) { cnfe = e; } throw cnfe; } private ApplicationClassLoader applicationClassLoader() { return application.getInjector().getInstance(ApplicationClassLoader.class); } }