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