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(0, new 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<?>> bindings) throws 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 }
|