1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.annotations;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.net.URI;
24 import java.net.URL;
25 import java.net.URLClassLoader;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Set;
32 import java.util.jar.JarEntry;
33
34 import org.eclipse.jetty.util.Loader;
35 import org.eclipse.jetty.util.log.Log;
36 import org.eclipse.jetty.util.log.Logger;
37 import org.eclipse.jetty.util.resource.Resource;
38 import org.eclipse.jetty.webapp.JarScanner;
39 import org.objectweb.asm.AnnotationVisitor;
40 import org.objectweb.asm.ClassReader;
41 import org.objectweb.asm.FieldVisitor;
42 import org.objectweb.asm.MethodVisitor;
43 import org.objectweb.asm.commons.EmptyVisitor;
44
45
46
47
48
49
50
51 public class AnnotationParser
52 {
53 private static final Logger LOG = Log.getLogger(AnnotationParser.class);
54
55 protected Set<String> _parsedClassNames = new HashSet<String>();
56 protected List<Handler> _handlers = new ArrayList<Handler>();
57
58 public static String normalize (String name)
59 {
60 if (name==null)
61 return null;
62
63 if (name.startsWith("L") && name.endsWith(";"))
64 name = name.substring(1, name.length()-1);
65
66 if (name.endsWith(".class"))
67 name = name.substring(0, name.length()-".class".length());
68
69 return name.replace('/', '.');
70 }
71
72
73
74 public abstract class Value
75 {
76 String _name;
77
78 public Value (String name)
79 {
80 _name = name;
81 }
82
83 public String getName()
84 {
85 return _name;
86 }
87
88 public abstract Object getValue();
89
90 }
91
92
93
94
95 public class SimpleValue extends Value
96 {
97 Object _val;
98
99 public SimpleValue(String name)
100 {
101 super(name);
102 }
103
104 public void setValue(Object val)
105 {
106 _val=val;
107 }
108 public Object getValue()
109 {
110 return _val;
111 }
112
113 public String toString()
114 {
115 return "("+getName()+":"+_val+")";
116 }
117 }
118
119 public class ListValue extends Value
120 {
121 List<Value> _val;
122
123 public ListValue (String name)
124 {
125 super(name);
126 _val = new ArrayList<Value>();
127 }
128
129 public Object getValue()
130 {
131 return _val;
132 }
133
134 public List<Value> getList()
135 {
136 return _val;
137 }
138
139 public void addValue (Value v)
140 {
141 _val.add(v);
142 }
143
144 public int size ()
145 {
146 return _val.size();
147 }
148
149 public String toString()
150 {
151 StringBuffer buff = new StringBuffer();
152 buff.append("(");
153 buff.append(getName());
154 buff.append(":");
155 for (Value n: _val)
156 {
157 buff.append(" "+n.toString());
158 }
159 buff.append(")");
160
161 return buff.toString();
162 }
163 }
164
165
166
167
168
169
170
171
172 public interface Handler
173 {
174
175 }
176
177
178
179
180
181
182
183
184 public interface DiscoverableAnnotationHandler extends Handler
185 {
186
187
188
189
190
191
192
193
194
195
196
197 public void handleClass (String className, int version, int access,
198 String signature, String superName, String[] interfaces,
199 String annotation, List<Value>values);
200
201
202
203
204
205
206
207
208
209
210
211
212 public void handleMethod (String className, String methodName, int access,
213 String desc, String signature,String[] exceptions,
214 String annotation, List<Value>values);
215
216
217
218
219
220
221
222
223
224
225
226
227
228 public void handleField (String className, String fieldName, int access,
229 String fieldType, String signature, Object value,
230 String annotation, List<Value>values);
231
232
233
234
235
236
237
238 public String getAnnotationName();
239 }
240
241
242
243
244
245
246
247
248 public interface ClassHandler extends Handler
249 {
250 public void handle (String className, int version, int access, String signature, String superName, String[] interfaces);
251 }
252
253
254
255
256
257
258
259
260 public interface MethodHandler extends Handler
261 {
262 public void handle (String className, String methodName, int access, String desc, String signature,String[] exceptions);
263 }
264
265
266
267
268
269
270
271 public interface FieldHandler extends Handler
272 {
273 public void handle (String className, String fieldName, int access, String fieldType, String signature, Object value);
274 }
275
276
277
278
279
280
281
282
283 public class MyAnnotationVisitor implements AnnotationVisitor
284 {
285 List<Value> _annotationValues;
286 String _annotationName;
287
288 public MyAnnotationVisitor (String annotationName, List<Value> values)
289 {
290 _annotationValues = values;
291 _annotationName = annotationName;
292 }
293
294 public List<Value> getAnnotationValues()
295 {
296 return _annotationValues;
297 }
298
299
300
301
302
303 public void visit(String aname, Object avalue)
304 {
305 SimpleValue v = new SimpleValue(aname);
306 v.setValue(avalue);
307 _annotationValues.add(v);
308 }
309
310
311
312
313
314 public AnnotationVisitor visitAnnotation(String name, String desc)
315 {
316 String s = normalize(desc);
317 ListValue v = new ListValue(s);
318 _annotationValues.add(v);
319 MyAnnotationVisitor visitor = new MyAnnotationVisitor(s, v.getList());
320 return visitor;
321 }
322
323
324
325
326
327 public AnnotationVisitor visitArray(String name)
328 {
329 ListValue v = new ListValue(name);
330 _annotationValues.add(v);
331 MyAnnotationVisitor visitor = new MyAnnotationVisitor(null, v.getList());
332 return visitor;
333 }
334
335
336
337
338
339 public void visitEnum(String name, String desc, String value)
340 {
341
342 }
343
344 public void visitEnd()
345 {
346 }
347 }
348
349
350
351
352
353
354
355
356
357 public class MyClassVisitor extends EmptyVisitor
358 {
359 String _className;
360 int _access;
361 String _signature;
362 String _superName;
363 String[] _interfaces;
364 int _version;
365
366
367 public void visit (int version,
368 final int access,
369 final String name,
370 final String signature,
371 final String superName,
372 final String[] interfaces)
373 {
374 _className = normalize(name);
375 _access = access;
376 _signature = signature;
377 _superName = superName;
378 _interfaces = interfaces;
379 _version = version;
380
381 _parsedClassNames.add(_className);
382
383 String[] normalizedInterfaces = null;
384 if (interfaces!= null)
385 {
386 normalizedInterfaces = new String[interfaces.length];
387 int i=0;
388 for (String s : interfaces)
389 normalizedInterfaces[i++] = normalize(s);
390 }
391
392 for (Handler h : AnnotationParser.this._handlers)
393 {
394 if (h instanceof ClassHandler)
395 {
396 ((ClassHandler)h).handle(_className, _version, _access, _signature, normalize(_superName), normalizedInterfaces);
397 }
398 }
399 }
400
401 public AnnotationVisitor visitAnnotation (String desc, boolean visible)
402 {
403 MyAnnotationVisitor visitor = new MyAnnotationVisitor(normalize(desc), new ArrayList<Value>())
404 {
405 public void visitEnd()
406 {
407 super.visitEnd();
408
409
410 for (Handler h : AnnotationParser.this._handlers)
411 {
412 if (h instanceof DiscoverableAnnotationHandler)
413 {
414 DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
415 if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
416 dah.handleClass(_className, _version, _access, _signature, _superName, _interfaces, _annotationName, _annotationValues);
417 }
418 }
419 }
420 };
421
422 return visitor;
423 }
424
425 public MethodVisitor visitMethod (final int access,
426 final String name,
427 final String methodDesc,
428 final String signature,
429 final String[] exceptions)
430 {
431
432 return new EmptyVisitor ()
433 {
434 public AnnotationVisitor visitAnnotation(String desc, boolean visible)
435 {
436 MyAnnotationVisitor visitor = new MyAnnotationVisitor (normalize(desc), new ArrayList<Value>())
437 {
438 public void visitEnd()
439 {
440 super.visitEnd();
441
442 for (Handler h : AnnotationParser.this._handlers)
443 {
444 if (h instanceof DiscoverableAnnotationHandler)
445 {
446 DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
447 if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
448 dah.handleMethod(_className, name, access, methodDesc, signature, exceptions, _annotationName, _annotationValues);
449 }
450 }
451 }
452 };
453
454 return visitor;
455 }
456 };
457 }
458
459 public FieldVisitor visitField (final int access,
460 final String fieldName,
461 final String fieldType,
462 final String signature,
463 final Object value)
464 {
465
466 return new EmptyVisitor ()
467 {
468 public AnnotationVisitor visitAnnotation(String desc, boolean visible)
469 {
470 MyAnnotationVisitor visitor = new MyAnnotationVisitor(normalize(desc), new ArrayList<Value>())
471 {
472 public void visitEnd()
473 {
474 super.visitEnd();
475 for (Handler h : AnnotationParser.this._handlers)
476 {
477 if (h instanceof DiscoverableAnnotationHandler)
478 {
479 DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
480 if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
481 dah.handleField(_className, fieldName, access, fieldType, signature, value, _annotationName, _annotationValues);
482 }
483 }
484 }
485 };
486 return visitor;
487 }
488 };
489 }
490 }
491
492
493
494
495
496
497
498
499
500
501 public void registerAnnotationHandler (String annotationName, DiscoverableAnnotationHandler handler)
502 {
503 _handlers.add(handler);
504 }
505
506
507
508
509
510
511
512 public List<DiscoverableAnnotationHandler> getAnnotationHandlers(String annotationName)
513 {
514 List<DiscoverableAnnotationHandler> handlers = new ArrayList<DiscoverableAnnotationHandler>();
515 for (Handler h:_handlers)
516 {
517 if (h instanceof DiscoverableAnnotationHandler)
518 {
519 DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
520 if (annotationName.equals(dah.getAnnotationName()))
521 handlers.add(dah);
522 }
523 }
524
525 return handlers;
526 }
527
528
529
530
531
532 public List<DiscoverableAnnotationHandler> getAnnotationHandlers()
533 {
534 List<DiscoverableAnnotationHandler> allAnnotationHandlers = new ArrayList<DiscoverableAnnotationHandler>();
535 for (Handler h:_handlers)
536 {
537 if (h instanceof DiscoverableAnnotationHandler)
538 allAnnotationHandlers.add((DiscoverableAnnotationHandler)h);
539 }
540 return allAnnotationHandlers;
541 }
542
543
544
545
546
547 public void registerClassHandler (ClassHandler handler)
548 {
549 _handlers.add(handler);
550 }
551
552
553
554
555
556
557
558
559 public void registerHandler(Handler h)
560 {
561 if (h == null)
562 return;
563
564 _handlers.add(h);
565 }
566
567
568
569
570
571
572
573 public void registerHandlers(List<? extends Handler> handlers)
574 {
575 if (handlers == null)
576 return;
577 _handlers.addAll(handlers);
578 }
579
580
581
582
583
584
585
586
587 public boolean deregisterHandler(Handler h)
588 {
589 return _handlers.remove(h);
590 }
591
592
593
594
595
596 public void clearHandlers()
597 {
598 _handlers.clear();
599 }
600
601
602
603
604
605
606
607 public boolean isParsed (String className)
608 {
609 return _parsedClassNames.contains(className);
610 }
611
612
613
614
615
616
617
618
619
620
621 public void parse (String className, ClassNameResolver resolver)
622 throws Exception
623 {
624 if (className == null)
625 return;
626
627 if (!resolver.isExcluded(className))
628 {
629 if (!isParsed(className) || resolver.shouldOverride(className))
630 {
631 className = className.replace('.', '/')+".class";
632 URL resource = Loader.getResource(this.getClass(), className, false);
633 if (resource!= null)
634 {
635 Resource r = Resource.newResource(resource);
636 scanClass(r.getInputStream());
637 }
638 }
639 }
640 }
641
642
643
644
645
646
647
648
649
650
651
652 public void parse (Class clazz, ClassNameResolver resolver, boolean visitSuperClasses)
653 throws Exception
654 {
655 Class cz = clazz;
656 while (cz != null)
657 {
658 if (!resolver.isExcluded(cz.getName()))
659 {
660 if (!isParsed(cz.getName()) || resolver.shouldOverride(cz.getName()))
661 {
662 String nameAsResource = cz.getName().replace('.', '/')+".class";
663 URL resource = Loader.getResource(this.getClass(), nameAsResource, false);
664 if (resource!= null)
665 {
666 Resource r = Resource.newResource(resource);
667 scanClass(r.getInputStream());
668 }
669 }
670 }
671 if (visitSuperClasses)
672 cz = cz.getSuperclass();
673 else
674 cz = null;
675 }
676 }
677
678
679
680
681
682
683
684
685
686
687 public void parse (String[] classNames, ClassNameResolver resolver)
688 throws Exception
689 {
690 if (classNames == null)
691 return;
692
693 parse(Arrays.asList(classNames), resolver);
694 }
695
696
697
698
699
700
701
702
703
704 public void parse (List<String> classNames, ClassNameResolver resolver)
705 throws Exception
706 {
707 for (String s:classNames)
708 {
709 if ((resolver == null) || (!resolver.isExcluded(s) && (!isParsed(s) || resolver.shouldOverride(s))))
710 {
711 s = s.replace('.', '/')+".class";
712 URL resource = Loader.getResource(this.getClass(), s, false);
713 if (resource!= null)
714 {
715 Resource r = Resource.newResource(resource);
716 scanClass(r.getInputStream());
717 }
718 }
719 }
720 }
721
722
723
724
725
726
727
728
729
730 public void parse (Resource dir, ClassNameResolver resolver)
731 throws Exception
732 {
733 if (!dir.isDirectory() || !dir.exists() || dir.getName().startsWith("."))
734 return;
735
736
737 String[] files=dir.list();
738 for (int f=0;files!=null && f<files.length;f++)
739 {
740 try
741 {
742 Resource res = dir.addPath(files[f]);
743 if (res.isDirectory())
744 parse(res, resolver);
745 else
746 {
747 String fullname = res.getName();
748 String filename = res.getFile().getName();
749
750 if (isValidClassFileName(filename))
751 {
752 if ((resolver == null)|| (!resolver.isExcluded(fullname) && (!isParsed(fullname) || resolver.shouldOverride(fullname))))
753 {
754 Resource r = Resource.newResource(res.getURL());
755 scanClass(r.getInputStream());
756 }
757
758 }
759 }
760 }
761 catch (Exception ex)
762 {
763 LOG.warn(Log.EXCEPTION,ex);
764 }
765 }
766 }
767
768
769
770
771
772
773
774
775
776
777
778
779 public void parse (ClassLoader loader, boolean visitParents, boolean nullInclusive, final ClassNameResolver resolver)
780 throws Exception
781 {
782 if (loader==null)
783 return;
784
785 if (!(loader instanceof URLClassLoader))
786 return;
787
788 JarScanner scanner = new JarScanner()
789 {
790 public void processEntry(URI jarUri, JarEntry entry)
791 {
792 try
793 {
794
795 if (entry.isDirectory())
796 return;
797
798 String name = entry.getName();
799 if (isValidClassFilePath(name) && isValidClassFileName(name))
800 {
801 String shortName = name.replace('/', '.').substring(0,name.length()-6);
802 if ((resolver == null)
803 ||
804 (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
805 {
806
807 Resource clazz = Resource.newResource("jar:"+jarUri+"!/"+name);
808 scanClass(clazz.getInputStream());
809 }
810 }
811 }
812 catch (Exception e)
813 {
814 LOG.warn("Problem processing jar entry "+entry, e);
815 }
816 }
817
818 };
819
820 scanner.scan(null, loader, nullInclusive, visitParents);
821 }
822
823
824
825
826
827
828
829
830
831 public void parse (URI[] uris, final ClassNameResolver resolver)
832 throws Exception
833 {
834 if (uris==null)
835 return;
836
837 JarScanner scanner = new JarScanner()
838 {
839 public void processEntry(URI jarUri, JarEntry entry)
840 {
841 try
842 {
843
844 if (entry.isDirectory())
845 return;
846
847 String name = entry.getName();
848 if (isValidClassFilePath(name) && isValidClassFileName(name))
849 {
850 String shortName = name.replace('/', '.').substring(0,name.length()-6);
851
852 if ((resolver == null)
853 ||
854 (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
855 {
856 Resource clazz = Resource.newResource("jar:"+jarUri+"!/"+name);
857 scanClass(clazz.getInputStream());
858
859 }
860 }
861 }
862 catch (Exception e)
863 {
864 LOG.warn("Problem processing jar entry "+entry, e);
865 }
866 }
867
868 };
869 scanner.scan(null, uris, true);
870 }
871
872
873
874
875
876
877
878 public void parse (URI uri, final ClassNameResolver resolver)
879 throws Exception
880 {
881 if (uri == null)
882 return;
883 URI[] uris = {uri};
884 parse(uris, resolver);
885 }
886
887
888
889
890
891
892
893
894
895 protected void scanClass (InputStream is)
896 throws IOException
897 {
898 ClassReader reader = new ClassReader(is);
899 reader.accept(new MyClassVisitor(), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
900 }
901
902
903
904
905
906
907
908
909
910
911
912
913 private boolean isValidClassFileName (String name)
914 {
915
916 if (name == null || name.length()==0)
917 return false;
918
919
920 if (!name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
921 {
922 if (LOG.isDebugEnabled()) LOG.debug("Not a class: {}",name);
923 return false;
924 }
925
926
927 int c0 = 0;
928 int ldir = name.lastIndexOf('/', name.length()-6);
929 c0 = (ldir > -1 ? ldir+1 : c0);
930 if (!Character.isJavaIdentifierStart(name.charAt(c0)))
931 {
932 if (LOG.isDebugEnabled()) LOG.debug("Not a java identifier: {}"+name);
933 return false;
934 }
935
936 return true;
937 }
938
939
940
941
942
943
944
945
946
947 private boolean isValidClassFilePath (String path)
948 {
949
950 if (path == null || path.length()==0)
951 return false;
952
953
954
955 if (path.startsWith(".") || path.contains("/."))
956 {
957 if (LOG.isDebugEnabled()) LOG.debug("Contains hidden dirs: {}"+path);
958 return false;
959 }
960
961 return true;
962 }
963 }
964