KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > directwebremoting > guice > BindUtil


1 /*
2  * Copyright 2007 Tim Peierls
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.directwebremoting.guice;
17
18 import com.google.inject.Binder;
19 import com.google.inject.Inject;
20 import com.google.inject.Injector;
21 import com.google.inject.Key;
22 import com.google.inject.Provider;
23
24 import java.lang.reflect.Constructor JavaDoc;
25 import java.lang.reflect.Method JavaDoc;
26 import java.lang.reflect.InvocationTargetException JavaDoc;
27 import static java.lang.reflect.Modifier.isStatic JavaDoc;
28
29 /**
30  * Binding utilities for creating ad hoc providers. These come in two flavors,
31  * constructor and factory method. In both cases, you can specify additional
32  * methods to inject after creation by chaining calls to
33  * {@link BindingProvider#injecting injecting(methodName, paramKeys)}.
34  * Each method has two variants, one with a {@code Binder} parameter and one
35  * without. The former reports construction errors via the binder (so more than
36  * one error can be reported), the latter throws a runtime exception immediately,
37  * preventing further error reporting.
38  *
39  * <p> Some examples:
40  * <pre>
41  * bind(SomeInterface.class)
42  * .toProvider(
43  * .fromConstructor(LegacyImpl.class,
44  * Key.get(String.class, MyAnnotation.class))
45  * .injecting("configure", Key.get(Configuration.class)));
46  * </pre>
47  * This binds {@code SomeInterface} to a provider that uses the
48  * {@code LegacyImpl} constructor with a single String parameter.
49  * That parameter is injected as if it had been marked with {@code @MyAnnotation}.
50  * It also calls the {@code LegacyImpl.configure} method with an injected
51  * instance of {@code Configuration}.
52  * <pre>
53  * bind(SomeInterface.class)
54  * .toProvider(
55  * .fromFactoryMethod(SomeInterface.class,
56  * LegacyFactory.class, "newSomeInterface",
57  * Key.get(String.class, MyAnnotation.class)));
58  * </pre>
59  * This binds {@code SomeInterface} to a provider that calls a factory method
60  * {@code newSomeInterface} of {@code LegacyFactory} with a single string parameter,
61  * which is injected as if it were marked with {@code @MyAnnotation}. If the
62  * method is not static, an instance of {@code LegacyFactory} is created by injection
63  * and used to call the method.
64  * @author Tim Peierls [tim at peierls dot net]
65  */

66 public class BindUtil
67 {
68     /**
69      * For fluent-style decoration with one or more method bindings when
70      * using {@link #fromConstructor}.
71      */

72     public interface BindingProvider<T> extends Provider<T>
73     {
74         /**
75          * Adds injection of a method defined by the given name and parameter
76          * types (specified as Guice keys) to this provider.
77          */

78         BindingProvider<T> injecting(String JavaDoc methodName, Key... paramKeys);
79     }
80     
81     /**
82      * Creates a chainable provider that constructs an instance of the
83      * given type given a list of constructor parameter types, specified
84      * as Guice keys. Construction errors are thrown immediately.
85      */

86     public static <T> BindingProvider<T> fromConstructor(Class JavaDoc<T> type, final Key... keys)
87     {
88         return new ConstructorBindingProvider<T>(null, type, keys);
89     }
90     
91     /**
92      * Creates a chainable provider that constructs an instance of the
93      * given type given a list of constructor parameter types, specified
94      * as Guice keys. Construction errors are passed to {@code binder}
95      * to be thrown at the end of all binding.
96      */

97     public static <T> BindingProvider<T> fromConstructor(Binder binder, Class JavaDoc<T> type, final Key... keys)
98     {
99         return new ConstructorBindingProvider<T>(binder, type, keys);
100     }
101     
102     /**
103      * Creates a chainable provider that constructs an instance of {@code providedType}
104      * using a factory method defined by {@code factoryType}, {@code methodName},
105      * and a list of method parameter types specified as Guice keys. If the
106      * method is not static an instance of the factory type is created and injected.
107      * Construction errors are thrown immediately.
108      */

109     public static <T> BindingProvider<T> fromFactoryMethod(
110         Class JavaDoc<T> providedType, Class JavaDoc<?> factoryType, String JavaDoc methodName, final Key... keys)
111     {
112         return new FactoryMethodBindingProvider<T>(
113             null, providedType, Key.get(factoryType), methodName, keys);
114     }
115     
116     /**
117      * Creates a chainable provider that constructs an instance of {@code providedType}
118      * using a factory method defined by {@code factoryType}, {@code methodName},
119      * and a list of method parameter types specified as Guice keys. If the
120      * method is not static an instance of the factory type is created and injected.
121      * Construction errors are passed to {@code binder} to be thrown at the end
122      * of all binding.
123      */

124     public static <T> BindingProvider<T> fromFactoryMethod(
125         Binder binder, Class JavaDoc<T> providedType,
126         Class JavaDoc<?> factoryType, String JavaDoc methodName, final Key... keys)
127     {
128         return new FactoryMethodBindingProvider<T>(
129             binder, providedType, Key.get(factoryType), methodName, keys);
130     }
131     
132     /**
133      * Creates a chainable provider that constructs an instance of {@code providedType} by
134      * calling method {@code methodName} of the type in {@code factoryKey} with
135      * method parameter types specified as Guice keys. If the method is not static
136      * an instance is created and injected using the factory key.
137      * Construction errors are thrown immediately.
138      */

139     public static <T> BindingProvider<T> fromFactoryMethod(
140         Class JavaDoc<T> providedType, Key<?> factoryKey, String JavaDoc methodName, final Key... keys)
141     {
142         return new FactoryMethodBindingProvider<T>(
143             null, providedType, factoryKey, methodName, keys);
144     }
145     
146     /**
147      * Creates a chainable provider that constructs an instance of {@code providedType} by
148      * calling method {@code methodName} of the type in {@code factoryKey} with
149      * method parameter types specified as Guice keys. If the method is not static
150      * an instance is created and injected using the factory key.
151      * Construction errors are passed to {@code binder} to be thrown at the end
152      * of all binding.
153      */

154     public static <T> BindingProvider<T> fromFactoryMethod(
155         Binder binder, Class JavaDoc<T> providedType,
156         Key<?> factoryKey, String JavaDoc methodName, final Key... keys)
157     {
158         return new FactoryMethodBindingProvider<T>(
159             binder, providedType, factoryKey, methodName, keys);
160     }
161
162
163     //
164
// Implementation classes
165
//
166

167     private static abstract class AbstractBindingProvider<T> implements BindingProvider<T>
168     {
169         protected AbstractBindingProvider(Binder binder, Class JavaDoc<T> type, Key... keys)
170         {
171             this.binder = binder;
172             this.type = type;
173             this.keys = keys;
174         }
175         
176         public final T get()
177         {
178             return get(theInjector);
179         }
180         
181         protected abstract T get(Injector injector);
182         
183         public final BindingProvider<T> injecting(String JavaDoc methodName, Key... paramKeys)
184         {
185             return new MethodBindingProvider(this, type, methodName, paramKeys);
186         }
187         
188         protected final Class JavaDoc[] getTypes()
189         {
190             Class JavaDoc[] types = new Class JavaDoc[keys.length];
191             int i = 0;
192             for (Key key : keys)
193             {
194                 @SuppressWarnings JavaDoc("unchecked")
195                 Class JavaDoc type = (Class JavaDoc) key.getTypeLiteral().getType();
196                 types[i++] = type;
197             }
198             return types;
199         }
200
201         protected final Object JavaDoc[] getValues(Injector injector)
202         {
203             Object JavaDoc[] values = new Object JavaDoc[keys.length];
204             int i = 0;
205             for (Key key : keys)
206             {
207                 Object JavaDoc param = injector.getInstance(key);
208                 values[i++] = param;
209             }
210             return values;
211         }
212         
213         protected final Binder binder;
214         protected final Class JavaDoc<T> type;
215         protected final Key[] keys;
216        
217         /**
218          * Effectively immutable: Injected at end of bind-time,
219          * read-only thereafter, and there is (or should be) a
220          * happens-before edge between bind-time and subsequent reads.
221          */

222         @Inject private Injector theInjector;
223     }
224     
225     private static class ConstructorBindingProvider<T> extends AbstractBindingProvider<T>
226     {
227         ConstructorBindingProvider(Binder binder, Class JavaDoc<T> type, Key... keys)
228         {
229             super(binder, type, keys);
230             
231             Constructor JavaDoc<T> constructor = null;
232             try
233             {
234                 constructor = type.getConstructor(getTypes());
235             }
236             catch (NoSuchMethodException JavaDoc e)
237             {
238                 if (binder == null)
239                 {
240                     throw new IllegalArgumentException JavaDoc("no such constructor", e);
241                 }
242                 else
243                 {
244                     binder.addError(e);
245                 }
246             }
247             
248             this.constructor = constructor;
249         }
250         
251         public T get(Injector injector)
252         {
253             try
254             {
255                 return constructor.newInstance(getValues(injector));
256             }
257             catch (InstantiationException JavaDoc e)
258             {
259                 throw new IllegalStateException JavaDoc(e);
260             }
261             catch (IllegalAccessException JavaDoc e)
262             {
263                 throw new IllegalStateException JavaDoc(e);
264             }
265             catch (InvocationTargetException JavaDoc e)
266             {
267                 throw new IllegalStateException JavaDoc(e);
268             }
269         }
270         
271         private final Constructor JavaDoc<T> constructor;
272     }
273     
274     private static class MethodBindingProvider<T> extends AbstractBindingProvider<T> {
275         
276         MethodBindingProvider(AbstractBindingProvider<T> prev,
277                               Class JavaDoc<T> type, String JavaDoc methodName, Key... keys)
278         {
279             super(prev.binder, type, keys);
280             
281             Method JavaDoc method = null;
282             try
283             {
284                 method = type.getMethod(methodName, getTypes());
285             }
286             catch (NoSuchMethodException JavaDoc e)
287             {
288                 if (binder == null)
289                 {
290                     throw new IllegalArgumentException JavaDoc("no such method", e);
291                 }
292                 else
293                 {
294                     binder.addError(e);
295                 }
296             }
297             
298             this.prev = prev;
299             this.method = method;
300         }
301         
302         public T get(Injector injector)
303         {
304             T target = prev.get(injector);
305             try
306             {
307                 method.invoke(target, getValues(injector));
308             }
309             catch (IllegalAccessException JavaDoc e)
310             {
311                 throw new IllegalStateException JavaDoc(e);
312             }
313             catch (InvocationTargetException JavaDoc e)
314             {
315                 throw new IllegalStateException JavaDoc(e);
316             }
317             return target;
318         }
319         
320         private final AbstractBindingProvider<T> prev;
321         private final Method JavaDoc method;
322     }
323
324     
325     private static class FactoryMethodBindingProvider<T> extends AbstractBindingProvider<T>
326     {
327         FactoryMethodBindingProvider(Binder binder, Class JavaDoc<T> providedType,
328                                      Key<?> factoryKey, String JavaDoc methodName, Key... keys)
329         {
330             super(binder, providedType, keys);
331             
332             Method JavaDoc method = null;
333             boolean isStaticFactory = false;
334             try
335             {
336                 @SuppressWarnings JavaDoc("unchecked")
337                 Class JavaDoc factoryType = (Class JavaDoc) factoryKey.getTypeLiteral().getType();
338                 method = factoryType.getMethod(methodName, getTypes());
339                 method.getReturnType().asSubclass(providedType);
340             }
341             catch (NoSuchMethodException JavaDoc e)
342             {
343                 if (binder == null)
344                 {
345                     throw new IllegalArgumentException JavaDoc("no such method", e);
346                 }
347                 else
348                 {
349                     binder.addError(e);
350                 }
351             }
352             catch (ClassCastException JavaDoc e)
353             {
354                 if (binder == null)
355                 {
356                     throw new IllegalArgumentException JavaDoc("bad return type", e);
357                 }
358                 else
359                 {
360                     binder.addError(e);
361                 }
362             }
363             
364             this.method = method;
365             this.factoryKey = factoryKey;
366         }
367         
368         public T get(Injector injector)
369         {
370             try
371             {
372                 Object JavaDoc target = null;
373                 if (!isStatic(method.getModifiers()))
374                 {
375                     target = injector.getInstance(factoryKey);
376                 }
377                 @SuppressWarnings JavaDoc("unchecked")
378                 T result = (T) method.invoke(target, getValues(injector));
379                 return result;
380             }
381             catch (IllegalAccessException JavaDoc e)
382             {
383                 throw new IllegalStateException JavaDoc(e);
384             }
385             catch (InvocationTargetException JavaDoc e)
386             {
387                 throw new IllegalStateException JavaDoc(e);
388             }
389         }
390         
391         private final Method JavaDoc method;
392         private final Key<?> factoryKey;
393     }
394 }
395
Popular Tags