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