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