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
82
83 private static final String ABSOLUTE_RESOURCE_NAME_PREFIX = "/org/jomc/ant/";
84
85
86
87
88 private static final URL[] NO_URLS =
89 {
90 };
91
92
93
94
95 private Set<String> modletExcludes;
96
97
98
99
100 private Modlets excludedModlets;
101
102
103
104
105 private Set<String> serviceExcludes;
106
107
108
109
110 private Services excludedServices;
111
112
113
114
115 private Set<String> schemaExcludes;
116
117
118
119
120 private Schemas excludedSchemas;
121
122
123
124
125 private Set<String> providerExcludes;
126
127
128
129
130 private Set<String> excludedProviders;
131
132
133
134
135 private final Project project;
136
137
138
139
140 private Set<String> modletResourceLocations;
141
142
143
144
145 private Set<String> providerResourceLocations;
146
147
148
149
150 private final Set<File> temporaryResources = new HashSet<File>();
151
152
153
154
155
156
157
158
159
160 public ProjectClassLoader( final Project project, final Path classpath ) throws MalformedURLException
161 {
162 super( NO_URLS, ProjectClassLoader.class.getClassLoader() );
163
164 for ( final String name : classpath.list() )
165 {
166 final File resolved = project.resolveFile( name );
167 this.addURL( resolved.toURI().toURL() );
168 }
169
170 this.project = project;
171 }
172
173
174
175
176
177
178 public final Project getProject()
179 {
180 return this.project;
181 }
182
183
184
185
186
187
188
189
190
191 @Override
192 public URL findResource( final String name )
193 {
194 try
195 {
196 URL resource = super.findResource( name );
197
198 if ( resource != null )
199 {
200 if ( this.getProviderResourceLocations().contains( name ) )
201 {
202 resource = this.filterProviders( resource );
203 }
204 else if ( this.getModletResourceLocations().contains( name ) )
205 {
206 resource = this.filterModlets( resource );
207 }
208 }
209
210 return resource;
211 }
212 catch ( final IOException e )
213 {
214 this.getProject().log( Messages.getMessage( e ), Project.MSG_ERR );
215 return null;
216 }
217 catch ( final JAXBException e )
218 {
219 String message = Messages.getMessage( e );
220 if ( message == null && e.getLinkedException() != null )
221 {
222 message = Messages.getMessage( e.getLinkedException() );
223 }
224
225 this.getProject().log( message, Project.MSG_ERR );
226 return null;
227 }
228 catch ( final ModelException e )
229 {
230 this.getProject().log( Messages.getMessage( e ), Project.MSG_ERR );
231 return null;
232 }
233 }
234
235
236
237
238
239
240
241
242
243
244 @Override
245 public Enumeration<URL> findResources( final String name ) throws IOException
246 {
247 final Enumeration<URL> allResources = super.findResources( name );
248 Enumeration<URL> enumeration = allResources;
249
250 if ( this.getProviderResourceLocations().contains( name ) )
251 {
252 enumeration = new Enumeration<URL>()
253 {
254
255 public boolean hasMoreElements()
256 {
257 return allResources.hasMoreElements();
258 }
259
260 public URL nextElement()
261 {
262 try
263 {
264 return filterProviders( allResources.nextElement() );
265 }
266 catch ( final IOException e )
267 {
268 getProject().log( Messages.getMessage( e ), Project.MSG_ERR );
269 return null;
270 }
271 }
272
273 };
274 }
275 else if ( this.getModletResourceLocations().contains( name ) )
276 {
277 enumeration = new Enumeration<URL>()
278 {
279
280 public boolean hasMoreElements()
281 {
282 return allResources.hasMoreElements();
283 }
284
285 public URL nextElement()
286 {
287 try
288 {
289 return filterModlets( allResources.nextElement() );
290 }
291 catch ( final IOException e )
292 {
293 getProject().log( Messages.getMessage( e ), Project.MSG_ERR );
294 return null;
295 }
296 catch ( final JAXBException e )
297 {
298 String message = Messages.getMessage( e );
299 if ( message == null && e.getLinkedException() != null )
300 {
301 message = Messages.getMessage( e.getLinkedException() );
302 }
303
304 getProject().log( message, Project.MSG_ERR );
305 return null;
306 }
307 catch ( final ModelException e )
308 {
309 getProject().log( Messages.getMessage( e ), Project.MSG_ERR );
310 return null;
311 }
312 }
313
314 };
315 }
316
317 return enumeration;
318 }
319
320
321
322
323
324
325
326
327
328
329
330 public final Set<String> getModletResourceLocations()
331 {
332 if ( this.modletResourceLocations == null )
333 {
334 this.modletResourceLocations = new HashSet<String>();
335 }
336
337 return this.modletResourceLocations;
338 }
339
340
341
342
343
344
345
346
347
348
349
350 public final Set<String> getProviderResourceLocations()
351 {
352 if ( this.providerResourceLocations == null )
353 {
354 this.providerResourceLocations = new HashSet<String>();
355 }
356
357 return this.providerResourceLocations;
358 }
359
360
361
362
363
364
365
366
367
368
369
370 public final Set<String> getModletExcludes()
371 {
372 if ( this.modletExcludes == null )
373 {
374 this.modletExcludes = new HashSet<String>();
375 }
376
377 return this.modletExcludes;
378 }
379
380
381
382
383
384
385
386
387 public static Set<String> getDefaultModletExcludes() throws IOException
388 {
389 return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultModletExcludes" );
390 }
391
392
393
394
395
396
397
398
399
400
401
402 public final Modlets getExcludedModlets()
403 {
404 if ( this.excludedModlets == null )
405 {
406 this.excludedModlets = new Modlets();
407 }
408
409 return this.excludedModlets;
410 }
411
412
413
414
415
416
417
418
419
420
421
422 public final Set<String> getProviderExcludes()
423 {
424 if ( this.providerExcludes == null )
425 {
426 this.providerExcludes = new HashSet<String>();
427 }
428
429 return this.providerExcludes;
430 }
431
432
433
434
435
436
437
438
439 public static Set<String> getDefaultProviderExcludes() throws IOException
440 {
441 return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultProviderExcludes" );
442 }
443
444
445
446
447
448
449
450
451
452
453
454 public final Set<String> getExcludedProviders()
455 {
456 if ( this.excludedProviders == null )
457 {
458 this.excludedProviders = new HashSet<String>();
459 }
460
461 return this.excludedProviders;
462 }
463
464
465
466
467
468
469
470
471
472
473
474 public final Set<String> getServiceExcludes()
475 {
476 if ( this.serviceExcludes == null )
477 {
478 this.serviceExcludes = new HashSet<String>();
479 }
480
481 return this.serviceExcludes;
482 }
483
484
485
486
487
488
489
490
491 public static Set<String> getDefaultServiceExcludes() throws IOException
492 {
493 return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultServiceExcludes" );
494 }
495
496
497
498
499
500
501
502
503
504
505
506 public final Services getExcludedServices()
507 {
508 if ( this.excludedServices == null )
509 {
510 this.excludedServices = new Services();
511 }
512
513 return this.excludedServices;
514 }
515
516
517
518
519
520
521
522
523
524
525
526 public final Set<String> getSchemaExcludes()
527 {
528 if ( this.schemaExcludes == null )
529 {
530 this.schemaExcludes = new HashSet<String>();
531 }
532
533 return this.schemaExcludes;
534 }
535
536
537
538
539
540
541
542
543 public static Set<String> getDefaultSchemaExcludes() throws IOException
544 {
545 return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultSchemaExcludes" );
546 }
547
548
549
550
551
552
553
554
555
556
557
558 public final Schemas getExcludedSchemas()
559 {
560 if ( this.excludedSchemas == null )
561 {
562 this.excludedSchemas = new Schemas();
563 }
564
565 return this.excludedSchemas;
566 }
567
568
569
570
571
572
573 @Override
574 @IgnoreJRERequirement
575 public void close() throws IOException
576 {
577 for ( final Iterator<File> it = this.temporaryResources.iterator(); it.hasNext(); )
578 {
579 final File temporaryResource = it.next();
580
581 if ( temporaryResource.exists() && temporaryResource.delete() )
582 {
583 it.remove();
584 }
585 }
586
587 if ( Closeable.class.isAssignableFrom( ProjectClassLoader.class ) )
588 {
589 super.close();
590 }
591 }
592
593
594
595
596
597
598 @Override
599 protected void finalize() throws Throwable
600 {
601 for ( final Iterator<File> it = this.temporaryResources.iterator(); it.hasNext(); )
602 {
603 final File temporaryResource = it.next();
604
605 if ( temporaryResource.exists() && !temporaryResource.delete() )
606 {
607 temporaryResource.deleteOnExit();
608 }
609
610 it.remove();
611 }
612
613 super.finalize();
614 }
615
616 private URL filterProviders( final URL resource ) throws IOException
617 {
618 InputStream in = null;
619 boolean suppressExceptionOnClose = true;
620
621 try
622 {
623 URL filteredResource = resource;
624 in = resource.openStream();
625 final List<String> lines = IOUtils.readLines( in, "UTF-8" );
626 final List<String> filteredLines = new ArrayList<String>( lines.size() );
627
628 for ( final String line : lines )
629 {
630 if ( !this.getProviderExcludes().contains( line.trim() ) )
631 {
632 filteredLines.add( line.trim() );
633 }
634 else
635 {
636 this.getExcludedProviders().add( line.trim() );
637 this.getProject().log( Messages.getMessage( "providerExclusion", resource.toExternalForm(),
638 line.trim() ), Project.MSG_DEBUG );
639
640 }
641 }
642
643 if ( lines.size() != filteredLines.size() )
644 {
645 OutputStream out = null;
646 final File tmpResource = File.createTempFile( this.getClass().getName(), ".rsrc" );
647 this.temporaryResources.add( tmpResource );
648
649 try
650 {
651 out = new FileOutputStream( tmpResource );
652 IOUtils.writeLines( filteredLines, System.getProperty( "line.separator", "\n" ), out, "UTF-8" );
653 suppressExceptionOnClose = false;
654 }
655 finally
656 {
657 try
658 {
659 if ( out != null )
660 {
661 out.close();
662 }
663
664 suppressExceptionOnClose = true;
665 }
666 catch ( final IOException e )
667 {
668 if ( suppressExceptionOnClose )
669 {
670 this.project.log( Messages.getMessage( e ), e, Project.MSG_ERR );
671 }
672 else
673 {
674 throw e;
675 }
676 }
677 }
678
679 filteredResource = tmpResource.toURI().toURL();
680 }
681
682 suppressExceptionOnClose = false;
683 return filteredResource;
684 }
685 finally
686 {
687 try
688 {
689 if ( in != null )
690 {
691 in.close();
692 }
693 }
694 catch ( final IOException e )
695 {
696 if ( suppressExceptionOnClose )
697 {
698 this.project.log( Messages.getMessage( e ), e, Project.MSG_ERR );
699 }
700 else
701 {
702 throw e;
703 }
704 }
705 }
706 }
707
708 private URL filterModlets( final URL resource ) throws ModelException, IOException, JAXBException
709 {
710 InputStream in = null;
711 boolean suppressExceptionOnClose = true;
712
713 try
714 {
715 URL filteredResource = resource;
716 final ModelContext modelContext = ModelContextFactory.newInstance().newModelContext();
717 in = resource.openStream();
718 final JAXBElement<?> e =
719 (JAXBElement<?>) modelContext.createUnmarshaller( ModletObject.MODEL_PUBLIC_ID ).unmarshal( in );
720
721 final Object o = e.getValue();
722 Modlets modlets = null;
723 boolean filtered = false;
724
725 if ( o instanceof Modlets )
726 {
727 modlets = (Modlets) o;
728 }
729 else if ( o instanceof Modlet )
730 {
731 modlets = new Modlets();
732 modlets.getModlet().add( (Modlet) o );
733 }
734
735 if ( modlets != null )
736 {
737 for ( final Iterator<Modlet> it = modlets.getModlet().iterator(); it.hasNext(); )
738 {
739 final Modlet m = it.next();
740
741 if ( this.getModletExcludes().contains( m.getName() ) )
742 {
743 it.remove();
744 filtered = true;
745 this.addExcludedModlet( m );
746 this.getProject().log( Messages.getMessage( "modletExclusion", resource.toExternalForm(),
747 m.getName() ), Project.MSG_DEBUG );
748
749 continue;
750 }
751
752 if ( this.filterModlet( m, resource.toExternalForm() ) )
753 {
754 filtered = true;
755 }
756 }
757
758 if ( filtered )
759 {
760 final File tmpResource = File.createTempFile( this.getClass().getName(), ".rsrc" );
761 this.temporaryResources.add( tmpResource );
762 modelContext.createMarshaller( ModletObject.MODEL_PUBLIC_ID ).marshal(
763 new ObjectFactory().createModlets( modlets ), tmpResource );
764
765 filteredResource = tmpResource.toURI().toURL();
766 }
767 }
768
769 suppressExceptionOnClose = false;
770 return filteredResource;
771 }
772 finally
773 {
774 try
775 {
776 if ( in != null )
777 {
778 in.close();
779 }
780 }
781 catch ( final IOException e )
782 {
783 if ( suppressExceptionOnClose )
784 {
785 this.project.log( Messages.getMessage( e ), e, Project.MSG_ERR );
786 }
787 else
788 {
789 throw e;
790 }
791 }
792 }
793 }
794
795 private boolean filterModlet( final Modlet modlet, final String resourceInfo )
796 {
797 boolean filteredSchemas = false;
798 boolean filteredServices = false;
799
800 if ( modlet.getSchemas() != null )
801 {
802 final Schemas schemas = new Schemas();
803
804 for ( final Schema s : modlet.getSchemas().getSchema() )
805 {
806 if ( !this.getSchemaExcludes().contains( s.getPublicId() ) )
807 {
808 schemas.getSchema().add( s );
809 }
810 else
811 {
812 this.getProject().log( Messages.getMessage( "schemaExclusion", resourceInfo, s.getPublicId() ),
813 Project.MSG_DEBUG );
814
815 this.addExcludedSchema( s );
816 filteredSchemas = true;
817 }
818 }
819
820 if ( filteredSchemas )
821 {
822 modlet.setSchemas( schemas );
823 }
824 }
825
826 if ( modlet.getServices() != null )
827 {
828 final Services services = new Services();
829
830 for ( final Service s : modlet.getServices().getService() )
831 {
832 if ( !this.getServiceExcludes().contains( s.getClazz() ) )
833 {
834 services.getService().add( s );
835 }
836 else
837 {
838 this.getProject().log( Messages.getMessage( "serviceExclusion", resourceInfo, s.getClazz() ),
839 Project.MSG_DEBUG );
840
841 this.addExcludedService( s );
842 filteredServices = true;
843 }
844 }
845
846 if ( filteredServices )
847 {
848 modlet.setServices( services );
849 }
850 }
851
852 return filteredSchemas || filteredServices;
853 }
854
855 private void addExcludedModlet( final Modlet modlet )
856 {
857 try
858 {
859 final Modlet m = this.getExcludedModlets().getModlet( modlet.getName() );
860
861 if ( m != null )
862 {
863 if ( m.getVersion() != null && modlet.getVersion() != null
864 && VersionParser.compare( m.getVersion(), modlet.getVersion() ) < 0 )
865 {
866 this.getExcludedModlets().getModlet().remove( m );
867 this.getExcludedModlets().getModlet().add( modlet );
868 }
869 }
870 else
871 {
872 this.getExcludedModlets().getModlet().add( modlet );
873 }
874 }
875 catch ( final ParseException e )
876 {
877 this.getProject().log( Messages.getMessage( e ), e, Project.MSG_WARN );
878 }
879 catch ( final TokenMgrError e )
880 {
881 this.getProject().log( Messages.getMessage( e ), e, Project.MSG_WARN );
882 }
883 }
884
885 private void addExcludedSchema( final Schema schema )
886 {
887 if ( this.getExcludedSchemas().getSchemaBySystemId( schema.getSystemId() ) == null )
888 {
889 this.getExcludedSchemas().getSchema().add( schema );
890 }
891 }
892
893 private void addExcludedService( final Service service )
894 {
895 for ( int i = 0, s0 = this.getExcludedServices().getService().size(); i < s0; i++ )
896 {
897 final Service s = this.getExcludedServices().getService().get( i );
898
899 if ( s.getIdentifier().equals( service.getIdentifier() ) && s.getClazz().equals( service.getClazz() ) )
900 {
901 return;
902 }
903 }
904
905 this.getExcludedServices().getService().add( service );
906 }
907
908 private static Set<String> readDefaultExcludes( final String location ) throws IOException
909 {
910 InputStream resource = null;
911 boolean suppressExceptionOnClose = true;
912 Set<String> defaultExcludes = null;
913
914 try
915 {
916 resource = ProjectClassLoader.class.getResourceAsStream( location );
917
918 if ( resource != null )
919 {
920 final List<String> lines = IOUtils.readLines( resource, "UTF-8" );
921 defaultExcludes = new HashSet<String>( lines.size() );
922
923 for ( final String line : lines )
924 {
925 final String trimmed = line.trim();
926
927 if ( trimmed.contains( "#" ) || StringUtils.isEmpty( trimmed ) )
928 {
929 continue;
930 }
931
932 defaultExcludes.add( trimmed );
933 }
934 }
935
936 suppressExceptionOnClose = false;
937 return defaultExcludes != null
938 ? Collections.unmodifiableSet( defaultExcludes ) : Collections.<String>emptySet();
939
940 }
941 finally
942 {
943 try
944 {
945 if ( resource != null )
946 {
947 resource.close();
948 }
949 }
950 catch ( final IOException e )
951 {
952 if ( !suppressExceptionOnClose )
953 {
954 throw e;
955 }
956 }
957 }
958 }
959
960 }