View Javadoc

1   package org.apache.onami.autobind.configuration;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements.  See the NOTICE file distributed with
6    * this work for additional information regarding copyright ownership.
7    * The ASF licenses this file to You under the Apache License, Version 2.0
8    * (the "License"); you may not use this file except in compliance with
9    * the License.  You may obtain a copy of the License at
10   *
11   *  http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  import static com.google.inject.TypeLiteral.get;
21  import static java.lang.System.getProperty;
22  import static java.util.Collections.addAll;
23  import static java.util.logging.Level.FINE;
24  import static java.util.logging.Level.INFO;
25  import static java.util.logging.Logger.getLogger;
26  import static org.apache.onami.autobind.jsr330.Names.named;
27  
28  import java.io.File;
29  import java.net.MalformedURLException;
30  import java.net.URI;
31  import java.net.URISyntaxException;
32  import java.net.URL;
33  import java.net.URLClassLoader;
34  import java.util.ArrayList;
35  import java.util.Collections;
36  import java.util.HashSet;
37  import java.util.List;
38  import java.util.Set;
39  import java.util.logging.Logger;
40  
41  import org.apache.onami.autobind.annotations.features.AutoBindingFeature;
42  import org.apache.onami.autobind.annotations.features.ImplementationBindingFeature;
43  import org.apache.onami.autobind.annotations.features.ModuleBindingFeature;
44  import org.apache.onami.autobind.annotations.features.MultiBindingFeature;
45  import org.apache.onami.autobind.install.BindingTracer;
46  import org.apache.onami.autobind.install.InstallationContext;
47  import org.apache.onami.autobind.install.bindjob.BindingJob;
48  import org.apache.onami.autobind.scanner.ClasspathScanner;
49  import org.apache.onami.autobind.scanner.PackageFilter;
50  import org.apache.onami.autobind.scanner.ScannerModule;
51  import org.apache.onami.autobind.scanner.features.ScannerFeature;
52  import org.apache.onami.configuration.PropertiesURLReader;
53  
54  import com.google.inject.AbstractModule;
55  import com.google.inject.Binder;
56  import com.google.inject.Guice;
57  import com.google.inject.Injector;
58  import com.google.inject.Module;
59  import com.google.inject.multibindings.Multibinder;
60  
61  /**
62   * The StartupModule is used for creating an initial Injector, which binds and
63   * instantiates the Scanning module. Due the fact that we have multiple Scanner
64   * Implementations, you have to pass the Class for the Scanner and the Packages
65   * which should be scanned. You can override the bindAnnotationListeners-Method,
66   * to add your own {@link ScannerFeature}.
67   */
68  public abstract class StartupModule extends AbstractModule {
69  
70  	protected final Logger _logger = getLogger(getClass().getName());
71  
72  	protected PackageFilter[] _packages;
73  
74  	protected Class<? extends ClasspathScanner> _scanner;
75  
76  	protected List<Class<? extends ScannerFeature>> _features = new ArrayList<Class<? extends ScannerFeature>>();
77  
78  	protected boolean bindSystemProperties;
79  
80  	protected boolean bindEnvironment;
81  
82  	protected boolean bindStartupConfiguration = true;
83  
84  	protected boolean verbose = (getProperty("org.apache.onami.autobind.verbose") != null ? true
85  			: false);
86  
87  	public StartupModule(Class<? extends ClasspathScanner> scanner,
88  			PackageFilter... filter) {
89  		_packages = (filter == null ? new PackageFilter[0] : filter);
90  		_scanner = scanner;
91  	}
92  
93  	@Override
94  	public void configure() {
95  		List<PackageFilter> packages = new ArrayList<PackageFilter>();
96  		addAll(packages, _packages);
97  		addAll(packages, bindPackages());
98  
99  		_packages = packages.toArray(new PackageFilter[packages.size()]);
100 		Module scannerModule = new AbstractModule() {
101 			@Override
102 			protected void configure() {
103 				Binder binder = binder();
104 				if (_logger.isLoggable(FINE)) {
105 					_logger.fine("Binding ClasspathScanner to "
106 							+ _scanner.getName());
107 					for (PackageFilter p : _packages) {
108 						_logger.fine("Using Package " + p + " for scanning.");
109 					}
110 				}
111 
112 				binder.bind(InstallationContext.class).asEagerSingleton();
113 				binder.bind(ClasspathScanner.class).to(_scanner);
114 				binder.bind(get(PackageFilter[].class))
115 						.annotatedWith(named("packages")).toInstance(_packages);
116 				Set<URL> classpath = findClassPaths();
117 				binder.bind(get(URL[].class))
118 						.annotatedWith(named("classpath"))
119 						.toInstance(
120 								classpath.toArray(new URL[classpath.size()]));
121 
122 				bindFeatures(binder);
123 			}
124 		};
125 
126 		ConfigurationModule configurationModule = new ConfigurationModule();
127 		if (bindSystemProperties) {
128 			configurationModule.bindSystemProperties();
129 		}
130 		if (bindEnvironment) {
131 			configurationModule.bindEnvironmentVariables();
132 		}
133 
134 		if (bindStartupConfiguration) {
135 			URL startup = getClass().getResource("/conf/startup.xml");
136 			if (startup != null) {
137 				try {
138 					URI startupURI = startup.toURI();
139 					_logger.log(INFO, "Startup Config is used from Path: "
140 							+ startupURI);
141 					PropertiesURLReader reader = new PropertiesURLReader(
142 							startup);
143 					reader.inXMLFormat();
144 					configurationModule.addConfigurationReader(reader);
145 				} catch (URISyntaxException e) {
146 					_logger.log(INFO,
147 							"Startup Config couldn't be found in Classpath.", e);
148 				}
149 			} else {
150 				_logger.log(INFO,
151 						"Startup Config couldn't be found, so it is not used.");
152 			}
153 		}
154 
155 		Injector internal = Guice.createInjector(scannerModule,
156 				configurationModule);
157 		binder().install(internal.getInstance(ScannerModule.class));
158 		binder().install(configurationModule);
159 
160 		if (verbose) {
161 			BindingTracer tracer = internal.getInstance(BindingTracer.class);
162 
163 			StringBuilder builder = new StringBuilder(
164 					"Following Binding were processed.\n");
165 			for (BindingJob job : tracer) {
166 				builder.append(job.toString()).append("\n");
167 			}
168 			_logger.info(builder.toString());
169 		}
170 	}
171 
172 	protected abstract Multibinder<ScannerFeature> bindFeatures(Binder binder);
173 
174 	protected PackageFilter[] bindPackages() {
175 		return new PackageFilter[0];
176 	}
177 
178 	protected Set<URL> findClassPaths() {
179 		Set<URL> urlSet = new HashSet<URL>();
180 
181 		ClassLoader loader = Thread.currentThread().getContextClassLoader();
182 		while (loader != null) {
183 			if (loader instanceof URLClassLoader) {
184 				URL[] urls = ((URLClassLoader) loader).getURLs();
185 				Collections.addAll(urlSet, urls);
186 			}
187 			loader = loader.getParent();
188 		}
189 
190 		String classpath = System.getProperty("java.class.path");
191 		if (classpath != null && classpath.length() > 0) {
192 			try {
193 				URL resource = StartupModule.class.getResource("/");
194 
195 				if (resource == null) {
196 					String className = StartupModule.class.getName().replace(
197 							'.', '/')
198 							+ ".class";
199 					resource = StartupModule.class.getResource(className);
200 
201 					if (resource != null) {
202 						String url = resource.toExternalForm();
203 						url = url.substring(0,
204 								url.length() - className.length());
205 						resource = new URL(url);
206 					}
207 				}
208 
209 				if (resource != null) {
210 					classpath = classpath + File.pathSeparator
211 							+ new File(resource.toURI()).getAbsolutePath();
212 				}
213 			} catch (URISyntaxException e) {
214 				// FIXME ignore for now
215 			} catch (MalformedURLException e) {
216 				// FIXME ignore for now
217 			}
218 
219 			for (String path : classpath.split(File.pathSeparator)) {
220 				File file = new File(path);
221 				try {
222 					if (file.exists()) {
223 						urlSet.add(file.toURI().toURL());
224 					}
225 				} catch (MalformedURLException e) {
226 					_logger.log(INFO,
227 							"Found invalid URL in Classpath: " + path, e);
228 				}
229 			}
230 		}
231 
232 		return urlSet;
233 	}
234 
235 	public StartupModule addFeature(Class<? extends ScannerFeature> listener) {
236 		_features.add(listener);
237 		return this;
238 	}
239 
240 	public StartupModule bindSystemProperties() {
241 		bindSystemProperties = true;
242 		return this;
243 	}
244 
245 	public StartupModule disableStartupConfiguration() {
246 		bindStartupConfiguration = false;
247 		return this;
248 	}
249 
250 	public StartupModule bindEnvironment() {
251 		bindEnvironment = true;
252 		return this;
253 	}
254 
255 	public void verbose() {
256 		verbose = true;
257 	}
258 
259 	public static StartupModule create(
260 			Class<? extends ClasspathScanner> scanner, PackageFilter... filter) {
261 		return new DefaultStartupModule(scanner, filter);
262 	}
263 
264 	public static class DefaultStartupModule extends StartupModule {
265 		public DefaultStartupModule(Class<? extends ClasspathScanner> scanner,
266 				PackageFilter... filter) {
267 			super(scanner, filter);
268 		}
269 
270 		@Override
271 		protected Multibinder<ScannerFeature> bindFeatures(Binder binder) {
272 			Multibinder<ScannerFeature> listeners = Multibinder.newSetBinder(
273 					binder, ScannerFeature.class);
274 			listeners.addBinding().to(AutoBindingFeature.class);
275 			listeners.addBinding().to(ImplementationBindingFeature.class);
276 			listeners.addBinding().to(MultiBindingFeature.class);
277 			listeners.addBinding().to(ModuleBindingFeature.class);
278 
279 			for (Class<? extends ScannerFeature> listener : _features) {
280 				listeners.addBinding().to(listener);
281 			}
282 
283 			return listeners;
284 		}
285 
286 		@Override
287 		protected PackageFilter[] bindPackages() {
288 			try {
289 				return new PackageFilter[] { PackageFilter
290 						.create(Thread
291 								.currentThread()
292 								.getContextClassLoader()
293 								.loadClass(
294 										"org.apache.onami.autobind.configuration.ConfigurationModule"),
295 								false) };
296 			} catch (ClassNotFoundException e) {
297 				// TODO Auto-generated catch block
298 				e.printStackTrace();
299 				return new PackageFilter[0];
300 			}
301 		}
302 	}
303 
304 }