1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.core.api.util;
17
18 import com.google.common.io.ByteStreams;
19 import com.google.common.io.Closeables;
20 import org.springframework.core.DecoratingClassLoader;
21 import org.springframework.instrument.classloading.WeavingTransformer;
22 import org.springframework.util.StringUtils;
23
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.lang.instrument.ClassFileTransformer;
27 import java.net.URL;
28 import java.util.Enumeration;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.Map;
32 import java.util.Set;
33
34
35
36
37
38
39
40
41
42
43 public class ShadowingInstrumentableClassLoader extends DecoratingClassLoader {
44
45 private static final String[] DEFAULT_EXCLUDES = new String[] {
46 "java.", "javax.", "sun.", "oracle.", "com.sun.", "com.ibm.", "COM.ibm.", "org.w3c.", "org.xml.",
47 "org.dom4j.", "org.eclipse", "org.aspectj.", "net.sf.cglib", "org.springframework.cglib",
48 "org.apache.xerces.", "org.apache.commons.logging."
49 };
50
51 private final ClassLoader enclosingClassLoader;
52 private final WeavingTransformer weavingTransformer;
53
54 private final Map<String, Class<?>> classCache = new HashMap<String, Class<?>>();
55 private final Set<String> attempted = new HashSet<String>();
56
57 public ShadowingInstrumentableClassLoader(ClassLoader enclosingClassLoader) {
58 this(enclosingClassLoader, null);
59 }
60
61 public ShadowingInstrumentableClassLoader(ClassLoader enclosingClassLoader, String[] excludedPackages) {
62 if (enclosingClassLoader == null) {
63 throw new IllegalArgumentException("Enclosing ClassLoader must not be null");
64 }
65 this.weavingTransformer = new WeavingTransformer(this);
66 this.enclosingClassLoader = enclosingClassLoader;
67 for (String defaultExcludedPackage : DEFAULT_EXCLUDES) {
68 excludePackage(defaultExcludedPackage);
69 }
70 if (excludedPackages != null) {
71 for (String excludedPackage : excludedPackages) {
72 excludePackage(excludedPackage);
73 }
74 }
75 }
76
77 public void addTransformer(ClassFileTransformer transformer) {
78 weavingTransformer.addTransformer(transformer);
79 }
80
81 @Override
82 public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
83 Class<?> cls = this.classCache.get(name);
84 if (cls != null) {
85 return cls;
86 }
87 if (!attempted.contains(name) && shouldInstrument(name)) {
88 return loadAndInstrumentClass(name);
89 }
90 else {
91 return this.enclosingClassLoader.loadClass(name);
92 }
93 }
94
95
96
97
98
99
100
101
102 private boolean shouldInstrument(String className) {
103 return !className.equals(getClass().getName()) && !isExcluded(className);
104 }
105
106 private Class<?> loadAndInstrumentClass(String name) throws ClassNotFoundException {
107 String internalName = StringUtils.replace(name, ".", "/") + ".class";
108 attempted.add(name);
109 attempted.add(internalName);
110 InputStream is = this.enclosingClassLoader.getResourceAsStream(internalName);
111 if (is == null) {
112 throw new ClassNotFoundException(name);
113 }
114 byte[] bytes;
115 try {
116 bytes = ByteStreams.toByteArray(is);
117 } catch (IOException e) {
118 throw new ClassNotFoundException("Cannot load resource for class [" + name + "]", e);
119 } finally {
120 Closeables.closeQuietly(is);
121 }
122 bytes = weavingTransformer.transformIfNecessary(name, bytes);
123 Class<?> cls = defineClass(name, bytes, 0, bytes.length);
124
125 if (cls.getPackage() == null) {
126 int packageSeparator = name.lastIndexOf('.');
127 if (packageSeparator != -1) {
128 String packageName = name.substring(0, packageSeparator);
129 definePackage(packageName, null, null, null, null, null, null, null);
130 }
131 }
132 this.classCache.put(name, cls);
133 return cls;
134 }
135
136 @Override
137 public URL getResource(String name) {
138 return this.enclosingClassLoader.getResource(name);
139 }
140
141 @Override
142 public InputStream getResourceAsStream(String name) {
143 return this.enclosingClassLoader.getResourceAsStream(name);
144 }
145
146 @Override
147 public Enumeration<URL> getResources(String name) throws IOException {
148 return this.enclosingClassLoader.getResources(name);
149 }
150
151 }