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 = (Class) args[0];
233 if (GriffonController.class.isAssignableFrom(klass)) {
234 GriffonController controller = (GriffonController) args[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 classLoader) throws 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 }
|