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