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
300 public static interface Handler
301 {
302 public void handle(ClassInfo classInfo);
303 public void handle(MethodInfo methodInfo);
304 public void handle (FieldInfo fieldInfo);
305 public void handle (ClassInfo info, String annotationName);
306 public void handle (MethodInfo info, String annotationName);
307 public void handle (FieldInfo info, String annotationName);
308 }
309
310
311
312
313
314
315
316
317
318 public static abstract class AbstractHandler implements Handler
319 {
320
321 @Override
322 public void handle(ClassInfo classInfo)
323 {
324
325 }
326
327 @Override
328 public void handle(MethodInfo methodInfo)
329 {
330
331 }
332
333 @Override
334 public void handle(FieldInfo fieldInfo)
335 {
336
337 }
338
339 @Override
340 public void handle(ClassInfo info, String annotationName)
341 {
342
343 }
344
345 @Override
346 public void handle(MethodInfo info, String annotationName)
347 {
348
349 }
350
351 @Override
352 public void handle(FieldInfo info, String annotationName)
353 {
354
355 }
356 }
357
358
359
360
361
362
363
364
365 public class MyMethodVisitor extends MethodVisitor
366 {
367 final MethodInfo _mi;
368 final Set<? extends Handler> _handlers;
369
370 public MyMethodVisitor(final Set<? extends Handler> handlers,
371 final ClassInfo classInfo,
372 final int access,
373 final String name,
374 final String methodDesc,
375 final String signature,
376 final String[] exceptions)
377 {
378 super(ASM_OPCODE_VERSION);
379 _handlers = handlers;
380 _mi = new MethodInfo(classInfo, name, access, methodDesc,signature, exceptions);
381 }
382
383
384
385
386
387
388
389 @Override
390 public AnnotationVisitor visitAnnotation(String desc, boolean visible)
391 {
392 String annotationName = normalize(desc);
393 for (Handler h:_handlers)
394 h.handle(_mi, annotationName);
395 return null;
396 }
397 }
398
399
400
401
402
403
404
405
406
407
408 public class MyFieldVisitor extends FieldVisitor
409 {
410 final FieldInfo _fieldInfo;
411 final Set<? extends Handler> _handlers;
412
413
414 public MyFieldVisitor(final Set<? extends Handler> handlers,
415 final ClassInfo classInfo,
416 final int access,
417 final String fieldName,
418 final String fieldType,
419 final String signature,
420 final Object value)
421 {
422 super(ASM_OPCODE_VERSION);
423 _handlers = handlers;
424 _fieldInfo = new FieldInfo(classInfo, fieldName, access, fieldType, signature, value);
425 }
426
427
428
429
430
431
432
433 @Override
434 public AnnotationVisitor visitAnnotation(String desc, boolean visible)
435 {
436 String annotationName = normalize(desc);
437 for (Handler h : _handlers)
438 h.handle(_fieldInfo, annotationName);
439
440 return null;
441 }
442 }
443
444
445
446
447
448
449
450
451
452 public class MyClassVisitor extends ClassVisitor
453 {
454
455 final Resource _containingResource;
456 final Set<? extends Handler> _handlers;
457 ClassInfo _ci;
458
459 public MyClassVisitor(Set<? extends Handler> handlers, Resource containingResource)
460 {
461 super(ASM_OPCODE_VERSION);
462 _handlers = handlers;
463 _containingResource = containingResource;
464 }
465
466
467 @Override
468 public void visit (final int version,
469 final int access,
470 final String name,
471 final String signature,
472 final String superName,
473 final String[] interfaces)
474 {
475 _ci = new ClassInfo(_containingResource, normalize(name), version, access, signature, normalize(superName), normalize(interfaces));
476
477 _parsedClassNames.add(_ci.getClassName());
478
479 for (Handler h:_handlers)
480 h.handle(_ci);
481 }
482
483
484
485
486
487
488
489 @Override
490 public AnnotationVisitor visitAnnotation (String desc, boolean visible)
491 {
492 String annotationName = normalize(desc);
493 for (Handler h : _handlers)
494 h.handle(_ci, annotationName);
495
496 return null;
497 }
498
499
500
501
502
503
504
505 @Override
506 public MethodVisitor visitMethod (final int access,
507 final String name,
508 final String methodDesc,
509 final String signature,
510 final String[] exceptions)
511 {
512
513 return new MyMethodVisitor(_handlers, _ci, access, name, methodDesc, signature, exceptions);
514 }
515
516
517
518
519
520
521 @Override
522 public FieldVisitor visitField (final int access,
523 final String fieldName,
524 final String fieldType,
525 final String signature,
526 final Object value)
527 {
528 return new MyFieldVisitor(_handlers, _ci, access, fieldName, fieldType, signature, value);
529 }
530 }
531
532
533
534
535
536
537
538 public boolean isParsed (String className)
539 {
540 return _parsedClassNames.contains(className);
541 }
542
543
544
545
546
547
548
549
550
551
552 public void parse (Set<? extends Handler> handlers, String className, ClassNameResolver resolver)
553 throws Exception
554 {
555 if (className == null)
556 return;
557
558 if (!resolver.isExcluded(className))
559 {
560 if (!isParsed(className) || resolver.shouldOverride(className))
561 {
562 className = className.replace('.', '/')+".class";
563 URL resource = Loader.getResource(this.getClass(), className);
564 if (resource!= null)
565 {
566 Resource r = Resource.newResource(resource);
567 scanClass(handlers, null, r.getInputStream());
568 }
569 }
570 }
571 }
572
573
574
575
576
577
578
579
580
581
582
583 public void parse (Set<? extends Handler> handlers, Class<?> clazz, ClassNameResolver resolver, boolean visitSuperClasses)
584 throws Exception
585 {
586 Class<?> cz = clazz;
587 while (cz != null)
588 {
589 if (!resolver.isExcluded(cz.getName()))
590 {
591 if (!isParsed(cz.getName()) || resolver.shouldOverride(cz.getName()))
592 {
593 String nameAsResource = cz.getName().replace('.', '/')+".class";
594 URL resource = Loader.getResource(this.getClass(), nameAsResource);
595 if (resource!= null)
596 {
597 Resource r = Resource.newResource(resource);
598 scanClass(handlers, null, r.getInputStream());
599 }
600 }
601 }
602
603 if (visitSuperClasses)
604 cz = cz.getSuperclass();
605 else
606 cz = null;
607 }
608 }
609
610
611
612
613
614
615
616
617
618
619 public void parse (Set<? extends Handler> handlers, String[] classNames, ClassNameResolver resolver)
620 throws Exception
621 {
622 if (classNames == null)
623 return;
624
625 parse(handlers, Arrays.asList(classNames), resolver);
626 }
627
628
629
630
631
632
633
634
635
636 public void parse (Set<? extends Handler> handlers, List<String> classNames, ClassNameResolver resolver)
637 throws Exception
638 {
639 MultiException me = new MultiException();
640
641 for (String s:classNames)
642 {
643 try
644 {
645 if ((resolver == null) || (!resolver.isExcluded(s) && (!isParsed(s) || resolver.shouldOverride(s))))
646 {
647 s = s.replace('.', '/')+".class";
648 URL resource = Loader.getResource(this.getClass(), s);
649 if (resource!= null)
650 {
651 Resource r = Resource.newResource(resource);
652 scanClass(handlers, null, r.getInputStream());
653 }
654 }
655 }
656 catch (Exception e)
657 {
658 me.add(new RuntimeException("Error scanning class "+s, e));
659 }
660 }
661 me.ifExceptionThrow();
662 }
663
664
665
666
667
668
669
670
671
672 protected void parseDir (Set<? extends Handler> handlers, Resource dir, ClassNameResolver resolver)
673 throws Exception
674 {
675
676 if (!dir.isDirectory() || !dir.exists() || dir.getName().startsWith("."))
677 return;
678
679 if (LOG.isDebugEnabled()) {LOG.debug("Scanning dir {}", dir);};
680
681 MultiException me = new MultiException();
682
683 String[] files=dir.list();
684 for (int f=0;files!=null && f<files.length;f++)
685 {
686 Resource res = dir.addPath(files[f]);
687 if (res.isDirectory())
688 parseDir(handlers, res, resolver);
689 else
690 {
691
692 File file = res.getFile();
693 if (isValidClassFileName((file==null?null:file.getName())))
694 {
695 try
696 {
697 String name = res.getName();
698 if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
699 {
700 Resource r = Resource.newResource(res.getURL());
701 if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);};
702 scanClass(handlers, dir, r.getInputStream());
703 }
704 }
705 catch (Exception ex)
706 {
707 if (LOG.isDebugEnabled()) LOG.debug("Error scanning file "+files[f], ex);
708 me.add(new RuntimeException("Error scanning file "+files[f],ex));
709 }
710 }
711 else
712 {
713 if (LOG.isDebugEnabled()) LOG.debug("Skipping scan on invalid file {}", res);
714 }
715 }
716 }
717
718 me.ifExceptionThrow();
719 }
720
721
722
723
724
725
726
727
728
729
730
731
732 public void parse (final Set<? extends Handler> handlers, ClassLoader loader, boolean visitParents, boolean nullInclusive, final ClassNameResolver resolver)
733 throws Exception
734 {
735 if (loader==null)
736 return;
737
738 if (!(loader instanceof URLClassLoader))
739 return;
740
741 final MultiException me = new MultiException();
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 me.add(new RuntimeException("Error parsing entry "+entry.getName()+" from jar "+ jarUri, e));
755 }
756 }
757
758 };
759
760 scanner.scan(null, loader, nullInclusive, visitParents);
761 me.ifExceptionThrow();
762 }
763
764
765
766
767
768
769
770
771
772 public void parse (final Set<? extends Handler> handlers, final URI[] uris, final ClassNameResolver resolver)
773 throws Exception
774 {
775 if (uris==null)
776 return;
777
778 MultiException me = new MultiException();
779
780 for (URI uri:uris)
781 {
782 try
783 {
784 parse(handlers, uri, resolver);
785 }
786 catch (Exception e)
787 {
788 me.add(new RuntimeException("Problem parsing classes from "+ uri, e));
789 }
790 }
791 me.ifExceptionThrow();
792 }
793
794
795
796
797
798
799
800 public void parse (final Set<? extends Handler> handlers, URI uri, final ClassNameResolver resolver)
801 throws Exception
802 {
803 if (uri == null)
804 return;
805
806 parse (handlers, Resource.newResource(uri), resolver);
807 }
808
809
810
811
812
813
814
815
816 public void parse (final Set<? extends Handler> handlers, Resource r, final ClassNameResolver resolver)
817 throws Exception
818 {
819 if (r == null)
820 return;
821
822 if (r.exists() && r.isDirectory())
823 {
824 parseDir(handlers, r, resolver);
825 return;
826 }
827
828 String fullname = r.toString();
829 if (fullname.endsWith(".jar"))
830 {
831 parseJar(handlers, r, resolver);
832 return;
833 }
834
835 if (fullname.endsWith(".class"))
836 {
837 scanClass(handlers, null, r.getInputStream());
838 return;
839 }
840
841 if (LOG.isDebugEnabled()) LOG.warn("Resource not scannable for classes: {}", r);
842 }
843
844
845
846
847
848
849
850
851
852
853
854 protected void parseJar (Set<? extends Handler> handlers, Resource jarResource, final ClassNameResolver resolver)
855 throws Exception
856 {
857 if (jarResource == null)
858 return;
859
860 if (jarResource.toString().endsWith(".jar"))
861 {
862 if (LOG.isDebugEnabled()) {LOG.debug("Scanning jar {}", jarResource);};
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
895 InputStream in = jarResource.getInputStream();
896 if (in==null)
897 return;
898
899 MultiException me = new MultiException();
900
901 JarInputStream jar_in = new JarInputStream(in);
902 try
903 {
904 JarEntry entry = jar_in.getNextJarEntry();
905 while (entry!=null)
906 {
907 try
908 {
909 parseJarEntry(handlers, jarResource, entry, resolver);
910 }
911 catch (Exception e)
912 {
913 me.add(new RuntimeException("Error scanning entry "+entry.getName()+" from jar "+jarResource, e));
914 }
915 entry = jar_in.getNextJarEntry();
916 }
917 }
918 finally
919 {
920 jar_in.close();
921 }
922 me.ifExceptionThrow();
923 }
924 }
925
926
927
928
929
930
931
932
933 protected void parseJarEntry (Set<? extends Handler> handlers, Resource jar, JarEntry entry, final ClassNameResolver resolver)
934 throws Exception
935 {
936 if (jar == null || entry == null)
937 return;
938
939
940 if (entry.isDirectory())
941 return;
942
943 String name = entry.getName();
944
945
946 if (isValidClassFileName(name) && isValidClassFilePath(name))
947 {
948 String shortName = name.replace('/', '.').substring(0,name.length()-6);
949
950 if ((resolver == null)
951 ||
952 (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
953 {
954 Resource clazz = Resource.newResource("jar:"+jar.getURI()+"!/"+name);
955 if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);};
956 scanClass(handlers, jar, clazz.getInputStream());
957 }
958 }
959 }
960
961
962
963
964
965
966
967
968
969
970 protected void scanClass (Set<? extends Handler> handlers, Resource containingResource, InputStream is)
971 throws IOException
972 {
973 ClassReader reader = new ClassReader(is);
974 reader.accept(new MyClassVisitor(handlers, containingResource), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
975 }
976
977
978
979
980
981
982
983
984
985
986
987
988 private boolean isValidClassFileName (String name)
989 {
990
991 if (name == null || name.length()==0)
992 return false;
993
994
995 if (!name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
996 {
997 if (LOG.isDebugEnabled()) LOG.debug("Not a class: {}",name);
998 return false;
999 }
1000
1001
1002 int c0 = 0;
1003 int ldir = name.lastIndexOf('/', name.length()-6);
1004 c0 = (ldir > -1 ? ldir+1 : c0);
1005 if (!Character.isJavaIdentifierStart(name.charAt(c0)))
1006 {
1007 if (LOG.isDebugEnabled()) LOG.debug("Not a java identifier: {}"+name);
1008 return false;
1009 }
1010
1011 return true;
1012 }
1013
1014
1015
1016
1017
1018
1019
1020
1021 private boolean isValidClassFilePath (String path)
1022 {
1023
1024 if (path == null || path.length()==0)
1025 return false;
1026
1027
1028 if (path.startsWith(".") || path.contains("/."))
1029 {
1030 if (LOG.isDebugEnabled()) LOG.debug("Contains hidden dirs: {}"+path);
1031 return false;
1032 }
1033
1034 return true;
1035 }
1036 }
1037