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("JVM: {}", GriffonEnvironment.getJvmVersion());
068         LOG.info("OS: {}", GriffonEnvironment.getOsVersion());
069 
070         // 2 create bindings
071         LOG.debug("Creating module bindings");
072         Iterable<Binding<?>> bindings = createBindings();
073 
074         if (LOG.isTraceEnabled()) {
075             for (Binding<?> binding : bindings) {
076                 LOG.trace(binding.toString());
077             }
078         }
079 
080         // 3 create injector
081         LOG.debug("Creating application injector");
082         createInjector(bindings);
083     }
084 
085     @Override
086     public void run() {
087         application.initialize();
088         application.startup();
089         application.ready();
090     }
091 
092     @Nonnull
093     protected Iterable<Binding<?>> createBindings() {
094         Map<Key, Binding<?>> map = new LinkedHashMap<>();
095 
096         List<Module> modules = new ArrayList<>();
097         createApplicationModule(modules);
098         createArtifactsModule(modules);
099         collectModuleBindings(modules);
100 
101         for (Module module : modules) {
102             for (Binding<?> binding : module.getBindings()) {
103                 map.put(Key.of(binding), binding);
104             }
105         }
106 
107         return unmodifiableCollection(map.values());
108     }
109 
110     protected void createArtifactsModule(@Nonnull List<Module> modules) {
111         final List<Class<?>> classes = new ArrayList<>();
112         load(getClass().getClassLoader(), GRIFFON_PATH, new ServiceLoaderUtils.PathFilter() {
113             @Override
114             public boolean accept(@Nonnull String path) {
115                 return !path.endsWith(PROPERTIES);
116             }
117         }new ServiceLoaderUtils.ResourceProcessor() {
118             @Override
119             public void process(@Nonnull ClassLoader classLoader, @Nonnull String line) {
120                 line = line.trim();
121                 try {
122                     classes.add(classLoader.loadClass(line));
123                 catch (ClassNotFoundException e) {
124                     LOG.warn("'" + line + "' could not be resolved as a Class");
125                 }
126             }
127         });
128 
129         modules.add(new AbstractModule() {
130             @Override
131             protected void doConfigure() {
132                 for (Class<?> clazz : classes) {
133                     if (GriffonService.class.isAssignableFrom(clazz)) {
134                         bind(clazz).asSingleton();
135                     else {
136                         bind(clazz);
137                     }
138                 }
139             }
140         });
141     }
142 
143     protected void createApplicationModule(@Nonnull List<Module> modules) {
144         modules.add(new AbstractModule() {
145             @Override
146             protected void doConfigure() {
147                 bind(GriffonApplication.class)
148                     .toInstance(application);
149             }
150         });
151     }
152 
153     protected void collectModuleBindings(@Nonnull Collection<Module> modules) {
154         List<Module> moduleInstances = loadModules();
155         moduleInstances.add(0new DefaultApplicationModule());
156         Map<String, Module> sortedModules = sortModules(moduleInstances);
157         for (Map.Entry<String, Module> entry : sortedModules.entrySet()) {
158             LOG.debug("Loading module bindings from {}:{}", entry.getKey(), entry.getValue());
159             modules.add(entry.getValue());
160         }
161     }
162 
163     @Nonnull
164     protected Map<String, Module> sortModules(@Nonnull List<Module> moduleInstances) {
165         return sortByDependencies(moduleInstances, "Module""module");
166     }
167 
168     @Nonnull
169     protected abstract List<Module> loadModules();
170 
171     private void createInjector(@Nonnull Iterable<Binding<?>> bindingsthrows Exception {
172         ServiceLoader<InjectorFactory> serviceLoader = ServiceLoader.load(InjectorFactory.class);
173         try {
174             Iterator<InjectorFactory> iterator = serviceLoader.iterator();
175             InjectorFactory injectorFactory = iterator.next();
176             LOG.debug("Injector will be created by {}", injectorFactory);
177             Injector<?> injector = injectorFactory.createInjector(application, bindings);
178             GriffonClassUtils.setProperty(application, INJECTOR, injector);
179         catch (Exception e) {
180             LOG.error("An error occurred while initializing the injector", sanitize(e));
181             throw e;
182         }
183     }
184 }