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