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