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