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