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