/components/camel-guice/src/main/java/org/apache/camel/guice/inject/Injectors.java
Java | 457 lines | 286 code | 34 blank | 137 comment | 58 complexity | 532608309008005bbc59c594aa1d1c5e MD5 | raw file
1/**
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package org.apache.camel.guice.inject;
19
20import java.lang.annotation.Annotation;
21import java.lang.reflect.Field;
22import java.lang.reflect.Type;
23import java.util.List;
24import java.util.Map;
25import java.util.Map.Entry;
26import java.util.Set;
27import java.util.StringTokenizer;
28
29import com.google.common.collect.Lists;
30import com.google.common.collect.Sets;
31import com.google.inject.AbstractModule;
32import com.google.inject.Binding;
33import com.google.inject.Guice;
34import com.google.inject.Injector;
35import com.google.inject.Key;
36import com.google.inject.Module;
37import com.google.inject.Provider;
38import com.google.inject.Scope;
39import com.google.inject.Singleton;
40import com.google.inject.TypeLiteral;
41import com.google.inject.internal.BindingImpl;
42import com.google.inject.internal.Scoping;
43import com.google.inject.matcher.Matcher;
44import com.google.inject.name.Names;
45import com.google.inject.util.Modules;
46
47import org.apache.camel.guice.jndi.GuiceInitialContextFactory;
48import org.apache.camel.guice.jndi.internal.Classes;
49import org.apache.camel.guice.support.CloseErrors;
50import org.apache.camel.guice.support.CloseFailedException;
51import org.apache.camel.guice.support.Closer;
52import org.apache.camel.guice.support.Closers;
53import org.apache.camel.guice.support.CompositeCloser;
54import org.apache.camel.guice.support.HasScopeAnnotation;
55import org.apache.camel.guice.support.internal.CloseErrorsImpl;
56
57@SuppressWarnings({ "rawtypes", "unchecked" })
58public final class Injectors {
59 public static final String MODULE_CLASS_NAMES = "org.guiceyfruit.modules";
60
61 private Injectors() {
62 //Helper class
63 }
64 /**
65 * Creates an injector from the given properties, loading any modules define
66 * by the {@link #MODULE_CLASS_NAMES} property value (space separated) along
67 * with any other modules passed as an argument.
68 *
69 * @param environment
70 * the properties used to create the injector
71 * @param overridingModules
72 * any modules which override the modules referenced in the
73 * environment such as to provide the actual JNDI context
74 */
75 public static Injector createInjector(final Map environment,
76 Module... overridingModules) throws ClassNotFoundException,
77 IllegalAccessException, InstantiationException {
78 List<Module> modules = Lists.newArrayList();
79
80 // lets bind the properties
81 modules.add(new AbstractModule() {
82 protected void configure() {
83 Names.bindProperties(binder(), environment);
84 }
85 });
86
87 Object moduleValue = environment.get(MODULE_CLASS_NAMES);
88 if (moduleValue instanceof String) {
89 String names = (String) moduleValue;
90 StringTokenizer iter = new StringTokenizer(names);
91 while (iter.hasMoreTokens()) {
92 String moduleName = iter.nextToken();
93 Module module = loadModule(moduleName);
94 if (module != null) {
95 modules.add(module);
96 }
97 }
98 }
99 Injector injector = Guice.createInjector(Modules.override(modules)
100 .with(overridingModules));
101 return injector;
102 }
103
104 /**
105 * Returns an instance of the given type with the
106 * {@link com.google.inject.name.Named} annotation value.
107 *
108 * This method allows you to switch this code
109 * <code>injector.getInstance(Key.get(type, Names.named(name)));</code>
110 *
111 * to the more concise
112 * <code>Injectors.getInstance(injector, type, name);</code>
113 */
114 public static <T> T getInstance(Injector injector, java.lang.Class<T> type,
115 String name) {
116 return injector.getInstance(Key.get(type, Names.named(name)));
117 }
118
119 /**
120 * Returns a collection of all instances of the given base type
121 *
122 * @param baseClass
123 * the base type of objects required
124 * @param <T>
125 * the base type
126 * @return a set of objects returned from this injector
127 */
128 public static <T> Set<T> getInstancesOf(Injector injector,
129 Class<T> baseClass) {
130 Set<T> answer = Sets.newHashSet();
131 Set<Entry<Key<?>, Binding<?>>> entries = injector.getBindings()
132 .entrySet();
133 for (Entry<Key<?>, Binding<?>> entry : entries) {
134 Key<?> key = entry.getKey();
135 Class<?> keyType = getKeyType(key);
136 if (keyType != null && baseClass.isAssignableFrom(keyType)) {
137 Binding<?> binding = entry.getValue();
138 Object value = binding.getProvider().get();
139 if (value != null) {
140 T castValue = baseClass.cast(value);
141 answer.add(castValue);
142 }
143 }
144 }
145 return answer;
146 }
147
148 /**
149 * Returns a collection of all instances matching the given matcher
150 *
151 * @param matcher
152 * matches the types to return instances
153 * @return a set of objects returned from this injector
154 */
155 public static <T> Set<T> getInstancesOf(Injector injector,
156 Matcher<Class> matcher) {
157 Set<T> answer = Sets.newHashSet();
158 Set<Entry<Key<?>, Binding<?>>> entries = injector.getBindings()
159 .entrySet();
160 for (Entry<Key<?>, Binding<?>> entry : entries) {
161 Key<?> key = entry.getKey();
162 Class<?> keyType = getKeyType(key);
163 if (keyType != null && matcher.matches(keyType)) {
164 Binding<?> binding = entry.getValue();
165 Object value = binding.getProvider().get();
166 answer.add((T) value);
167 }
168 }
169 return answer;
170 }
171
172 /**
173 * Returns a collection of all of the providers matching the given matcher
174 *
175 * @param matcher
176 * matches the types to return instances
177 * @return a set of objects returned from this injector
178 */
179 public static <T> Set<Provider<T>> getProvidersOf(Injector injector,
180 Matcher<Class> matcher) {
181 Set<Provider<T>> answer = Sets.newHashSet();
182 Set<Entry<Key<?>, Binding<?>>> entries = injector.getBindings()
183 .entrySet();
184 for (Entry<Key<?>, Binding<?>> entry : entries) {
185 Key<?> key = entry.getKey();
186 Class<?> keyType = getKeyType(key);
187 if (keyType != null && matcher.matches(keyType)) {
188 Binding<?> binding = entry.getValue();
189 answer.add((Provider<T>) binding.getProvider());
190 }
191 }
192 return answer;
193 }
194
195 /**
196 * Returns a collection of all providers of the given base type
197 *
198 * @param baseClass
199 * the base type of objects required
200 * @param <T>
201 * the base type
202 * @return a set of objects returned from this injector
203 */
204 public static <T> Set<Provider<T>> getProvidersOf(Injector injector,
205 Class<T> baseClass) {
206 Set<Provider<T>> answer = Sets.newHashSet();
207 Set<Entry<Key<?>, Binding<?>>> entries = injector.getBindings()
208 .entrySet();
209 for (Entry<Key<?>, Binding<?>> entry : entries) {
210 Key<?> key = entry.getKey();
211 Class<?> keyType = getKeyType(key);
212 if (keyType != null && baseClass.isAssignableFrom(keyType)) {
213 Binding<?> binding = entry.getValue();
214 answer.add((Provider<T>) binding.getProvider());
215 }
216 }
217 return answer;
218 }
219
220 /** Returns true if a binding exists for the given matcher */
221 public static boolean hasBinding(Injector injector, Matcher<Class> matcher) {
222 return !getBindingsOf(injector, matcher).isEmpty();
223 }
224
225 /** Returns true if a binding exists for the given base class */
226 public static boolean hasBinding(Injector injector, Class<?> baseClass) {
227 return !getBindingsOf(injector, baseClass).isEmpty();
228 }
229
230 /** Returns true if a binding exists for the given key */
231 public static boolean hasBinding(Injector injector, Key<?> key) {
232 Binding<?> binding = getBinding(injector, key);
233 return binding != null;
234 }
235
236 /**
237 * Returns the binding for the given key or null if there is no such binding
238 */
239 public static Binding<?> getBinding(Injector injector, Key<?> key) {
240 Map<Key<?>, Binding<?>> bindings = injector.getBindings();
241 Binding<?> binding = bindings.get(key);
242 return binding;
243 }
244
245 /**
246 * Returns a collection of all of the bindings matching the given matcher
247 *
248 * @param matcher
249 * matches the types to return instances
250 * @return a set of objects returned from this injector
251 */
252 public static Set<Binding<?>> getBindingsOf(Injector injector,
253 Matcher<Class> matcher) {
254 Set<Binding<?>> answer = Sets.newHashSet();
255 Set<Entry<Key<?>, Binding<?>>> entries = injector.getBindings()
256 .entrySet();
257 for (Entry<Key<?>, Binding<?>> entry : entries) {
258 Key<?> key = entry.getKey();
259 Class<?> keyType = getKeyType(key);
260 if (keyType != null && matcher.matches(keyType)) {
261 answer.add(entry.getValue());
262 }
263 }
264 return answer;
265 }
266
267 /**
268 * Returns a collection of all bindings of the given base type
269 *
270 * @param baseClass
271 * the base type of objects required
272 * @return a set of objects returned from this injector
273 */
274 public static Set<Binding<?>> getBindingsOf(Injector injector,
275 Class<?> baseClass) {
276 Set<Binding<?>> answer = Sets.newHashSet();
277 Set<Entry<Key<?>, Binding<?>>> entries = injector.getBindings()
278 .entrySet();
279 for (Entry<Key<?>, Binding<?>> entry : entries) {
280 Key<?> key = entry.getKey();
281 Class<?> keyType = getKeyType(key);
282 if (keyType != null && baseClass.isAssignableFrom(keyType)) {
283 answer.add(entry.getValue());
284 }
285 }
286 return answer;
287 }
288
289 /** Returns the key type of the given key */
290 public static <T> Class<?> getKeyType(Key<?> key) {
291 Class<?> keyType = null;
292 TypeLiteral<?> typeLiteral = key.getTypeLiteral();
293 Type type = typeLiteral.getType();
294 if (type instanceof Class) {
295 keyType = (Class<?>) type;
296 }
297 return keyType;
298 }
299
300 protected static Module loadModule(String moduleName)
301 throws ClassNotFoundException, IllegalAccessException,
302 InstantiationException {
303 Class<?> type = Classes.loadClass(moduleName,
304 GuiceInitialContextFactory.class.getClassLoader());
305 return (Module) type.newInstance();
306 }
307
308 /*
309 */
310 /**
311 * Closes the given scope on this injector
312 *
313 * @param injector
314 * the injector on which to close objects
315 * @param scopeAnnotation
316 * the scope on which to close the objects
317 * @throws CloseFailedException
318 * the exceptions caused if closing an object fails
319 */
320 /*
321 * public static void close(Injector injector, Annotation scopeAnnotation)
322 * throws CloseFailedException { Key<PreDestroyer> key =
323 * Key.get(PreDestroyer.class, scopeAnnotation); if (hasBinding(injector,
324 * key)) { PreDestroyer destroyer = injector.getInstance(key);
325 * destroyer.close(); } }
326 */
327
328 /**
329 * Closes any singleton objects in the injector using the currently
330 * registered {@link Closer} implementations
331 */
332 public static void close(Injector injector) throws CloseFailedException {
333 close(injector, new CloseErrorsImpl(Injectors.class));
334 }
335
336 /**
337 * Closes objects within the given scope using the currently registered
338 * {@link Closer} implementations
339 */
340 public static void close(Injector injector, CloseErrors errors)
341 throws CloseFailedException {
342 close(injector, Singleton.class, errors);
343 }
344
345 /**
346 * Closes objects within the given scope using the currently registered
347 * {@link Closer} implementations
348 */
349 public static void close(Injector injector,
350 Class<? extends Annotation> scopeAnnotationToClose)
351 throws CloseFailedException {
352 close(injector, scopeAnnotationToClose, new CloseErrorsImpl(
353 Injectors.class));
354 }
355
356 /**
357 * Closes objects within the given scope using the currently registered
358 * {@link Closer} implementations
359 */
360 public static void close(Injector injector,
361 Class<? extends Annotation> scopeAnnotationToClose,
362 CloseErrors errors) throws CloseFailedException {
363 Set<Closer> closers = getInstancesOf(injector, Closer.class);
364 Closer closer = CompositeCloser.newInstance(closers);
365 if (closer == null) {
366 return;
367 }
368
369 Set<Entry<Key<?>, Binding<?>>> entries = injector.getBindings()
370 .entrySet();
371 for (Entry<Key<?>, Binding<?>> entry : entries) {
372 Key<?> key = entry.getKey();
373 Binding<?> binding = entry.getValue();
374 closeBinding(key, binding, scopeAnnotationToClose, closer, errors);
375 }
376
377 tryCloseJitBindings(closer, injector, scopeAnnotationToClose, errors);
378 errors.throwIfNecessary();
379 }
380
381 private static void tryCloseJitBindings(Closer closer, Injector injector,
382 Class<? extends Annotation> scopeAnnotationToClose,
383 CloseErrors errors) {
384 Class<? extends Injector> type = injector.getClass();
385 Field field;
386 try {
387 field = type.getDeclaredField("jitBindings");
388 field.setAccessible(true);
389 Object bindings = field.get(injector);
390 if (bindings != null) {
391 if (bindings instanceof Map) {
392 Map<Key<?>, BindingImpl<?>> map = (Map<Key<?>, BindingImpl<?>>) bindings;
393 Set<Entry<Key<?>, BindingImpl<?>>> entries = map.entrySet();
394 for (Entry<Key<?>, BindingImpl<?>> entry : entries) {
395 closeBinding(entry.getKey(), entry.getValue(),
396 scopeAnnotationToClose, closer, errors);
397 }
398 }
399 }
400 } catch (NoSuchFieldException e) {
401 // ignore - Guice has refactored so we can't access the jit bindings
402 // System.out.println("No such field! " + e);
403 } catch (IllegalAccessException e) {
404 // ignore - Guice has refactored so we can't access the jit bindings
405 // System.out.println("Failed to access field: " + field +
406 // ". Reason: " + e);
407 }
408 }
409
410 private static void closeBinding(Key<?> key, Binding<?> binding,
411 Class<? extends Annotation> scopeAnnotationToClose, Closer closer,
412 CloseErrors errors) {
413 Provider<?> provider = binding.getProvider();
414
415 Class<? extends Annotation> scopeAnnotation = getScopeAnnotation(binding);
416 if ((scopeAnnotation != null)
417 && scopeAnnotation.equals(scopeAnnotationToClose)) {
418 Object value = provider.get();
419 if (value != null) {
420 Closers.close(key, value, closer, errors);
421 }
422 }
423 }
424
425 /**
426 * Returns the scope annotation for the given binding or null if there is no
427 * scope
428 */
429 public static Class<? extends Annotation> getScopeAnnotation(
430 Binding<?> binding) {
431 Class<? extends Annotation> scopeAnnotation = null;
432 if (binding instanceof BindingImpl) {
433 BindingImpl bindingImpl = (BindingImpl) binding;
434 Scoping scoping = bindingImpl.getScoping();
435 if (scoping != null) {
436 scopeAnnotation = scoping.getScopeAnnotation();
437
438 // TODO not sure why we need this hack???
439 if (scopeAnnotation == null) {
440 Scope scope = scoping.getScopeInstance();
441 if (scope instanceof HasScopeAnnotation) {
442 HasScopeAnnotation hasScopeAnnotation = (HasScopeAnnotation) scope;
443 scopeAnnotation = hasScopeAnnotation
444 .getScopeAnnotation();
445 }
446
447 if (scopeAnnotation == null
448 && (scoping == Scoping.EAGER_SINGLETON
449 || scoping == Scoping.SINGLETON_ANNOTATION || scoping == Scoping.SINGLETON_INSTANCE)) {
450 scopeAnnotation = Singleton.class;
451 }
452 }
453 }
454 }
455 return scopeAnnotation;
456 }
457}