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