1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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.net.MalformedURLException;
40 import java.net.URL;
41 import java.net.URLClassLoader;
42 import java.util.ArrayList;
43 import java.util.Collections;
44 import java.util.Enumeration;
45 import java.util.HashSet;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.Set;
49 import javax.xml.bind.JAXBElement;
50 import javax.xml.bind.JAXBException;
51 import org.apache.commons.io.IOUtils;
52 import org.apache.commons.lang.StringUtils;
53 import org.apache.tools.ant.Project;
54 import org.apache.tools.ant.types.Path;
55 import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
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
73
74
75
76
77 public class ProjectClassLoader extends URLClassLoader
78 {
79
80
81 private static final String ABSOLUTE_RESOURCE_NAME_PREFIX = "/org/jomc/ant/";
82
83
84 private static final URL[] NO_URLS =
85 {
86 };
87
88
89 private Set<String> modletExcludes;
90
91
92 private Modlets excludedModlets;
93
94
95 private Set<String> serviceExcludes;
96
97
98 private Services excludedServices;
99
100
101 private Set<String> schemaExcludes;
102
103
104 private Schemas excludedSchemas;
105
106
107 private Set<String> providerExcludes;
108
109
110 private Set<String> excludedProviders;
111
112
113 private final Project project;
114
115
116 private Set<String> modletResourceLocations;
117
118
119 private Set<String> providerResourceLocations;
120
121
122 private final Set<File> temporaryResources = new HashSet<File>();
123
124
125
126
127
128
129
130
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
147
148
149
150 public final Project getProject()
151 {
152 return this.project;
153 }
154
155
156
157
158
159
160
161
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
209
210
211
212
213
214
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
294
295
296
297
298
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
312
313
314
315
316
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
330
331
332
333
334
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
348
349
350
351
352
353 public static Set<String> getDefaultModletExcludes() throws IOException
354 {
355 return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultModletExcludes" );
356 }
357
358
359
360
361
362
363
364
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
378
379
380
381
382
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
396
397
398
399
400
401 public static Set<String> getDefaultProviderExcludes() throws IOException
402 {
403 return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultProviderExcludes" );
404 }
405
406
407
408
409
410
411
412
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
426
427
428
429
430
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
444
445
446
447
448
449 public static Set<String> getDefaultServiceExcludes() throws IOException
450 {
451 return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultServiceExcludes" );
452 }
453
454
455
456
457
458
459
460
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
474
475
476
477
478
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
492
493
494
495
496
497 public static Set<String> getDefaultSchemaExcludes() throws IOException
498 {
499 return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultSchemaExcludes" );
500 }
501
502
503
504
505
506
507
508
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
522
523
524 @Override
525 @IgnoreJRERequirement
526 public void close() throws IOException
527 {
528 for ( final Iterator<File> it = this.temporaryResources.iterator(); it.hasNext(); )
529 {
530 final File temporaryResource = it.next();
531
532 if ( temporaryResource.exists() && temporaryResource.delete() )
533 {
534 it.remove();
535 }
536 }
537
538 if ( Closeable.class.isAssignableFrom( ProjectClassLoader.class ) )
539 {
540 super.close();
541 }
542 }
543
544
545
546
547
548 @Override
549 protected void finalize() throws Throwable
550 {
551 for ( final Iterator<File> it = this.temporaryResources.iterator(); it.hasNext(); )
552 {
553 final File temporaryResource = it.next();
554
555 if ( temporaryResource.exists() && !temporaryResource.delete() )
556 {
557 temporaryResource.deleteOnExit();
558 }
559
560 it.remove();
561 }
562
563 super.finalize();
564 }
565
566 private URL filterProviders( final URL resource ) throws IOException
567 {
568 InputStream in = null;
569 boolean suppressExceptionOnClose = true;
570
571 try
572 {
573 URL filteredResource = resource;
574 in = resource.openStream();
575 final List<String> lines = IOUtils.readLines( in, "UTF-8" );
576 final List<String> filteredLines = new ArrayList<String>( lines.size() );
577
578 for ( String line : lines )
579 {
580 if ( !this.getProviderExcludes().contains( line.trim() ) )
581 {
582 filteredLines.add( line.trim() );
583 }
584 else
585 {
586 this.getExcludedProviders().add( line.trim() );
587 this.getProject().log( Messages.getMessage( "providerExclusion", resource.toExternalForm(),
588 line.trim() ), Project.MSG_DEBUG );
589
590 }
591 }
592
593 if ( lines.size() != filteredLines.size() )
594 {
595 OutputStream out = null;
596 final File tmpResource = File.createTempFile( this.getClass().getName(), ".rsrc" );
597 this.temporaryResources.add( tmpResource );
598
599 try
600 {
601 out = new FileOutputStream( tmpResource );
602 IOUtils.writeLines( filteredLines, System.getProperty( "line.separator", "\n" ), out, "UTF-8" );
603 suppressExceptionOnClose = false;
604 }
605 finally
606 {
607 try
608 {
609 if ( out != null )
610 {
611 out.close();
612 }
613
614 suppressExceptionOnClose = true;
615 }
616 catch ( final IOException e )
617 {
618 if ( suppressExceptionOnClose )
619 {
620 this.project.log( Messages.getMessage( e ), e, Project.MSG_ERR );
621 }
622 else
623 {
624 throw e;
625 }
626 }
627 }
628
629 filteredResource = tmpResource.toURI().toURL();
630 }
631
632 suppressExceptionOnClose = false;
633 return filteredResource;
634 }
635 finally
636 {
637 try
638 {
639 if ( in != null )
640 {
641 in.close();
642 }
643 }
644 catch ( final IOException e )
645 {
646 if ( suppressExceptionOnClose )
647 {
648 this.project.log( Messages.getMessage( e ), e, Project.MSG_ERR );
649 }
650 else
651 {
652 throw e;
653 }
654 }
655 }
656 }
657
658 private URL filterModlets( final URL resource ) throws ModelException, IOException, JAXBException
659 {
660 InputStream in = null;
661 boolean suppressExceptionOnClose = true;
662
663 try
664 {
665 URL filteredResource = resource;
666 final ModelContext modelContext = ModelContextFactory.newInstance().newModelContext();
667 in = resource.openStream();
668 final JAXBElement<?> e =
669 (JAXBElement<?>) modelContext.createUnmarshaller( ModletObject.MODEL_PUBLIC_ID ).unmarshal( in );
670
671 final Object o = e.getValue();
672 Modlets modlets = null;
673 boolean filtered = false;
674
675 if ( o instanceof Modlets )
676 {
677 modlets = (Modlets) o;
678 }
679 else if ( o instanceof Modlet )
680 {
681 modlets = new Modlets();
682 modlets.getModlet().add( (Modlet) o );
683 }
684
685 if ( modlets != null )
686 {
687 for ( final Iterator<Modlet> it = modlets.getModlet().iterator(); it.hasNext(); )
688 {
689 final Modlet m = it.next();
690
691 if ( this.getModletExcludes().contains( m.getName() ) )
692 {
693 it.remove();
694 filtered = true;
695 this.addExcludedModlet( m );
696 this.getProject().log( Messages.getMessage( "modletExclusion", resource.toExternalForm(),
697 m.getName() ), Project.MSG_DEBUG );
698
699 continue;
700 }
701
702 if ( this.filterModlet( m, resource.toExternalForm() ) )
703 {
704 filtered = true;
705 }
706 }
707
708 if ( filtered )
709 {
710 final File tmpResource = File.createTempFile( this.getClass().getName(), ".rsrc" );
711 this.temporaryResources.add( tmpResource );
712 modelContext.createMarshaller( ModletObject.MODEL_PUBLIC_ID ).marshal(
713 new ObjectFactory().createModlets( modlets ), tmpResource );
714
715 filteredResource = tmpResource.toURI().toURL();
716 }
717 }
718
719 suppressExceptionOnClose = false;
720 return filteredResource;
721 }
722 finally
723 {
724 try
725 {
726 if ( in != null )
727 {
728 in.close();
729 }
730 }
731 catch ( final IOException e )
732 {
733 if ( suppressExceptionOnClose )
734 {
735 this.project.log( Messages.getMessage( e ), e, Project.MSG_ERR );
736 }
737 else
738 {
739 throw e;
740 }
741 }
742 }
743 }
744
745 private boolean filterModlet( final Modlet modlet, final String resourceInfo )
746 {
747 boolean filteredSchemas = false;
748 boolean filteredServices = false;
749
750 if ( modlet.getSchemas() != null )
751 {
752 final Schemas schemas = new Schemas();
753
754 for ( Schema s : modlet.getSchemas().getSchema() )
755 {
756 if ( !this.getSchemaExcludes().contains( s.getPublicId() ) )
757 {
758 schemas.getSchema().add( s );
759 }
760 else
761 {
762 this.getProject().log( Messages.getMessage( "schemaExclusion", resourceInfo, s.getPublicId() ),
763 Project.MSG_DEBUG );
764
765 this.addExcludedSchema( s );
766 filteredSchemas = true;
767 }
768 }
769
770 if ( filteredSchemas )
771 {
772 modlet.setSchemas( schemas );
773 }
774 }
775
776 if ( modlet.getServices() != null )
777 {
778 final Services services = new Services();
779
780 for ( Service s : modlet.getServices().getService() )
781 {
782 if ( !this.getServiceExcludes().contains( s.getClazz() ) )
783 {
784 services.getService().add( s );
785 }
786 else
787 {
788 this.getProject().log( Messages.getMessage( "serviceExclusion", resourceInfo, s.getClazz() ),
789 Project.MSG_DEBUG );
790
791 this.addExcludedService( s );
792 filteredServices = true;
793 }
794 }
795
796 if ( filteredServices )
797 {
798 modlet.setServices( services );
799 }
800 }
801
802 return filteredSchemas || filteredServices;
803 }
804
805 private void addExcludedModlet( final Modlet modlet )
806 {
807 try
808 {
809 final Modlet m = this.getExcludedModlets().getModlet( modlet.getName() );
810
811 if ( m != null )
812 {
813 if ( m.getVersion() != null && modlet.getVersion() != null
814 && VersionParser.compare( m.getVersion(), modlet.getVersion() ) < 0 )
815 {
816 this.getExcludedModlets().getModlet().remove( m );
817 this.getExcludedModlets().getModlet().add( modlet );
818 }
819 }
820 else
821 {
822 this.getExcludedModlets().getModlet().add( modlet );
823 }
824 }
825 catch ( final ParseException e )
826 {
827 this.getProject().log( Messages.getMessage( e ), e, Project.MSG_WARN );
828 }
829 catch ( final TokenMgrError e )
830 {
831 this.getProject().log( Messages.getMessage( e ), e, Project.MSG_WARN );
832 }
833 }
834
835 private void addExcludedSchema( final Schema schema )
836 {
837 if ( this.getExcludedSchemas().getSchemaBySystemId( schema.getSystemId() ) == null )
838 {
839 this.getExcludedSchemas().getSchema().add( schema );
840 }
841 }
842
843 private void addExcludedService( final Service service )
844 {
845 for ( int i = 0, s0 = this.getExcludedServices().getService().size(); i < s0; i++ )
846 {
847 final Service s = this.getExcludedServices().getService().get( i );
848
849 if ( s.getIdentifier().equals( service.getIdentifier() ) && s.getClazz().equals( service.getClazz() ) )
850 {
851 return;
852 }
853 }
854
855 this.getExcludedServices().getService().add( service );
856 }
857
858 private static Set<String> readDefaultExcludes( final String location ) throws IOException
859 {
860 InputStream resource = null;
861 boolean suppressExceptionOnClose = true;
862 Set<String> defaultExcludes = null;
863
864 try
865 {
866 resource = ProjectClassLoader.class.getResourceAsStream( location );
867
868 if ( resource != null )
869 {
870 final List<String> lines = IOUtils.readLines( resource, "UTF-8" );
871 defaultExcludes = new HashSet<String>( lines.size() );
872
873 for ( String line : lines )
874 {
875 final String trimmed = line.trim();
876
877 if ( trimmed.contains( "#" ) || StringUtils.isEmpty( trimmed ) )
878 {
879 continue;
880 }
881
882 defaultExcludes.add( trimmed );
883 }
884 }
885
886 suppressExceptionOnClose = false;
887 return defaultExcludes != null
888 ? Collections.unmodifiableSet( defaultExcludes ) : Collections.<String>emptySet();
889
890 }
891 finally
892 {
893 try
894 {
895 if ( resource != null )
896 {
897 resource.close();
898 }
899 }
900 catch ( final IOException e )
901 {
902 if ( !suppressExceptionOnClose )
903 {
904 throw e;
905 }
906 }
907 }
908 }
909
910 }