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 (Exception e) {
203
204 currentThread.setContextClassLoader(originalClassLoader);
205 throw new IllegalStateException("Could not synthesize classpath from original classloader.", e);
206 }
207
208 final String classPathString = builder.length() > 0
209 ? builder.toString().substring(0, builder.length() - File.pathSeparator.length())
210 : "";
211
212
213 return new DefaultHolder(currentThread, this.originalClassLoader, classPathString);
214 }
215
216
217
218
219
220
221
222
223
224
225
226
227 public static ThreadContextClassLoaderBuilder createFor(final ClassLoader classLoader,
228 final Log log,
229 final String encoding) {
230
231
232 Validate.notNull(classLoader, "classLoader");
233 Validate.notNull(log, "log");
234
235
236 return new ThreadContextClassLoaderBuilder(classLoader, log, encoding);
237 }
238
239
240
241
242
243
244
245
246
247
248 public static ThreadContextClassLoaderBuilder createFor(final Class<?> aClass,
249 final Log log,
250 final String encoding) {
251
252
253 Validate.notNull(aClass, "aClass");
254
255
256 return createFor(aClass.getClassLoader(), log, encoding);
257 }
258
259
260
261
262
263
264
265
266
267 public static String getClassPathElement(final URL anURL, final String encoding) throws IllegalArgumentException {
268
269
270 Validate.notNull(anURL, "anURL");
271
272 final String protocol = anURL.getProtocol();
273 String toReturn = null;
274
275 if ("file".equalsIgnoreCase(protocol)) {
276
277 final String originalPath = anURL.getPath();
278 try {
279 return URLDecoder.decode(anURL.getPath(), encoding);
280 } catch (UnsupportedEncodingException e) {
281 throw new IllegalArgumentException("Could not URLDecode path [" + originalPath
282 + "] using encoding [" + encoding + "]", e);
283 }
284 } else if ("jar".equalsIgnoreCase(protocol)) {
285 toReturn = anURL.getPath();
286 } else if ("http".equalsIgnoreCase(protocol) || "https".equalsIgnoreCase(protocol)) {
287 toReturn = anURL.toString();
288 } else if ("bundleresource".equalsIgnoreCase(protocol)) {
289 toReturn = anURL.toString();
290 } else {
291 throw new IllegalArgumentException("Unknown protocol [" + protocol + "]; could not handle URL ["
292 + anURL + "]");
293 }
294
295 return toReturn;
296 }
297
298
299
300
301
302 private URL addSlashToDirectoryUrlIfRequired(final URL anURL) {
303
304
305 Validate.notNull(anURL, "anURL");
306
307 URL toReturn = anURL;
308 if ("file".equalsIgnoreCase(anURL.getProtocol())) {
309
310 final File theFile = new File(anURL.getPath());
311 if (theFile.isDirectory()) {
312 try {
313
314
315
316
317 toReturn = theFile.toURI().toURL();
318 } catch (MalformedURLException e) {
319
320 throw new IllegalArgumentException("Could not convert a File to an URL", e);
321 }
322 }
323 }
324
325
326 return toReturn;
327 }
328
329
330
331
332
333
334 class DefaultHolder implements ThreadContextClassLoaderHolder {
335
336
337 private Thread affectedThread;
338 private ClassLoader originalClassLoader;
339 private String classPathArgument;
340
341 public DefaultHolder(final Thread affectedThread,
342 final ClassLoader originalClassLoader,
343 final String classPathArgument) {
344
345
346 Validate.notNull(affectedThread, "affectedThread");
347 Validate.notNull(originalClassLoader, "originalClassLoader");
348 Validate.notNull(classPathArgument, "classPathArgument");
349
350
351 this.affectedThread = affectedThread;
352 this.originalClassLoader = originalClassLoader;
353 this.classPathArgument = classPathArgument;
354 }
355
356
357
358
359 @Override
360 public void restoreClassLoaderAndReleaseThread() {
361 if (affectedThread != null) {
362
363
364 affectedThread.setContextClassLoader(originalClassLoader);
365
366
367 affectedThread = null;
368 originalClassLoader = null;
369 classPathArgument = null;
370 }
371 }
372
373
374
375
376 @Override
377 public String getClassPathAsArgument() {
378 return classPathArgument;
379 }
380
381
382
383
384 @Override
385 protected void finalize() throws Throwable {
386 try {
387
388 restoreClassLoaderAndReleaseThread();
389 } finally {
390
391 super.finalize();
392 }
393 }
394 }
395 }