1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mortbay.jetty.annotations;
17
18
19 import java.lang.reflect.Field;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Modifier;
22 import java.util.ArrayList;
23 import java.util.List;
24
25 import javax.annotation.Resource;
26 import javax.annotation.PostConstruct;
27 import javax.annotation.PreDestroy;
28 import javax.annotation.Resources;
29 import javax.annotation.security.RunAs;
30 import javax.naming.NameNotFoundException;
31 import javax.naming.NamingException;
32 import javax.servlet.Servlet;
33 import javax.transaction.UserTransaction;
34
35 import org.mortbay.jetty.plus.annotation.Injection;
36 import org.mortbay.jetty.plus.annotation.InjectionCollection;
37 import org.mortbay.jetty.plus.annotation.LifeCycleCallbackCollection;
38 import org.mortbay.jetty.plus.annotation.PostConstructCallback;
39 import org.mortbay.jetty.plus.annotation.PreDestroyCallback;
40 import org.mortbay.jetty.plus.annotation.RunAsCollection;
41 import org.mortbay.jetty.plus.naming.EnvEntry;
42 import org.mortbay.jetty.plus.naming.Transaction;
43 import org.mortbay.jetty.servlet.Holder;
44 import org.mortbay.jetty.servlet.ServletHolder;
45 import org.mortbay.log.Log;
46 import org.mortbay.util.IntrospectionUtil;
47 import org.mortbay.util.Loader;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public class AnnotationCollection
67 {
68 private Class _targetClass;
69 private List _methods = new ArrayList();
70 private List _fields = new ArrayList();
71 private List _classes = new ArrayList();
72 private static Class[] __envEntryTypes =
73 new Class[] {String.class, Character.class, Integer.class, Boolean.class, Double.class, Byte.class, Short.class, Long.class, Float.class};
74
75
76
77
78
79
80 public Class getTargetClass()
81 {
82 return _targetClass;
83 }
84
85
86
87
88
89 public void setTargetClass(Class clazz)
90 {
91 _targetClass=clazz;
92 }
93
94
95 public void addClass (Class clazz)
96 {
97 if (clazz.getDeclaredAnnotations().length==0)
98 return;
99 _classes.add(clazz);
100 }
101
102 public void addMethod (Method method)
103 {
104 if (method.getDeclaredAnnotations().length==0)
105 return;
106 _methods.add(method);
107 }
108
109 public void addField(Field field)
110 {
111 if (field.getDeclaredAnnotations().length==0)
112 return;
113 _fields.add(field);
114 }
115
116 public List getClasses()
117 {
118 return _classes;
119 }
120 public List getMethods ()
121 {
122 return _methods;
123 }
124
125
126 public List getFields()
127 {
128 return _fields;
129 }
130
131
132
133 public void processRunAsAnnotations (RunAsCollection runAsCollection)
134 {
135 for (int i=0; i<_classes.size();i++)
136 {
137 Class clazz = (Class)_classes.get(i);
138
139
140 if (Servlet.class.isAssignableFrom(clazz))
141 {
142 RunAs runAs = (RunAs)clazz.getAnnotation(RunAs.class);
143 if (runAs != null)
144 {
145 String role = runAs.value();
146 if (role != null)
147 {
148 org.mortbay.jetty.plus.annotation.RunAs ra = new org.mortbay.jetty.plus.annotation.RunAs();
149 ra.setTargetClass(clazz);
150 ra.setRoleName(role);
151 runAsCollection.add(ra);
152 }
153 }
154 }
155 }
156 }
157
158
159
160
161
162
163
164 public InjectionCollection processResourceAnnotations(InjectionCollection injections)
165 {
166 processClassResourceAnnotations();
167 processMethodResourceAnnotations(injections);
168 processFieldResourceAnnotations(injections);
169
170 return injections;
171 }
172
173
174
175
176
177
178 public LifeCycleCallbackCollection processLifeCycleCallbackAnnotations(LifeCycleCallbackCollection callbacks)
179 {
180 processPostConstructAnnotations(callbacks);
181 processPreDestroyAnnotations(callbacks);
182 return callbacks;
183 }
184
185
186
187
188
189
190
191 public void processResourcesAnnotations ()
192 {
193 for (int i=0; i<_classes.size();i++)
194 {
195 Class clazz = (Class)_classes.get(i);
196 Resources resources = (Resources)clazz.getAnnotation(Resources.class);
197 if (resources != null)
198 {
199 Resource[] resArray = resources.value();
200 if (resArray==null||resArray.length==0)
201 continue;
202
203 for (int j=0;j<resArray.length;j++)
204 {
205
206 String name = resArray[j].name();
207 String mappedName = resArray[j].mappedName();
208 Resource.AuthenticationType auth = resArray[j].authenticationType();
209 Class type = resArray[j].type();
210 boolean shareable = resArray[j].shareable();
211
212 if (name==null || name.trim().equals(""))
213 throw new IllegalStateException ("Class level Resource annotations must contain a name (Common Annotations Spec Section 2.3)");
214
215 try
216 {
217
218
219
220
221
222
223
224 org.mortbay.jetty.plus.naming.NamingEntryUtil.bindToENC(name, mappedName);
225
226
227
228
229
230
231
232 }
233 catch (NamingException e)
234 {
235 throw new IllegalStateException(e);
236 }
237 }
238 }
239 }
240 }
241
242
243
244
245
246
247
248 private void processClassResourceAnnotations ()
249 {
250 for (int i=0; i<_classes.size();i++)
251 {
252 Class clazz = (Class)_classes.get(i);
253 Resource resource = (Resource)clazz.getAnnotation(Resource.class);
254 if (resource != null)
255 {
256 String name = resource.name();
257 String mappedName = resource.mappedName();
258 Resource.AuthenticationType auth = resource.authenticationType();
259 Class type = resource.type();
260 boolean shareable = resource.shareable();
261
262 if (name==null || name.trim().equals(""))
263 throw new IllegalStateException ("Class level Resource annotations must contain a name (Common Annotations Spec Section 2.3)");
264
265 try
266 {
267
268
269
270
271
272 org.mortbay.jetty.plus.naming.NamingEntryUtil.bindToENC(name,mappedName);
273 }
274 catch (NamingException e)
275 {
276 throw new IllegalStateException(e);
277 }
278 }
279 }
280 }
281
282
283
284
285
286
287
288
289 private void processMethodResourceAnnotations(InjectionCollection webXmlInjections)
290 {
291
292 for (int i=0;i<_methods.size();i++)
293 {
294 Method m = (Method)_methods.get(i);
295 Resource resource = (Resource)m.getAnnotation(Resource.class);
296 if (resource != null)
297 {
298
299 if (Modifier.isStatic(m.getModifiers()))
300 throw new IllegalStateException(m+" cannot be static");
301
302
303
304 if (!IntrospectionUtil.isJavaBeanCompliantSetter(m))
305 throw new IllegalStateException(m+" is not a java bean compliant setter method");
306
307
308 String name = m.getName().substring(3);
309 name = name.substring(0,1).toLowerCase()+name.substring(1);
310 name = m.getDeclaringClass().getCanonicalName()+"/"+name;
311
312 name = (resource.name()!=null && !resource.name().trim().equals("")? resource.name(): name);
313
314 String mappedName = (resource.mappedName()!=null && !resource.mappedName().trim().equals("")?resource.mappedName():null);
315
316 Class type = m.getParameterTypes()[0];
317
318
319
320 Resource.AuthenticationType auth = resource.authenticationType();
321 boolean shareable = resource.shareable();
322
323
324 if ((resource.type() != null)
325 &&
326 !resource.type().equals(Object.class)
327 &&
328 (!IntrospectionUtil.isTypeCompatible(type, resource.type(), false)))
329 throw new IllegalStateException("@Resource incompatible type="+resource.type()+ " with method param="+type+ " for "+m);
330
331
332 Injection webXmlInjection = webXmlInjections.getInjection(getTargetClass(), m);
333 if (webXmlInjection == null)
334 {
335 try
336 {
337 org.mortbay.jetty.plus.naming.NamingEntryUtil.bindToENC(name, mappedName);
338
339 Log.debug("Bound "+(mappedName==null?name:mappedName) + " as "+ name);
340
341 Injection injection = new Injection();
342 injection.setTargetClass(getTargetClass());
343 injection.setJndiName(name);
344 injection.setMappingName(mappedName);
345 injection.setTarget(m);
346 webXmlInjections.add(injection);
347 }
348 catch (NamingException e)
349 {
350
351
352
353 if (!isEnvEntryType(type))
354 throw new IllegalStateException(e);
355 }
356 }
357 else
358 {
359
360
361 try
362 {
363 Object value = webXmlInjection.lookupInjectedValue();
364 if (!IntrospectionUtil.isTypeCompatible(type, value.getClass(), false))
365 throw new IllegalStateException("Type of field="+type+" is not compatible with Resource type="+value.getClass());
366 }
367 catch (NamingException e)
368 {
369 throw new IllegalStateException(e);
370 }
371 }
372 }
373 }
374 }
375
376
377
378
379
380
381
382
383
384 private void processFieldResourceAnnotations (InjectionCollection webXmlInjections)
385 {
386 for (int i=0;i<_fields.size();i++)
387 {
388 Field f = (Field)_fields.get(i);
389 Resource resource = (Resource)f.getAnnotation(Resource.class);
390 if (resource != null)
391 {
392
393 if (Modifier.isStatic(f.getModifiers()))
394 throw new IllegalStateException(f+" cannot be static");
395
396
397 if (Modifier.isFinal(f.getModifiers()))
398 throw new IllegalStateException(f+" cannot be final");
399
400
401 String name = f.getDeclaringClass().getCanonicalName()+"/"+f.getName();
402
403 name = (resource.name()!=null && !resource.name().trim().equals("")? resource.name(): name);
404
405
406 Class type = f.getType();
407
408 if ((resource.type() != null)
409 &&
410 !resource.type().equals(Object.class)
411 &&
412 (!IntrospectionUtil.isTypeCompatible(type, resource.type(), false)))
413 throw new IllegalStateException("@Resource incompatible type="+resource.type()+ " with field type ="+f.getType());
414
415
416 String mappedName = (resource.mappedName()!=null && !resource.mappedName().trim().equals("")?resource.mappedName():null);
417
418 Resource.AuthenticationType auth = resource.authenticationType();
419 boolean shareable = resource.shareable();
420 System.err.println("Name="+name+" mappedName="+mappedName);
421
422 Injection webXmlInjection = webXmlInjections.getInjection(getTargetClass(), f);
423 if (webXmlInjection == null)
424 {
425 try
426 {
427
428 org.mortbay.jetty.plus.naming.NamingEntryUtil.bindToENC(name, mappedName);
429
430 Log.debug("Bound "+(mappedName==null?name:mappedName) + " as "+ name);
431
432 Injection injection = new Injection();
433 injection.setTargetClass(getTargetClass());
434 injection.setJndiName(name);
435 injection.setMappingName(mappedName);
436 injection.setTarget(f);
437 webXmlInjections.add(injection);
438
439 }
440 catch (NamingException e)
441 {
442
443
444
445 if (!isEnvEntryType(type))
446 throw new IllegalStateException(e);
447 }
448 }
449 else
450 {
451
452
453 try
454 {
455 Object value = webXmlInjection.lookupInjectedValue();
456 if (!IntrospectionUtil.isTypeCompatible(type, value.getClass(), false))
457 throw new IllegalStateException("Type of field="+type+" is not compatible with Resource type="+value.getClass());
458 }
459 catch (NamingException e)
460 {
461 throw new IllegalStateException(e);
462 }
463 }
464 }
465 }
466 }
467
468
469
470
471
472
473
474
475
476
477
478
479
480 private void processPostConstructAnnotations (LifeCycleCallbackCollection callbacks)
481 {
482
483 for (int i=0; i<_methods.size(); i++)
484 {
485 Method m = (Method)_methods.get(i);
486 if (m.isAnnotationPresent(PostConstruct.class))
487 {
488 if (m.getParameterTypes().length != 0)
489 throw new IllegalStateException(m+" has parameters");
490 if (m.getReturnType() != Void.TYPE)
491 throw new IllegalStateException(m+" is not void");
492 if (m.getExceptionTypes().length != 0)
493 throw new IllegalStateException(m+" throws checked exceptions");
494 if (Modifier.isStatic(m.getModifiers()))
495 throw new IllegalStateException(m+" is static");
496
497
498 PostConstructCallback callback = new PostConstructCallback();
499 callback.setTargetClass(getTargetClass());
500 callback.setTarget(m);
501 callbacks.add(callback);
502 }
503 }
504 }
505
506
507
508
509
510
511
512
513
514
515
516 private void processPreDestroyAnnotations (LifeCycleCallbackCollection callbacks)
517 {
518
519
520 for (int i=0; i<_methods.size(); i++)
521 {
522 Method m = (Method)_methods.get(i);
523 if (m.isAnnotationPresent(PreDestroy.class))
524 {
525 if (m.getParameterTypes().length != 0)
526 throw new IllegalStateException(m+" has parameters");
527 if (m.getReturnType() != Void.TYPE)
528 throw new IllegalStateException(m+" is not void");
529 if (m.getExceptionTypes().length != 0)
530 throw new IllegalStateException(m+" throws checked exceptions");
531 if (Modifier.isStatic(m.getModifiers()))
532 throw new IllegalStateException(m+" is static");
533
534 PreDestroyCallback callback = new PreDestroyCallback();
535 callback.setTargetClass(getTargetClass());
536 callback.setTarget(m);
537 callbacks.add(callback);
538 }
539 }
540 }
541
542
543 private static boolean isEnvEntryType (Class type)
544 {
545 boolean result = false;
546 for (int i=0;i<__envEntryTypes.length && !result;i++)
547 {
548 result = (type.equals(__envEntryTypes[i]));
549 }
550 return result;
551 }
552
553 private static Class getNamingEntryType (Class type)
554 {
555 if (type==null)
556 return null;
557
558 if (isEnvEntryType(type))
559 return EnvEntry.class;
560
561 if (type.getName().equals("javax.transaction.UserTransaction"))
562 return Transaction.class;
563 else
564 return org.mortbay.jetty.plus.naming.Resource.class;
565 }
566 }