View Javadoc

1   // ========================================================================
2   // Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.plus.annotation;
15  
16  import java.lang.reflect.Field;
17  import java.lang.reflect.Member;
18  import java.lang.reflect.Method;
19  
20  import javax.naming.InitialContext;
21  import javax.naming.NamingException;
22  
23  import org.eclipse.jetty.util.IntrospectionUtil;
24  import org.eclipse.jetty.util.Loader;
25  import org.eclipse.jetty.util.TypeUtil;
26  import org.eclipse.jetty.util.log.Log;
27  
28  /**
29   * Injection
30   *
31   * Represents the injection of a resource into a target (method or field).
32   * The injection is performed by doing an ENC lookup using the jndi
33   * name provided, and setting the object obtained on the target.
34   *
35   */
36  public class Injection
37  {
38      private Class _targetClass;
39      private String _jndiName;
40      private String _mappingName;
41      private Member _target;
42      private String _className;
43      private String _fieldName;
44      private String _methodName;
45      private String _paramCanonicalName;
46      private String _annotationResourceType;
47      
48      
49      public Injection ()
50      {}
51      
52  
53      /**
54       * @return the _className
55       */
56      public Class getTargetClass()
57      {
58          return _targetClass;
59      }
60  
61      
62      public String getTargetClassName()
63      {
64          return _className;
65      }
66  
67      public String getFieldName ()
68      {
69          return _fieldName;
70      }
71      
72      public String getMethodName ()
73      {
74          return _methodName;
75      }
76      
77      public String getParamCanonicalName ()
78      {
79          return _paramCanonicalName;
80      }
81      
82      public boolean isField ()
83      {
84          return (_fieldName != null);
85      }
86      
87      public boolean isMethod ()
88      {
89          return (_methodName != null);
90      }
91      
92      /**
93       * @return the jndiName
94       */
95      public String getJndiName()
96      {
97          return _jndiName;
98      }
99      /**
100      * @param jndiName the jndiName to set
101      */
102     public void setJndiName(String jndiName)
103     {
104         this._jndiName = jndiName;
105     }
106     /**
107      * @return the mappingName
108      */
109     public String getMappingName()
110     {
111         return _mappingName;
112     }
113     /**
114      * @param mappingName the mappingName to set
115      */
116     public void setMappingName(String mappingName)
117     {
118         this._mappingName = mappingName;
119     }
120     
121     /**
122      * @return the target
123      */
124     public Member getTarget()
125     {
126         return _target;
127     }
128     
129  
130  
131     /**
132      * Set up an injection target that is a field
133      * @param className
134      * @param fieldName
135      */
136     public void setTarget (String className, String fieldName, String annotationResourceType)
137     {
138         _className = className;
139         _fieldName = fieldName;
140         _annotationResourceType = annotationResourceType;
141     }
142     
143     public void setTarget (String className, String methodName, String paramCanonicalName, String annotationResourceType)
144     {
145         _className = className;
146         _methodName = methodName;
147         _paramCanonicalName = paramCanonicalName;
148         _annotationResourceType = annotationResourceType;
149     }
150     
151     
152     public void setTarget (Class clazz, String targetName, Class targetType)
153     {
154         //first look for a javabeans style setter matching the targetName
155         String setter = "set"+targetName.substring(0,1).toUpperCase()+targetName.substring(1);
156         try
157         {
158             Log.debug("Looking for method for setter: "+setter+" with arg "+targetType);
159             _target = IntrospectionUtil.findMethod(clazz, setter, new Class[] {targetType}, true, false); 
160             _targetClass = clazz;
161             _className = clazz.getCanonicalName();
162             _methodName = targetName;
163             _paramCanonicalName = targetType.getCanonicalName();
164         }
165         catch (NoSuchMethodException me)
166         {
167             //try as a field
168             try
169             {
170                 _target = IntrospectionUtil.findField(clazz, targetName, targetType, true, false);
171                 _targetClass = clazz;   
172                 _className = clazz.getCanonicalName();
173                 _fieldName = targetName;
174             }
175             catch (NoSuchFieldException fe)
176             {
177                 throw new IllegalArgumentException("No such field or method "+targetName+" on class "+_targetClass);
178             }
179         }
180     }
181 
182     
183     /**
184      * Inject a value for a Resource from JNDI into an object
185      * @param injectable
186      */
187     public void inject (Object injectable)
188     { 
189         try
190         {
191             if (_target == null)
192                 loadField();
193 
194             if (_target == null)
195                 loadMethod();
196         }
197         catch (Exception e)
198         {
199             throw new IllegalStateException (e);
200         }
201 
202         if (_target != null)
203         {
204             if (_target instanceof Field)
205                 injectField((Field)_target, injectable);
206             else
207                 injectMethod((Method)_target, injectable);
208         }
209         else
210             throw new IllegalStateException ("No method or field to inject with "+getJndiName());
211     }
212 
213     
214     /**
215      * The Resource must already exist in the ENC of this webapp.
216      * @return the injected valud
217      * @throws NamingException
218      */
219     public Object lookupInjectedValue ()
220     throws NamingException
221     {
222         InitialContext context = new InitialContext();
223         return context.lookup("java:comp/env/"+getJndiName());
224     }
225     
226     
227 
228     /**
229      * Inject value from jndi into a field of an instance
230      * @param field
231      * @param injectable
232      */
233     protected void injectField (Field field, Object injectable)
234     {   
235         if (validateInjection())
236         {
237             try
238             {
239                 boolean accessibility = field.isAccessible();
240                 field.setAccessible(true);
241                 field.set(injectable, lookupInjectedValue());
242                 field.setAccessible(accessibility);
243             }
244             catch (Exception e)
245             {
246                 Log.warn(e);
247                 throw new IllegalStateException("Inject failed for field "+field.getName());
248             }
249         }
250         else
251             throw new IllegalStateException ("Invalid injection for "+_className+"."+_fieldName);
252     }
253 
254     /**
255      * Inject value from jndi into a setter method of an instance
256      * @param method
257      * @param injectable
258      */
259     protected void injectMethod (Method method, Object injectable)
260     {
261         if (validateInjection())
262         {
263             try
264             {
265                 boolean accessibility = method.isAccessible();
266                 method.setAccessible(true);
267                 method.invoke(injectable, new Object[] {lookupInjectedValue()});
268                 method.setAccessible(accessibility);
269             }
270             catch (Exception e)
271             {
272                 Log.warn(e);
273                 throw new IllegalStateException("Inject failed for method "+method.getName());
274             }
275         }
276         else
277         throw new IllegalStateException("Invalid injection for "+_className+"."+_methodName);
278     }
279 
280 
281 
282     protected void loadField()
283     throws ClassNotFoundException, NoSuchFieldException
284     {
285         if (_fieldName == null || _className == null)
286             return;
287         
288         if (_targetClass == null)
289             _targetClass = Loader.loadClass(null, _className);
290         
291         _target = _targetClass.getDeclaredField(_fieldName);
292     }
293     
294     
295     /**
296      * Load the target class and method.
297      * A valid injection target method only has 1 argument.
298      * @throws ClassNotFoundException
299      * @throws NoSuchMethodException
300      */
301     protected void loadMethod ()
302     throws ClassNotFoundException, NoSuchMethodException
303     {
304         if (_methodName == null || _className == null)
305             return;
306 
307         if (_targetClass == null)
308             _targetClass = Loader.loadClass(null, _className);
309           
310         Class arg =  TypeUtil.fromName(_paramCanonicalName);
311         
312         if (arg == null)
313             arg = Loader.loadClass(null, _paramCanonicalName);
314       
315         _target = _targetClass.getDeclaredMethod(_methodName, new Class[] {arg}); 
316     }
317     
318     
319     private boolean validateInjection ()
320     {
321    
322         //check that if the injection came from an annotation, the type specified in the annotation
323         //is compatible with the field or method to inject
324         //JavaEE spec sec 5.2.4
325         if (_annotationResourceType != null)
326         {
327             if (_target == null)
328                 return false;
329             
330             try
331             {
332                 Class<?> annotationType = TypeUtil.fromName(_annotationResourceType);
333                 if (annotationType == null)
334                     annotationType = Loader.loadClass(null, _annotationResourceType);
335 
336                 if (_target instanceof Field)
337                 {
338                     return ((Field)_target).getType().isAssignableFrom(annotationType);
339                 }
340                 else if (_target instanceof Method)
341                 {
342                     Class<?>[] args = ((Method)_target).getParameterTypes();
343                     return args[0].isAssignableFrom(annotationType);
344                 }
345 
346                 return false;
347             }
348             catch (Exception e)
349             {
350                 Log.warn("Unable to verify injection for "+_className+"."+ (_fieldName==null?_methodName:_fieldName));
351                 return false;
352             }
353         }
354         else
355             return true;
356     }
357 }