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.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
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 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 {
538 this.jdk7Close();
539 }
540 }
541
542
543
544
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 }