| 
001 /*002  * Copyright 2008-2015 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.core;
 017
 018 import griffon.core.ApplicationClassLoader;
 019 import griffon.core.ApplicationConfigurer;
 020 import griffon.core.ApplicationEvent;
 021 import griffon.core.CallableWithArgs;
 022 import griffon.core.GriffonApplication;
 023 import griffon.core.LifecycleHandler;
 024 import griffon.core.PlatformHandler;
 025 import griffon.core.artifact.ArtifactHandler;
 026 import griffon.core.artifact.ArtifactManager;
 027 import griffon.core.artifact.GriffonController;
 028 import griffon.core.controller.ActionHandler;
 029 import griffon.core.controller.ActionInterceptor;
 030 import griffon.core.env.Lifecycle;
 031 import griffon.core.event.EventHandler;
 032 import griffon.core.injection.Injector;
 033 import griffon.core.mvc.MVCGroupConfiguration;
 034 import griffon.core.resources.ResourceInjector;
 035 import griffon.util.ServiceLoaderUtils;
 036 import org.codehaus.griffon.runtime.core.controller.NoopActionManager;
 037 import org.slf4j.Logger;
 038 import org.slf4j.LoggerFactory;
 039
 040 import javax.annotation.Nonnull;
 041 import javax.annotation.Nullable;
 042 import javax.annotation.concurrent.GuardedBy;
 043 import javax.inject.Inject;
 044 import java.beans.PropertyEditor;
 045 import java.beans.PropertyEditorManager;
 046 import java.util.Collection;
 047 import java.util.Collections;
 048 import java.util.LinkedHashMap;
 049 import java.util.List;
 050 import java.util.Map;
 051
 052 import static griffon.core.GriffonExceptionHandler.sanitize;
 053 import static griffon.util.AnnotationUtils.named;
 054 import static griffon.util.AnnotationUtils.sortByDependencies;
 055 import static java.util.Arrays.asList;
 056 import static java.util.Objects.requireNonNull;
 057
 058 /**
 059  * Utility class for bootstrapping an application.
 060  *
 061  * @author Danno Ferrin
 062  * @author Andres Almiray
 063  */
 064 public class DefaultApplicationConfigurer implements ApplicationConfigurer {
 065     private static final Logger LOG = LoggerFactory.getLogger(DefaultApplicationConfigurer.class);
 066
 067     private static final String ERROR_APPLICATION_NULL = "Argument 'application' must not be null";
 068     private static final String KEY_APP_LIFECYCLE_HANDLER_DISABLE = "application.lifecycle.handler.disable";
 069     private static final String KEY_GRIFFON_CONTROLLER_ACTION_HANDLER_ORDER = "griffon.controller.action.handler.order";
 070
 071     private final Object lock = new Object();
 072     @GuardedBy("lock")
 073     private boolean initialized;
 074
 075     private final GriffonApplication application;
 076
 077     @Inject
 078     public DefaultApplicationConfigurer(@Nonnull GriffonApplication application) {
 079         this.application = requireNonNull(application, ERROR_APPLICATION_NULL);
 080     }
 081
 082     @Override
 083     public final void init() {
 084         synchronized (lock) {
 085             if (!initialized) {
 086                 doInitialize();
 087                 initialized = true;
 088             }
 089         }
 090     }
 091
 092     @Override
 093     public void runLifecycleHandler(@Nonnull Lifecycle lifecycle) {
 094         requireNonNull(lifecycle, "Argument 'lifecycle' must not be null");
 095
 096         boolean skipHandler = application.getConfiguration().getAsBoolean(KEY_APP_LIFECYCLE_HANDLER_DISABLE, false);
 097         if (skipHandler) {
 098             LOG.info("Lifecycle handler '{}' has been disabled. SKIPPING.", lifecycle.getName());
 099             return;
 100         }
 101
 102         LifecycleHandler handler;
 103         try {
 104             handler = application.getInjector().getInstance(LifecycleHandler.class, named(lifecycle.getName()));
 105         } catch (Exception e) {
 106             // the script must not exist, do nothing
 107             //LOGME - may be because of chained failures
 108             return;
 109         }
 110
 111         handler.execute();
 112     }
 113
 114     protected void doInitialize() {
 115         initializeEventHandler();
 116
 117         event(ApplicationEvent.BOOTSTRAP_START, asList(application));
 118
 119         initializePropertyEditors();
 120         initializeResourcesInjector();
 121         runLifecycleHandler(Lifecycle.INITIALIZE);
 122         applyPlatformTweaks();
 123         initializeAddonManager();
 124         initializeMvcManager();
 125         initializeActionManager();
 126         initializeArtifactManager();
 127
 128         event(ApplicationEvent.BOOTSTRAP_END, asList(application));
 129     }
 130
 131     protected void initializeEventHandler() {
 132         EventHandler eventHandler = application.getInjector().getInstance(EventHandler.class);
 133         application.getEventRouter().addEventListener(eventHandler);
 134     }
 135
 136     protected void event(@Nonnull ApplicationEvent event, @Nullable List<?> args) {
 137         application.getEventRouter().publishEvent(event.getName(), args);
 138     }
 139
 140     protected void initializePropertyEditors() {
 141         ServiceLoaderUtils.load(applicationClassLoader().get(), "META-INF/editors/", PropertyEditor.class, new ServiceLoaderUtils.LineProcessor() {
 142             @Override
 143             public void process(@Nonnull ClassLoader classLoader, @Nonnull Class<?> type, @Nonnull String line) {
 144                 try {
 145                     String[] parts = line.trim().split("=");
 146                     Class<?> targetType = loadClass(parts[0].trim(), classLoader);
 147                     Class<?> editorClass = loadClass(parts[1].trim(), classLoader);
 148
 149                     // Editor must have a no-args constructor
 150                     // CCE means the class can not be used
 151                     editorClass.newInstance();
 152                     PropertyEditorManager.registerEditor(targetType, editorClass);
 153                     LOG.debug("Registering {} as editor for {}", editorClass.getName(), targetType.getName());
 154                 } catch (Exception e) {
 155                     if (LOG.isWarnEnabled()) {
 156                         LOG.warn("Could not load " + type.getName() + " with " + line, sanitize(e));
 157                     }
 158                 }
 159             }
 160         });
 161
 162         Class<?>[][] pairs = new Class<?>[][]{
 163             new Class<?>[]{Boolean.class, Boolean.TYPE},
 164             new Class<?>[]{Byte.class, Byte.TYPE},
 165             new Class<?>[]{Short.class, Short.TYPE},
 166             new Class<?>[]{Integer.class, Integer.TYPE},
 167             new Class<?>[]{Long.class, Long.TYPE},
 168             new Class<?>[]{Float.class, Float.TYPE},
 169             new Class<?>[]{Double.class, Double.TYPE}
 170         };
 171
 172         for (Class<?>[] pair : pairs) {
 173             PropertyEditor editor = PropertyEditorManager.findEditor(pair[0]);
 174             LOG.debug("Registering {} as editor for {}", editor.getClass().getName(), pair[1].getName());
 175             PropertyEditorManager.registerEditor(pair[1], editor.getClass());
 176         }
 177     }
 178
 179     protected void initializeResourcesInjector() {
 180         final ResourceInjector injector = application.getResourceInjector();
 181         application.getEventRouter().addEventListener(ApplicationEvent.NEW_INSTANCE.getName(), new CallableWithArgs<Void>() {
 182             public Void call(@Nonnull Object... args) {
 183                 Object instance = args[1];
 184                 injector.injectResources(instance);
 185                 return null;
 186             }
 187         });
 188     }
 189
 190     protected void initializeArtifactManager() {
 191         Injector<?> injector = application.getInjector();
 192         ArtifactManager artifactManager = application.getArtifactManager();
 193         for (ArtifactHandler<?> artifactHandler : injector.getInstances(ArtifactHandler.class)) {
 194             artifactManager.registerArtifactHandler(artifactHandler);
 195         }
 196         artifactManager.loadArtifactMetadata(injector);
 197         application.addShutdownHandler(artifactManager);
 198     }
 199
 200     protected void applyPlatformTweaks() {
 201         PlatformHandler platformHandler = application.getInjector().getInstance(PlatformHandler.class);
 202         platformHandler.handle(application);
 203     }
 204
 205     protected void initializeAddonManager() {
 206         application.getAddonManager().initialize();
 207     }
 208
 209     @SuppressWarnings("unchecked")
 210     protected void initializeMvcManager() {
 211         Map<String, MVCGroupConfiguration> configurations = new LinkedHashMap<>();
 212         Map<String, Map<String, Object>> mvcGroups = application.getConfiguration().get("mvcGroups", Collections.<String, Map<String, Object>>emptyMap());
 213         if (mvcGroups != null) {
 214             for (Map.Entry<String, Map<String, Object>> groupEntry : mvcGroups.entrySet()) {
 215                 String type = groupEntry.getKey();
 216                 LOG.debug("Adding MVC group {}", type);
 217                 Map<String, Object> members = groupEntry.getValue();
 218                 Map<String, Object> configMap = new LinkedHashMap<>();
 219                 Map<String, String> membersCopy = new LinkedHashMap<>();
 220                 for (Map.Entry<String, Object> entry : members.entrySet()) {
 221                     String key = String.valueOf(entry.getKey());
 222                     if ("config".equals(key) && entry.getValue() instanceof Map) {
 223                         configMap = (Map<String, Object>) entry.getValue();
 224                     } else {
 225                         membersCopy.put(key, String.valueOf(entry.getValue()));
 226                     }
 227                 }
 228                 configurations.put(type, application.getMvcGroupManager().newMVCGroupConfiguration(type, membersCopy, configMap));
 229             }
 230         }
 231
 232         application.getMvcGroupManager().initialize(configurations);
 233     }
 234
 235     protected void initializeActionManager() {
 236         if (application.getActionManager() instanceof NoopActionManager) {
 237             return;
 238         }
 239
 240         application.getEventRouter().addEventListener(ApplicationEvent.NEW_INSTANCE.getName(), new CallableWithArgs<Void>() {
 241             @Nullable
 242             public Void call(@Nullable Object... args) {
 243                 Class<?> klass = (Class) args[0];
 244                 if (GriffonController.class.isAssignableFrom(klass)) {
 245                     GriffonController controller = (GriffonController) args[1];
 246                     application.getActionManager().createActions(controller);
 247                 }
 248                 return null;
 249             }
 250         });
 251
 252         Injector<?> injector = application.getInjector();
 253         Collection<ActionHandler> handlerInstances = injector.getInstances(ActionHandler.class);
 254         List<String> handlerOrder = application.getConfiguration().get(KEY_GRIFFON_CONTROLLER_ACTION_HANDLER_ORDER, Collections.<String>emptyList());
 255         Map<String, ActionHandler> sortedHandlers = sortByDependencies(handlerInstances, ActionHandler.SUFFIX, "handler", handlerOrder);
 256
 257         for (ActionHandler handler : sortedHandlers.values()) {
 258             application.getActionManager().addActionHandler(handler);
 259         }
 260
 261         Collection<ActionInterceptor> interceptorInstances = injector.getInstances(ActionInterceptor.class);
 262         if (!interceptorInstances.isEmpty()) {
 263             application.getLog().error(ActionInterceptor.class.getName() + " have been deprecated and is no longer supported");
 264             throw new UnsupportedOperationException(ActionInterceptor.class.getName() + " have been deprecated and is no longer supported");
 265         }
 266     }
 267
 268     protected Class<?> loadClass(@Nonnull String className, @Nonnull ClassLoader classLoader) throws ClassNotFoundException {
 269         ClassNotFoundException cnfe;
 270
 271         ClassLoader cl = DefaultApplicationConfigurer.class.getClassLoader();
 272         try {
 273             return cl.loadClass(className);
 274         } catch (ClassNotFoundException e) {
 275             cnfe = e;
 276         }
 277
 278         cl = classLoader;
 279         try {
 280             return cl.loadClass(className);
 281         } catch (ClassNotFoundException e) {
 282             cnfe = e;
 283         }
 284
 285         throw cnfe;
 286     }
 287
 288     private ApplicationClassLoader applicationClassLoader() {
 289         return application.getInjector().getInstance(ApplicationClassLoader.class);
 290     }
 291 }
 |