AbstractApplicationBootstrapper.java
001 /*
002  * Copyright 2008-2015 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.ApplicationBootstrapper;
019 import griffon.core.GriffonApplication;
020 import griffon.core.artifact.GriffonService;
021 import griffon.core.env.GriffonEnvironment;
022 import griffon.core.injection.Binding;
023 import griffon.core.injection.Injector;
024 import griffon.core.injection.InjectorFactory;
025 import griffon.core.injection.Module;
026 import griffon.util.GriffonClassUtils;
027 import griffon.util.ServiceLoaderUtils;
028 import org.codehaus.griffon.runtime.core.injection.AbstractModule;
029 import org.slf4j.Logger;
030 import org.slf4j.LoggerFactory;
031 
032 import javax.annotation.Nonnull;
033 import java.util.ArrayList;
034 import java.util.Collection;
035 import java.util.Iterator;
036 import java.util.LinkedHashMap;
037 import java.util.List;
038 import java.util.Map;
039 import java.util.ServiceLoader;
040 
041 import static griffon.core.GriffonExceptionHandler.sanitize;
042 import static griffon.util.AnnotationUtils.sortByDependencies;
043 import static griffon.util.ServiceLoaderUtils.load;
044 import static java.util.Collections.unmodifiableCollection;
045 import static java.util.Objects.requireNonNull;
046 
047 /**
048  @author Andres Almiray
049  @since 2.0.0
050  */
051 public abstract class AbstractApplicationBootstrapper implements ApplicationBootstrapper {
052     private static final Logger LOG = LoggerFactory.getLogger(DefaultApplicationBootstrapper.class);
053     private static final String INJECTOR = "injector";
054     private static final String GRIFFON_PATH = "META-INF/griffon";
055     private static final String PROPERTIES = ".properties";
056     protected final GriffonApplication application;
057 
058     public AbstractApplicationBootstrapper(@Nonnull GriffonApplication application) {
059         this.application = requireNonNull(application, "Argument 'application' must not be null");
060     }
061 
062     @Override
063     public void bootstrap() throws Exception {
064         // 1 initialize environment settings
065         LOG.info("Griffon {}", GriffonEnvironment.getGriffonVersion());
066         LOG.info("Build: {}", GriffonEnvironment.getBuildDateTime());
067         LOG.info("Revision: {}", GriffonEnvironment.getBuildRevision());
068         LOG.info("JVM: {}", GriffonEnvironment.getJvmVersion());
069         LOG.info("OS: {}", GriffonEnvironment.getOsVersion());
070 
071         // 2 create bindings
072         LOG.debug("Creating module bindings");
073         Iterable<Binding<?>> bindings = createBindings();
074 
075         if (LOG.isTraceEnabled()) {
076             for (Binding<?> binding : bindings) {
077                 LOG.trace(binding.toString());
078             }
079         }
080 
081         // 3 create injector
082         LOG.debug("Creating application injector");
083         createInjector(bindings);
084     }
085 
086     @Override
087     public void run() {
088         application.initialize();
089         application.startup();
090         application.ready();
091     }
092 
093     @Nonnull
094     protected Iterable<Binding<?>> createBindings() {
095         Map<Key, Binding<?>> map = new LinkedHashMap<>();
096 
097         List<Module> modules = new ArrayList<>();
098         createApplicationModule(modules);
099         createArtifactsModule(modules);
100         collectModuleBindings(modules);
101 
102         for (Module module : modules) {
103             for (Binding<?> binding : module.getBindings()) {
104                 map.put(Key.of(binding), binding);
105             }
106         }
107 
108         return unmodifiableCollection(map.values());
109     }
110 
111     protected void createArtifactsModule(@Nonnull List<Module> modules) {
112         final List<Class<?>> classes = new ArrayList<>();
113         load(getClass().getClassLoader(), GRIFFON_PATH, new ServiceLoaderUtils.PathFilter() {
114             @Override
115             public boolean accept(@Nonnull String path) {
116                 return !path.endsWith(PROPERTIES);
117             }
118         }new ServiceLoaderUtils.ResourceProcessor() {
119             @Override
120             public void process(@Nonnull ClassLoader classLoader, @Nonnull String line) {
121                 line = line.trim();
122                 try {
123                     classes.add(classLoader.loadClass(line));
124                 catch (ClassNotFoundException e) {
125                     LOG.warn("'" + line + "' could not be resolved as a Class");
126                 }
127             }
128         });
129 
130         modules.add(new AbstractModule() {
131             @Override
132             protected void doConfigure() {
133                 for (Class<?> clazz : classes) {
134                     if (GriffonService.class.isAssignableFrom(clazz)) {
135                         bind(clazz).asSingleton();
136                     else {
137                         bind(clazz);
138                     }
139                 }
140             }
141         });
142     }
143 
144     protected void createApplicationModule(@Nonnull List<Module> modules) {
145         modules.add(new AbstractModule() {
146             @Override
147             protected void doConfigure() {
148                 bind(GriffonApplication.class)
149                     .toInstance(application);
150             }
151         });
152     }
153 
154     protected void collectModuleBindings(@Nonnull Collection<Module> modules) {
155         List<Module> moduleInstances = loadModules();
156         moduleInstances.add(0new DefaultApplicationModule());
157         Map<String, Module> sortedModules = sortModules(moduleInstances);
158         for (Map.Entry<String, Module> entry : sortedModules.entrySet()) {
159             LOG.debug("Loading module bindings from {}:{}", entry.getKey(), entry.getValue());
160             modules.add(entry.getValue());
161         }
162     }
163 
164     @Nonnull
165     protected Map<String, Module> sortModules(@Nonnull List<Module> moduleInstances) {
166         return sortByDependencies(moduleInstances, "Module""module");
167     }
168 
169     @Nonnull
170     protected abstract List<Module> loadModules();
171 
172     private void createInjector(@Nonnull Iterable<Binding<?>> bindingsthrows Exception {
173         ServiceLoader<InjectorFactory> serviceLoader = ServiceLoader.load(InjectorFactory.class);
174         try {
175             Iterator<InjectorFactory> iterator = serviceLoader.iterator();
176             InjectorFactory injectorFactory = iterator.next();
177             LOG.debug("Injector will be created by {}", injectorFactory);
178             Injector<?> injector = injectorFactory.createInjector(application, bindings);
179             GriffonClassUtils.setProperty(application, INJECTOR, injector);
180         catch (Exception e) {
181             LOG.error("An error occurred while initializing the injector", sanitize(e));
182             throw e;
183         }
184     }
185 }