1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.util;
19
20 import java.io.File;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.net.URL;
24 import java.security.AccessController;
25 import java.security.PrivilegedAction;
26 import java.util.Collection;
27 import java.util.Enumeration;
28 import java.util.concurrent.ConcurrentMap;
29 import java.util.jar.JarEntry;
30 import java.util.jar.JarFile;
31 import java.util.regex.Pattern;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.hadoop.classification.InterfaceAudience;
36 import org.apache.hadoop.conf.Configuration;
37 import org.apache.hadoop.fs.FileSystem;
38 import org.apache.hadoop.fs.Path;
39 import org.apache.hadoop.io.IOUtils;
40
41 import com.google.common.collect.MapMaker;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 @InterfaceAudience.Private
71 public class CoprocessorClassLoader extends ClassLoaderBase {
72 private static final Log LOG = LogFactory.getLog(CoprocessorClassLoader.class);
73
74
75
76 private static final String TMP_JARS_DIR = File.separator
77 + "jars" + File.separator + "tmp" + File.separator;
78
79
80
81
82
83
84
85 private static final ConcurrentMap<Path, CoprocessorClassLoader> classLoadersCache =
86 new MapMaker().concurrencyLevel(3).weakValues().makeMap();
87
88
89
90
91
92
93 private static final String[] CLASS_PREFIX_EXEMPTIONS = new String[] {
94
95 "com.sun.",
96 "launcher.",
97 "java.",
98 "javax.",
99 "org.ietf",
100 "org.omg",
101 "org.w3c",
102 "org.xml",
103 "sunw.",
104
105 "org.apache.commons.logging",
106 "org.apache.log4j",
107 "com.hadoop",
108
109 "org.apache.hadoop",
110 "org.apache.zookeeper",
111 };
112
113
114
115
116
117
118 private static final Pattern[] RESOURCE_LOAD_PARENT_FIRST_PATTERNS =
119 new Pattern[] {
120 Pattern.compile("^[^-]+-default\\.xml$")
121 };
122
123
124
125
126 private CoprocessorClassLoader(ClassLoader parent) {
127 super(parent);
128 }
129
130 private void init(Path path, String pathPrefix,
131 Configuration conf) throws IOException {
132 if (path == null) {
133 throw new IOException("The jar path is null");
134 }
135 if (!path.toString().endsWith(".jar")) {
136 throw new IOException(path.toString() + ": not a jar file?");
137 }
138
139
140 String parentDirPath =
141 conf.get(LOCAL_DIR_KEY, DEFAULT_LOCAL_DIR) + TMP_JARS_DIR;
142 File parentDir = new File(parentDirPath);
143 if (!parentDir.mkdirs() && !parentDir.isDirectory()) {
144 throw new RuntimeException("Failed to create local dir " + parentDir.getPath()
145 + ", CoprocessorClassLoader failed to init");
146 }
147
148 FileSystem fs = path.getFileSystem(conf);
149 File dst = new File(parentDir, "." + pathPrefix + "."
150 + path.getName() + "." + System.currentTimeMillis() + ".jar");
151 fs.copyToLocalFile(path, new Path(dst.toString()));
152 dst.deleteOnExit();
153
154 addURL(dst.getCanonicalFile().toURI().toURL());
155
156 JarFile jarFile = new JarFile(dst.toString());
157 try {
158 Enumeration<JarEntry> entries = jarFile.entries();
159 while (entries.hasMoreElements()) {
160 JarEntry entry = entries.nextElement();
161 if (entry.getName().matches("[/]?lib/[^/]+\\.jar")) {
162 File file = new File(parentDir, "." + pathPrefix + "." + path.getName()
163 + "." + System.currentTimeMillis() + "." + entry.getName().substring(5));
164 IOUtils.copyBytes(jarFile.getInputStream(entry), new FileOutputStream(file), conf, true);
165 file.deleteOnExit();
166 addURL(file.toURI().toURL());
167 }
168 }
169 } finally {
170 jarFile.close();
171 }
172 }
173
174
175 public static CoprocessorClassLoader getIfCached(final Path path) {
176 if (path == null) return null;
177 return classLoadersCache.get(path);
178 }
179
180
181 public static Collection<? extends ClassLoader> getAllCached() {
182 return classLoadersCache.values();
183 }
184
185
186 public static void clearCache() {
187 classLoadersCache.clear();
188 }
189
190
191
192
193
194
195
196
197
198
199
200
201 public static CoprocessorClassLoader getClassLoader(final Path path,
202 final ClassLoader parent, final String pathPrefix,
203 final Configuration conf) throws IOException {
204 CoprocessorClassLoader cl = getIfCached(path);
205 if (cl != null){
206 LOG.debug("Found classloader "+ cl + "for "+ path.toString());
207 return cl;
208 }
209
210 cl = AccessController.doPrivileged(new PrivilegedAction<CoprocessorClassLoader>() {
211 @Override
212 public CoprocessorClassLoader run() {
213 return new CoprocessorClassLoader(parent);
214 }
215 });
216
217 cl.init(path, pathPrefix, conf);
218
219
220 CoprocessorClassLoader prev = classLoadersCache.putIfAbsent(path, cl);
221 if (prev != null) {
222
223 cl = prev;
224 }
225 return cl;
226 }
227
228 @Override
229 public Class<?> loadClass(String name)
230 throws ClassNotFoundException {
231
232 if (isClassExempt(name)) {
233 if (LOG.isDebugEnabled()) {
234 LOG.debug("Skipping exempt class " + name +
235 " - delegating directly to parent");
236 }
237 return parent.loadClass(name);
238 }
239
240 synchronized (getClassLoadingLock(name)) {
241
242 Class<?> clasz = findLoadedClass(name);
243 if (clasz != null) {
244 if (LOG.isDebugEnabled()) {
245 LOG.debug("Class " + name + " already loaded");
246 }
247 }
248 else {
249 try {
250
251 if (LOG.isDebugEnabled()) {
252 LOG.debug("Finding class: " + name);
253 }
254 clasz = findClass(name);
255 } catch (ClassNotFoundException e) {
256
257 if (LOG.isDebugEnabled()) {
258 LOG.debug("Class " + name + " not found - delegating to parent");
259 }
260 try {
261 clasz = parent.loadClass(name);
262 } catch (ClassNotFoundException e2) {
263
264
265 if (LOG.isDebugEnabled()) {
266 LOG.debug("Class " + name + " not found in parent loader");
267 }
268 throw e2;
269 }
270 }
271 }
272 return clasz;
273 }
274 }
275
276 @Override
277 public URL getResource(String name) {
278 URL resource = null;
279 boolean parentLoaded = false;
280
281
282 if (loadResourceUsingParentFirst(name)) {
283 if (LOG.isDebugEnabled()) {
284 LOG.debug("Checking parent first for resource " + name);
285 }
286 resource = super.getResource(name);
287 parentLoaded = true;
288 }
289
290 if (resource == null) {
291 synchronized (getClassLoadingLock(name)) {
292
293 resource = findResource(name);
294 if ((resource == null) && !parentLoaded) {
295
296
297 resource = super.getResource(name);
298 }
299 }
300 }
301 return resource;
302 }
303
304
305
306
307
308
309
310
311 protected boolean isClassExempt(String name) {
312 for (String exemptPrefix : CLASS_PREFIX_EXEMPTIONS) {
313 if (name.startsWith(exemptPrefix)) {
314 return true;
315 }
316 }
317 return false;
318 }
319
320
321
322
323
324
325
326
327
328 protected boolean loadResourceUsingParentFirst(String name) {
329 for (Pattern resourcePattern : RESOURCE_LOAD_PARENT_FIRST_PATTERNS) {
330 if (resourcePattern.matcher(name).matches()) {
331 return true;
332 }
333 }
334 return false;
335 }
336 }