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