1 | /* |
2 | * Copyright (C) Christian Schulte, 2005-206 |
3 | * All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * |
9 | * o Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * |
12 | * o Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in |
14 | * the documentation and/or other materials provided with the |
15 | * distribution. |
16 | * |
17 | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
18 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
19 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, |
21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | * |
28 | * $JOMC: ProjectClassLoader.java 4256 2012-02-13 06:32:10Z schulte2005 $ |
29 | * |
30 | */ |
31 | package org.jomc.ant; |
32 | |
33 | import java.io.Closeable; |
34 | import java.io.File; |
35 | import java.io.FileOutputStream; |
36 | import java.io.IOException; |
37 | import java.io.InputStream; |
38 | import java.io.OutputStream; |
39 | import java.lang.reflect.InvocationTargetException; |
40 | import java.net.MalformedURLException; |
41 | import java.net.URL; |
42 | import java.net.URLClassLoader; |
43 | import java.util.ArrayList; |
44 | import java.util.Collections; |
45 | import java.util.Enumeration; |
46 | import java.util.HashSet; |
47 | import java.util.Iterator; |
48 | import java.util.List; |
49 | import java.util.Set; |
50 | import javax.xml.bind.JAXBElement; |
51 | import javax.xml.bind.JAXBException; |
52 | import org.apache.commons.io.IOUtils; |
53 | import org.apache.commons.lang.StringUtils; |
54 | import org.apache.tools.ant.Project; |
55 | import org.apache.tools.ant.types.Path; |
56 | import org.jomc.modlet.ModelContext; |
57 | import org.jomc.modlet.ModelContextFactory; |
58 | import org.jomc.modlet.ModelException; |
59 | import org.jomc.modlet.Modlet; |
60 | import org.jomc.modlet.ModletObject; |
61 | import org.jomc.modlet.Modlets; |
62 | import org.jomc.modlet.ObjectFactory; |
63 | import org.jomc.modlet.Schema; |
64 | import org.jomc.modlet.Schemas; |
65 | import org.jomc.modlet.Service; |
66 | import org.jomc.modlet.Services; |
67 | import org.jomc.util.ParseException; |
68 | import org.jomc.util.TokenMgrError; |
69 | import org.jomc.util.VersionParser; |
70 | |
71 | /** |
72 | * Class loader supporting JOMC resources backed by a project. |
73 | * |
74 | * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a> |
75 | * @version $JOMC: ProjectClassLoader.java 4256 2012-02-13 06:32:10Z schulte2005 $ |
76 | */ |
77 | public class ProjectClassLoader extends URLClassLoader |
78 | { |
79 | |
80 | /** Constant to prefix relative resource names with. */ |
81 | private static final String ABSOLUTE_RESOURCE_NAME_PREFIX = "/org/jomc/ant/"; |
82 | |
83 | /** Empty URL array. */ |
84 | private static final URL[] NO_URLS = |
85 | { |
86 | }; |
87 | |
88 | /** Set of modlet names to exclude. */ |
89 | private Set<String> modletExcludes; |
90 | |
91 | /** Excluded modlets. */ |
92 | private Modlets excludedModlets; |
93 | |
94 | /** Set of service class names to exclude. */ |
95 | private Set<String> serviceExcludes; |
96 | |
97 | /** Excluded services. */ |
98 | private Services excludedServices; |
99 | |
100 | /** Set of schema public ids to exclude. */ |
101 | private Set<String> schemaExcludes; |
102 | |
103 | /** Excluded schemas. */ |
104 | private Schemas excludedSchemas; |
105 | |
106 | /** Set of providers to exclude. */ |
107 | private Set<String> providerExcludes; |
108 | |
109 | /** Set of excluded providers. */ |
110 | private Set<String> excludedProviders; |
111 | |
112 | /** The project the class loader is associated with. */ |
113 | private final Project project; |
114 | |
115 | /** Set of modlet resource locations to filter. */ |
116 | private Set<String> modletResourceLocations; |
117 | |
118 | /** Set of provider resource locations to filter. */ |
119 | private Set<String> providerResourceLocations; |
120 | |
121 | /** Set of temporary resources. */ |
122 | private final Set<File> temporaryResources = new HashSet<File>(); |
123 | |
124 | /** |
125 | * Creates a new {@code ProjectClassLoader} instance taking a project and a class path. |
126 | * |
127 | * @param project The project to which this class loader is to belong. |
128 | * @param classpath The class path to use for loading. |
129 | * |
130 | * @throws MalformedURLException if {@code classpath} contains unsupported elements. |
131 | */ |
132 | public ProjectClassLoader( final Project project, final Path classpath ) throws MalformedURLException |
133 | { |
134 | super( NO_URLS, ProjectClassLoader.class.getClassLoader() ); |
135 | |
136 | for ( final String name : classpath.list() ) |
137 | { |
138 | final File resolved = project.resolveFile( name ); |
139 | this.addURL( resolved.toURI().toURL() ); |
140 | } |
141 | |
142 | this.project = project; |
143 | } |
144 | |
145 | /** |
146 | * Gets the project of the instance. |
147 | * |
148 | * @return The project of the instance. |
149 | */ |
150 | public final Project getProject() |
151 | { |
152 | return this.project; |
153 | } |
154 | |
155 | /** |
156 | * Finds a resource with a given name. |
157 | * |
158 | * @param name The name of the resource to search. |
159 | * |
160 | * @return An {@code URL} object for reading the resource or {@code null}, if no resource matching {@code name} is |
161 | * found. |
162 | */ |
163 | @Override |
164 | public URL findResource( final String name ) |
165 | { |
166 | try |
167 | { |
168 | URL resource = super.findResource( name ); |
169 | |
170 | if ( resource != null ) |
171 | { |
172 | if ( this.getProviderResourceLocations().contains( name ) ) |
173 | { |
174 | resource = this.filterProviders( resource ); |
175 | } |
176 | else if ( this.getModletResourceLocations().contains( name ) ) |
177 | { |
178 | resource = this.filterModlets( resource ); |
179 | } |
180 | } |
181 | |
182 | return resource; |
183 | } |
184 | catch ( final IOException e ) |
185 | { |
186 | this.getProject().log( Messages.getMessage( e ), Project.MSG_ERR ); |
187 | return null; |
188 | } |
189 | catch ( final JAXBException e ) |
190 | { |
191 | String message = Messages.getMessage( e ); |
192 | if ( message == null && e.getLinkedException() != null ) |
193 | { |
194 | message = Messages.getMessage( e.getLinkedException() ); |
195 | } |
196 | |
197 | this.getProject().log( message, Project.MSG_ERR ); |
198 | return null; |
199 | } |
200 | catch ( final ModelException e ) |
201 | { |
202 | this.getProject().log( Messages.getMessage( e ), Project.MSG_ERR ); |
203 | return null; |
204 | } |
205 | } |
206 | |
207 | /** |
208 | * Gets all resources matching a given name. |
209 | * |
210 | * @param name The name of the resources to get. |
211 | * |
212 | * @return An enumeration of {@code URL} objects of found resources. |
213 | * |
214 | * @throws IOException if getting resources fails. |
215 | */ |
216 | @Override |
217 | public Enumeration<URL> findResources( final String name ) throws IOException |
218 | { |
219 | final Enumeration<URL> allResources = super.findResources( name ); |
220 | Enumeration<URL> enumeration = allResources; |
221 | |
222 | if ( this.getProviderResourceLocations().contains( name ) ) |
223 | { |
224 | enumeration = new Enumeration<URL>() |
225 | { |
226 | |
227 | public boolean hasMoreElements() |
228 | { |
229 | return allResources.hasMoreElements(); |
230 | } |
231 | |
232 | public URL nextElement() |
233 | { |
234 | try |
235 | { |
236 | return filterProviders( allResources.nextElement() ); |
237 | } |
238 | catch ( final IOException e ) |
239 | { |
240 | getProject().log( Messages.getMessage( e ), Project.MSG_ERR ); |
241 | return null; |
242 | } |
243 | } |
244 | |
245 | }; |
246 | } |
247 | else if ( this.getModletResourceLocations().contains( name ) ) |
248 | { |
249 | enumeration = new Enumeration<URL>() |
250 | { |
251 | |
252 | public boolean hasMoreElements() |
253 | { |
254 | return allResources.hasMoreElements(); |
255 | } |
256 | |
257 | public URL nextElement() |
258 | { |
259 | try |
260 | { |
261 | return filterModlets( allResources.nextElement() ); |
262 | } |
263 | catch ( final IOException e ) |
264 | { |
265 | getProject().log( Messages.getMessage( e ), Project.MSG_ERR ); |
266 | return null; |
267 | } |
268 | catch ( final JAXBException e ) |
269 | { |
270 | String message = Messages.getMessage( e ); |
271 | if ( message == null && e.getLinkedException() != null ) |
272 | { |
273 | message = Messages.getMessage( e.getLinkedException() ); |
274 | } |
275 | |
276 | getProject().log( message, Project.MSG_ERR ); |
277 | return null; |
278 | } |
279 | catch ( final ModelException e ) |
280 | { |
281 | getProject().log( Messages.getMessage( e ), Project.MSG_ERR ); |
282 | return null; |
283 | } |
284 | } |
285 | |
286 | }; |
287 | } |
288 | |
289 | return enumeration; |
290 | } |
291 | |
292 | /** |
293 | * Gets a set of modlet resource locations to filter. |
294 | * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make |
295 | * to the returned set will be present inside the object. This is why there is no {@code set} method for the |
296 | * modlet resource locations property.</p> |
297 | * |
298 | * @return A set of modlet resource locations to filter. |
299 | */ |
300 | public final Set<String> getModletResourceLocations() |
301 | { |
302 | if ( this.modletResourceLocations == null ) |
303 | { |
304 | this.modletResourceLocations = new HashSet<String>(); |
305 | } |
306 | |
307 | return this.modletResourceLocations; |
308 | } |
309 | |
310 | /** |
311 | * Gets a set of provider resource locations to filter. |
312 | * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make |
313 | * to the returned set will be present inside the object. This is why there is no {@code set} method for the |
314 | * provider resource locations property.</p> |
315 | * |
316 | * @return A set of provider resource locations to filter. |
317 | */ |
318 | public final Set<String> getProviderResourceLocations() |
319 | { |
320 | if ( this.providerResourceLocations == null ) |
321 | { |
322 | this.providerResourceLocations = new HashSet<String>(); |
323 | } |
324 | |
325 | return this.providerResourceLocations; |
326 | } |
327 | |
328 | /** |
329 | * Gets a set of modlet names to exclude. |
330 | * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make |
331 | * to the returned set will be present inside the object. This is why there is no {@code set} method for the |
332 | * modlet excludes property.</p> |
333 | * |
334 | * @return A set of modlet names to exclude. |
335 | */ |
336 | public final Set<String> getModletExcludes() |
337 | { |
338 | if ( this.modletExcludes == null ) |
339 | { |
340 | this.modletExcludes = new HashSet<String>(); |
341 | } |
342 | |
343 | return this.modletExcludes; |
344 | } |
345 | |
346 | /** |
347 | * Gets a set of modlet names excluded by default. |
348 | * |
349 | * @return An unmodifiable set of modlet names excluded by default. |
350 | * |
351 | * @throws IOException if reading configuration resources fails. |
352 | */ |
353 | public static Set<String> getDefaultModletExcludes() throws IOException |
354 | { |
355 | return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultModletExcludes" ); |
356 | } |
357 | |
358 | /** |
359 | * Gets a set of modlets excluded during resource loading. |
360 | * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make |
361 | * to the returned set will be present inside the object. This is why there is no {@code set} method for the |
362 | * excluded modlets property.</p> |
363 | * |
364 | * @return A set of modlets excluded during resource loading. |
365 | */ |
366 | public final Modlets getExcludedModlets() |
367 | { |
368 | if ( this.excludedModlets == null ) |
369 | { |
370 | this.excludedModlets = new Modlets(); |
371 | } |
372 | |
373 | return this.excludedModlets; |
374 | } |
375 | |
376 | /** |
377 | * Gets a set of provider names to exclude. |
378 | * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make |
379 | * to the returned set will be present inside the object. This is why there is no {@code set} method for the |
380 | * provider excludes property.</p> |
381 | * |
382 | * @return A set of providers to exclude. |
383 | */ |
384 | public final Set<String> getProviderExcludes() |
385 | { |
386 | if ( this.providerExcludes == null ) |
387 | { |
388 | this.providerExcludes = new HashSet<String>(); |
389 | } |
390 | |
391 | return this.providerExcludes; |
392 | } |
393 | |
394 | /** |
395 | * Gets a set of provider names excluded by default. |
396 | * |
397 | * @return An unmodifiable set of provider names excluded by default. |
398 | * |
399 | * @throws IOException if reading configuration resources fails. |
400 | */ |
401 | public static Set<String> getDefaultProviderExcludes() throws IOException |
402 | { |
403 | return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultProviderExcludes" ); |
404 | } |
405 | |
406 | /** |
407 | * Gets a set of providers excluded during resource loading. |
408 | * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make |
409 | * to the returned set will be present inside the object. This is why there is no {@code set} method for the |
410 | * excluded providers property.</p> |
411 | * |
412 | * @return A set of providers excluded during resource loading. |
413 | */ |
414 | public final Set<String> getExcludedProviders() |
415 | { |
416 | if ( this.excludedProviders == null ) |
417 | { |
418 | this.excludedProviders = new HashSet<String>(); |
419 | } |
420 | |
421 | return this.excludedProviders; |
422 | } |
423 | |
424 | /** |
425 | * Gets a set of service class names to exclude. |
426 | * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make |
427 | * to the returned set will be present inside the object. This is why there is no {@code set} method for the |
428 | * service excludes property.</p> |
429 | * |
430 | * @return A set of service class names to exclude. |
431 | */ |
432 | public final Set<String> getServiceExcludes() |
433 | { |
434 | if ( this.serviceExcludes == null ) |
435 | { |
436 | this.serviceExcludes = new HashSet<String>(); |
437 | } |
438 | |
439 | return this.serviceExcludes; |
440 | } |
441 | |
442 | /** |
443 | * Gets a set of service class names excluded by default. |
444 | * |
445 | * @return An unmodifiable set of service class names excluded by default. |
446 | * |
447 | * @throws IOException if reading configuration resources fails. |
448 | */ |
449 | public static Set<String> getDefaultServiceExcludes() throws IOException |
450 | { |
451 | return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultServiceExcludes" ); |
452 | } |
453 | |
454 | /** |
455 | * Gets a set of services excluded during resource loading. |
456 | * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make |
457 | * to the returned set will be present inside the object. This is why there is no {@code set} method for the |
458 | * excluded services property.</p> |
459 | * |
460 | * @return Services excluded during resource loading. |
461 | */ |
462 | public final Services getExcludedServices() |
463 | { |
464 | if ( this.excludedServices == null ) |
465 | { |
466 | this.excludedServices = new Services(); |
467 | } |
468 | |
469 | return this.excludedServices; |
470 | } |
471 | |
472 | /** |
473 | * Gets a set of schema public identifiers to exclude. |
474 | * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make |
475 | * to the returned set will be present inside the object. This is why there is no {@code set} method for the |
476 | * schema excludes property.</p> |
477 | * |
478 | * @return A set of schema public identifiers to exclude. |
479 | */ |
480 | public final Set<String> getSchemaExcludes() |
481 | { |
482 | if ( this.schemaExcludes == null ) |
483 | { |
484 | this.schemaExcludes = new HashSet<String>(); |
485 | } |
486 | |
487 | return this.schemaExcludes; |
488 | } |
489 | |
490 | /** |
491 | * Gets a set of schema public identifiers excluded by default. |
492 | * |
493 | * @return An unmodifiable set of schema public identifiers excluded by default. |
494 | * |
495 | * @throws IOException if reading configuration resources fails. |
496 | */ |
497 | public static Set<String> getDefaultSchemaExcludes() throws IOException |
498 | { |
499 | return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultSchemaExcludes" ); |
500 | } |
501 | |
502 | /** |
503 | * Gets a set of schemas excluded during resource loading. |
504 | * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make |
505 | * to the returned set will be present inside the object. This is why there is no {@code set} method for the |
506 | * excluded schemas property.</p> |
507 | * |
508 | * @return Schemas excluded during resource loading. |
509 | */ |
510 | public final Schemas getExcludedSchemas() |
511 | { |
512 | if ( this.excludedSchemas == null ) |
513 | { |
514 | this.excludedSchemas = new Schemas(); |
515 | } |
516 | |
517 | return this.excludedSchemas; |
518 | } |
519 | |
520 | /** |
521 | * Closes the class loader. |
522 | * @throws IOException if closing the class loader fails. |
523 | */ |
524 | public void close() throws IOException |
525 | { |
526 | for ( final Iterator<File> it = this.temporaryResources.iterator(); it.hasNext(); ) |
527 | { |
528 | final File temporaryResource = it.next(); |
529 | |
530 | if ( temporaryResource.exists() && temporaryResource.delete() ) |
531 | { |
532 | it.remove(); |
533 | } |
534 | } |
535 | |
536 | if ( Closeable.class.isAssignableFrom( ProjectClassLoader.class ) ) |
537 | { // JDK: As of JDK 7, super.close(); |
538 | this.jdk7Close(); |
539 | } |
540 | } |
541 | |
542 | /** |
543 | * Removes temporary resources. |
544 | * @throws Throwable if finalization fails. |
545 | */ |
546 | @Override |
547 | protected void finalize() throws Throwable |
548 | { |
549 | for ( final Iterator<File> it = this.temporaryResources.iterator(); it.hasNext(); ) |
550 | { |
551 | final File temporaryResource = it.next(); |
552 | |
553 | if ( temporaryResource.exists() && !temporaryResource.delete() ) |
554 | { |
555 | temporaryResource.deleteOnExit(); |
556 | } |
557 | |
558 | it.remove(); |
559 | } |
560 | |
561 | super.finalize(); |
562 | } |
563 | |
564 | private URL filterProviders( final URL resource ) throws IOException |
565 | { |
566 | InputStream in = null; |
567 | boolean suppressExceptionOnClose = true; |
568 | |
569 | try |
570 | { |
571 | URL filteredResource = resource; |
572 | in = resource.openStream(); |
573 | final List<String> lines = IOUtils.readLines( in, "UTF-8" ); |
574 | final List<String> filteredLines = new ArrayList<String>( lines.size() ); |
575 | |
576 | for ( String line : lines ) |
577 | { |
578 | if ( !this.getProviderExcludes().contains( line.trim() ) ) |
579 | { |
580 | filteredLines.add( line.trim() ); |
581 | } |
582 | else |
583 | { |
584 | this.getExcludedProviders().add( line.trim() ); |
585 | this.getProject().log( Messages.getMessage( "providerExclusion", resource.toExternalForm(), |
586 | line.trim() ), Project.MSG_DEBUG ); |
587 | |
588 | } |
589 | } |
590 | |
591 | if ( lines.size() != filteredLines.size() ) |
592 | { |
593 | OutputStream out = null; |
594 | final File tmpResource = File.createTempFile( this.getClass().getName(), ".rsrc" ); |
595 | this.temporaryResources.add( tmpResource ); |
596 | |
597 | try |
598 | { |
599 | out = new FileOutputStream( tmpResource ); |
600 | IOUtils.writeLines( filteredLines, System.getProperty( "line.separator", "\n" ), out, "UTF-8" ); |
601 | suppressExceptionOnClose = false; |
602 | } |
603 | finally |
604 | { |
605 | try |
606 | { |
607 | if ( out != null ) |
608 | { |
609 | out.close(); |
610 | } |
611 | |
612 | suppressExceptionOnClose = true; |
613 | } |
614 | catch ( final IOException e ) |
615 | { |
616 | if ( suppressExceptionOnClose ) |
617 | { |
618 | this.project.log( Messages.getMessage( e ), e, Project.MSG_ERR ); |
619 | } |
620 | else |
621 | { |
622 | throw e; |
623 | } |
624 | } |
625 | } |
626 | |
627 | filteredResource = tmpResource.toURI().toURL(); |
628 | } |
629 | |
630 | suppressExceptionOnClose = false; |
631 | return filteredResource; |
632 | } |
633 | finally |
634 | { |
635 | try |
636 | { |
637 | if ( in != null ) |
638 | { |
639 | in.close(); |
640 | } |
641 | } |
642 | catch ( final IOException e ) |
643 | { |
644 | if ( suppressExceptionOnClose ) |
645 | { |
646 | this.project.log( Messages.getMessage( e ), e, Project.MSG_ERR ); |
647 | } |
648 | else |
649 | { |
650 | throw e; |
651 | } |
652 | } |
653 | } |
654 | } |
655 | |
656 | private URL filterModlets( final URL resource ) throws ModelException, IOException, JAXBException |
657 | { |
658 | InputStream in = null; |
659 | boolean suppressExceptionOnClose = true; |
660 | |
661 | try |
662 | { |
663 | URL filteredResource = resource; |
664 | final ModelContext modelContext = ModelContextFactory.newInstance().newModelContext(); |
665 | in = resource.openStream(); |
666 | final JAXBElement<?> e = |
667 | (JAXBElement<?>) modelContext.createUnmarshaller( ModletObject.MODEL_PUBLIC_ID ).unmarshal( in ); |
668 | |
669 | final Object o = e.getValue(); |
670 | Modlets modlets = null; |
671 | boolean filtered = false; |
672 | |
673 | if ( o instanceof Modlets ) |
674 | { |
675 | modlets = (Modlets) o; |
676 | } |
677 | else if ( o instanceof Modlet ) |
678 | { |
679 | modlets = new Modlets(); |
680 | modlets.getModlet().add( (Modlet) o ); |
681 | } |
682 | |
683 | if ( modlets != null ) |
684 | { |
685 | for ( final Iterator<Modlet> it = modlets.getModlet().iterator(); it.hasNext(); ) |
686 | { |
687 | final Modlet m = it.next(); |
688 | |
689 | if ( this.getModletExcludes().contains( m.getName() ) ) |
690 | { |
691 | it.remove(); |
692 | filtered = true; |
693 | this.addExcludedModlet( m ); |
694 | this.getProject().log( Messages.getMessage( "modletExclusion", resource.toExternalForm(), |
695 | m.getName() ), Project.MSG_DEBUG ); |
696 | |
697 | continue; |
698 | } |
699 | |
700 | if ( this.filterModlet( m, resource.toExternalForm() ) ) |
701 | { |
702 | filtered = true; |
703 | } |
704 | } |
705 | |
706 | if ( filtered ) |
707 | { |
708 | final File tmpResource = File.createTempFile( this.getClass().getName(), ".rsrc" ); |
709 | this.temporaryResources.add( tmpResource ); |
710 | modelContext.createMarshaller( ModletObject.MODEL_PUBLIC_ID ).marshal( |
711 | new ObjectFactory().createModlets( modlets ), tmpResource ); |
712 | |
713 | filteredResource = tmpResource.toURI().toURL(); |
714 | } |
715 | } |
716 | |
717 | suppressExceptionOnClose = false; |
718 | return filteredResource; |
719 | } |
720 | finally |
721 | { |
722 | try |
723 | { |
724 | if ( in != null ) |
725 | { |
726 | in.close(); |
727 | } |
728 | } |
729 | catch ( final IOException e ) |
730 | { |
731 | if ( suppressExceptionOnClose ) |
732 | { |
733 | this.project.log( Messages.getMessage( e ), e, Project.MSG_ERR ); |
734 | } |
735 | else |
736 | { |
737 | throw e; |
738 | } |
739 | } |
740 | } |
741 | } |
742 | |
743 | private boolean filterModlet( final Modlet modlet, final String resourceInfo ) |
744 | { |
745 | boolean filteredSchemas = false; |
746 | boolean filteredServices = false; |
747 | |
748 | if ( modlet.getSchemas() != null ) |
749 | { |
750 | final Schemas schemas = new Schemas(); |
751 | |
752 | for ( Schema s : modlet.getSchemas().getSchema() ) |
753 | { |
754 | if ( !this.getSchemaExcludes().contains( s.getPublicId() ) ) |
755 | { |
756 | schemas.getSchema().add( s ); |
757 | } |
758 | else |
759 | { |
760 | this.getProject().log( Messages.getMessage( "schemaExclusion", resourceInfo, s.getPublicId() ), |
761 | Project.MSG_DEBUG ); |
762 | |
763 | this.addExcludedSchema( s ); |
764 | filteredSchemas = true; |
765 | } |
766 | } |
767 | |
768 | if ( filteredSchemas ) |
769 | { |
770 | modlet.setSchemas( schemas ); |
771 | } |
772 | } |
773 | |
774 | if ( modlet.getServices() != null ) |
775 | { |
776 | final Services services = new Services(); |
777 | |
778 | for ( Service s : modlet.getServices().getService() ) |
779 | { |
780 | if ( !this.getServiceExcludes().contains( s.getClazz() ) ) |
781 | { |
782 | services.getService().add( s ); |
783 | } |
784 | else |
785 | { |
786 | this.getProject().log( Messages.getMessage( "serviceExclusion", resourceInfo, s.getClazz() ), |
787 | Project.MSG_DEBUG ); |
788 | |
789 | this.addExcludedService( s ); |
790 | filteredServices = true; |
791 | } |
792 | } |
793 | |
794 | if ( filteredServices ) |
795 | { |
796 | modlet.setServices( services ); |
797 | } |
798 | } |
799 | |
800 | return filteredSchemas || filteredServices; |
801 | } |
802 | |
803 | private void addExcludedModlet( final Modlet modlet ) |
804 | { |
805 | try |
806 | { |
807 | final Modlet m = this.getExcludedModlets().getModlet( modlet.getName() ); |
808 | |
809 | if ( m != null ) |
810 | { |
811 | if ( m.getVersion() != null && modlet.getVersion() != null |
812 | && VersionParser.compare( m.getVersion(), modlet.getVersion() ) < 0 ) |
813 | { |
814 | this.getExcludedModlets().getModlet().remove( m ); |
815 | this.getExcludedModlets().getModlet().add( modlet ); |
816 | } |
817 | } |
818 | else |
819 | { |
820 | this.getExcludedModlets().getModlet().add( modlet ); |
821 | } |
822 | } |
823 | catch ( final ParseException e ) |
824 | { |
825 | this.getProject().log( Messages.getMessage( e ), e, Project.MSG_WARN ); |
826 | } |
827 | catch ( final TokenMgrError e ) |
828 | { |
829 | this.getProject().log( Messages.getMessage( e ), e, Project.MSG_WARN ); |
830 | } |
831 | } |
832 | |
833 | private void addExcludedSchema( final Schema schema ) |
834 | { |
835 | if ( this.getExcludedSchemas().getSchemaBySystemId( schema.getSystemId() ) == null ) |
836 | { |
837 | this.getExcludedSchemas().getSchema().add( schema ); |
838 | } |
839 | } |
840 | |
841 | private void addExcludedService( final Service service ) |
842 | { |
843 | for ( int i = 0, s0 = this.getExcludedServices().getService().size(); i < s0; i++ ) |
844 | { |
845 | final Service s = this.getExcludedServices().getService().get( i ); |
846 | |
847 | if ( s.getIdentifier().equals( service.getIdentifier() ) && s.getClazz().equals( service.getClazz() ) ) |
848 | { |
849 | return; |
850 | } |
851 | } |
852 | |
853 | this.getExcludedServices().getService().add( service ); |
854 | } |
855 | |
856 | private void jdk7Close() |
857 | { |
858 | try |
859 | { |
860 | URLClassLoader.class.getMethod( "close" ).invoke( this ); |
861 | } |
862 | catch ( final NoSuchMethodException e ) |
863 | { |
864 | this.project.log( Messages.getMessage( e ), e, Project.MSG_DEBUG ); |
865 | } |
866 | catch ( final IllegalAccessException e ) |
867 | { |
868 | this.project.log( Messages.getMessage( e ), e, Project.MSG_DEBUG ); |
869 | } |
870 | catch ( final InvocationTargetException e ) |
871 | { |
872 | this.project.log( Messages.getMessage( e ), e, Project.MSG_DEBUG ); |
873 | } |
874 | } |
875 | |
876 | private static Set<String> readDefaultExcludes( final String location ) throws IOException |
877 | { |
878 | InputStream resource = null; |
879 | boolean suppressExceptionOnClose = true; |
880 | Set<String> defaultExcludes = null; |
881 | |
882 | try |
883 | { |
884 | resource = ProjectClassLoader.class.getResourceAsStream( location ); |
885 | |
886 | if ( resource != null ) |
887 | { |
888 | final List<String> lines = IOUtils.readLines( resource, "UTF-8" ); |
889 | defaultExcludes = new HashSet<String>( lines.size() ); |
890 | |
891 | for ( String line : lines ) |
892 | { |
893 | final String trimmed = line.trim(); |
894 | |
895 | if ( trimmed.contains( "#" ) || StringUtils.isEmpty( trimmed ) ) |
896 | { |
897 | continue; |
898 | } |
899 | |
900 | defaultExcludes.add( trimmed ); |
901 | } |
902 | } |
903 | |
904 | suppressExceptionOnClose = false; |
905 | return defaultExcludes != null |
906 | ? Collections.unmodifiableSet( defaultExcludes ) : Collections.<String>emptySet(); |
907 | |
908 | } |
909 | finally |
910 | { |
911 | try |
912 | { |
913 | if ( resource != null ) |
914 | { |
915 | resource.close(); |
916 | } |
917 | } |
918 | catch ( final IOException e ) |
919 | { |
920 | if ( !suppressExceptionOnClose ) |
921 | { |
922 | throw e; |
923 | } |
924 | } |
925 | } |
926 | } |
927 | |
928 | } |