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