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