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