1 package org.codehaus.mojo.jaxb2.shared.environment.classloading;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.plugin.logging.Log;
23 import org.codehaus.mojo.jaxb2.shared.Validate;
24
25 import java.io.File;
26 import java.io.IOException;
27 import java.io.UnsupportedEncodingException;
28 import java.net.MalformedURLException;
29 import java.net.URL;
30 import java.net.URLClassLoader;
31 import java.net.URLDecoder;
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.List;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 public class ThreadContextClassLoaderBuilder {
65
66
67 private ClassLoader originalClassLoader;
68 private List<URL> urlList;
69 private Log log;
70 private String encoding;
71
72 private ThreadContextClassLoaderBuilder(final ClassLoader classLoader, final Log aLog, final String encoding) {
73 log = aLog;
74 originalClassLoader = classLoader;
75 urlList = new ArrayList<URL>();
76 this.encoding = encoding;
77 }
78
79
80
81
82
83
84
85
86 public ThreadContextClassLoaderBuilder addURL(final URL anURL) {
87
88
89 Validate.notNull(anURL, "anURL");
90
91
92 for (URL current : urlList) {
93 if (current.toString().equalsIgnoreCase(anURL.toString())) {
94
95 if (log.isWarnEnabled()) {
96 log.warn("Not adding URL [" + anURL.toString() + "] twice. Check your plugin configuration.");
97 }
98
99
100 return this;
101 }
102 }
103
104
105 if (log.isDebugEnabled()) {
106 log.debug("Adding URL [" + anURL.toString() + "]");
107 }
108
109
110
111
112
113
114
115
116
117
118
119 urlList.add(addSlashToDirectoryUrlIfRequired(anURL));
120
121 return this;
122 }
123
124
125
126
127
128
129
130
131 public ThreadContextClassLoaderBuilder addPath(final String path) {
132
133
134 Validate.notEmpty(path, "path");
135
136
137 final URL anUrl;
138 try {
139 anUrl = new File(path).toURI().toURL();
140 } catch (MalformedURLException e) {
141 throw new IllegalArgumentException("Could not convert path [" + path + "] to an URL.", e);
142 }
143
144
145 return addURL(anUrl);
146 }
147
148
149
150
151
152
153
154
155 public ThreadContextClassLoaderBuilder addPaths(final List<String> paths) {
156
157
158 Validate.notNull(paths, "paths");
159
160
161 for (String path : paths) {
162 addPath(path);
163 }
164
165 return this;
166 }
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181 public ThreadContextClassLoaderHolder buildAndSet() {
182
183
184 final URL[] allURLs = new URL[urlList.size()];
185 urlList.toArray(allURLs);
186 final URLClassLoader classLoader = new URLClassLoader(allURLs, originalClassLoader);
187
188
189 final Thread currentThread = Thread.currentThread();
190 currentThread.setContextClassLoader(classLoader);
191
192
193 StringBuilder builder = new StringBuilder();
194 try {
195 for (URL current : Collections.list(classLoader.getResources(""))) {
196
197 final String toAppend = getClassPathElement(current, encoding);
198 if (toAppend != null) {
199 builder.append(toAppend).append(File.pathSeparator);
200 }
201 }
202 } catch (IOException e) {
203
204
205 currentThread.setContextClassLoader(originalClassLoader);
206 throw new IllegalStateException("Could not synthesize classPath from original ClassLoader.", e);
207 }
208
209 final String classPathString = builder.length() > 0
210 ? builder.toString().substring(0, builder.length() - File.pathSeparator.length())
211 : "";
212
213
214 return new DefaultHolder(currentThread, this.originalClassLoader, classPathString);
215 }
216
217
218
219
220
221
222
223
224
225
226
227
228 public static ThreadContextClassLoaderBuilder createFor(final ClassLoader classLoader,
229 final Log log,
230 final String encoding) {
231
232
233 Validate.notNull(classLoader, "classLoader");
234 Validate.notNull(log, "log");
235
236
237 return new ThreadContextClassLoaderBuilder(classLoader, log, encoding);
238 }
239
240
241
242
243
244
245
246
247
248
249 public static ThreadContextClassLoaderBuilder createFor(final Class<?> aClass,
250 final Log log,
251 final String encoding) {
252
253
254 Validate.notNull(aClass, "aClass");
255
256
257 return createFor(aClass.getClassLoader(), log, encoding);
258 }
259
260
261
262
263
264
265
266
267
268 public static String getClassPathElement(final URL anURL, final String encoding) throws IllegalArgumentException {
269
270
271 Validate.notNull(anURL, "anURL");
272
273 final String protocol = anURL.getProtocol();
274 String toReturn = null;
275
276 if ("file".equalsIgnoreCase(protocol)) {
277
278 final String originalPath = anURL.getPath();
279 try {
280 return URLDecoder.decode(anURL.getPath(), encoding);
281 } catch (UnsupportedEncodingException e) {
282 throw new IllegalArgumentException("Could not URLDecode path [" + originalPath
283 + "] using encoding [" + encoding + "]", e);
284 }
285 } else if ("jar".equalsIgnoreCase(protocol)) {
286 toReturn = anURL.getPath();
287 } else if ("http".equalsIgnoreCase(protocol) || "https".equalsIgnoreCase(protocol)) {
288 toReturn = anURL.toString();
289 } else {
290 throw new IllegalArgumentException("Unknown protocol [" + protocol + "]; could not handle URL ["
291 + anURL + "]");
292 }
293
294 return toReturn;
295 }
296
297
298
299
300
301 private URL addSlashToDirectoryUrlIfRequired(final URL anURL) {
302
303
304 Validate.notNull(anURL, "anURL");
305
306 URL toReturn = anURL;
307 if ("file".equalsIgnoreCase(anURL.getProtocol())) {
308
309 final File theFile = new File(anURL.getPath());
310 if (theFile.isDirectory()) {
311 try {
312
313
314
315
316 toReturn = theFile.toURI().toURL();
317 } catch (MalformedURLException e) {
318
319 throw new IllegalArgumentException("Could not convert a File to an URL", e);
320 }
321 }
322 }
323
324
325 return toReturn;
326 }
327
328
329
330
331
332
333 class DefaultHolder implements ThreadContextClassLoaderHolder {
334
335
336 private Thread affectedThread;
337 private ClassLoader originalClassLoader;
338 private String classPathArgument;
339
340 public DefaultHolder(final Thread affectedThread,
341 final ClassLoader originalClassLoader,
342 final String classPathArgument) {
343
344
345 Validate.notNull(affectedThread, "affectedThread");
346 Validate.notNull(originalClassLoader, "originalClassLoader");
347 Validate.notNull(classPathArgument, "classPathArgument");
348
349
350 this.affectedThread = affectedThread;
351 this.originalClassLoader = originalClassLoader;
352 this.classPathArgument = classPathArgument;
353 }
354
355
356
357
358 @Override
359 public void restoreClassLoaderAndReleaseThread() {
360 if (affectedThread != null) {
361
362
363 affectedThread.setContextClassLoader(originalClassLoader);
364
365
366 affectedThread = null;
367 originalClassLoader = null;
368 classPathArgument = null;
369 }
370 }
371
372
373
374
375 @Override
376 public String getClassPathAsArgument() {
377 return classPathArgument;
378 }
379
380
381
382
383 @Override
384 protected void finalize() throws Throwable {
385 try {
386
387 restoreClassLoaderAndReleaseThread();
388 } finally {
389
390 super.finalize();
391 }
392 }
393 }
394 }