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 = (Class) args[0];
250 if (GriffonController.class.isAssignableFrom(klass)) {
251 application.getActionManager().createActions((GriffonController) args[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 classLoader) throws 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 }
|