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 }