View Javadoc
1   /*
2    *   Copyright (C) Christian Schulte, 2005-206
3    *   All rights reserved.
4    *
5    *   Redistribution and use in source and binary forms, with or without
6    *   modification, are permitted provided that the following conditions
7    *   are met:
8    *
9    *     o Redistributions of source code must retain the above copyright
10   *       notice, this list of conditions and the following disclaimer.
11   *
12   *     o Redistributions in binary form must reproduce the above copyright
13   *       notice, this list of conditions and the following disclaimer in
14   *       the documentation and/or other materials provided with the
15   *       distribution.
16   *
17   *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
18   *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19   *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20   *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21   *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22   *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23   *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24   *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25   *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26   *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27   *
28   *   $JOMC: ProjectClassLoader.java 4613 2012-09-22 10:07:08Z schulte $
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   * Class loader supporting JOMC resources backed by a project.
73   *
74   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
75   * @version $JOMC: ProjectClassLoader.java 4613 2012-09-22 10:07:08Z schulte $
76   */
77  public class ProjectClassLoader extends URLClassLoader
78  {
79  
80      /** Constant to prefix relative resource names with. */
81      private static final String ABSOLUTE_RESOURCE_NAME_PREFIX = "/org/jomc/ant/";
82  
83      /** Empty URL array. */
84      private static final URL[] NO_URLS =
85      {
86      };
87  
88      /** Set of modlet names to exclude. */
89      private Set<String> modletExcludes;
90  
91      /** Excluded modlets. */
92      private Modlets excludedModlets;
93  
94      /** Set of service class names to exclude. */
95      private Set<String> serviceExcludes;
96  
97      /** Excluded services. */
98      private Services excludedServices;
99  
100     /** Set of schema public ids to exclude. */
101     private Set<String> schemaExcludes;
102 
103     /** Excluded schemas. */
104     private Schemas excludedSchemas;
105 
106     /** Set of providers to exclude. */
107     private Set<String> providerExcludes;
108 
109     /** Set of excluded providers. */
110     private Set<String> excludedProviders;
111 
112     /** The project the class loader is associated with. */
113     private final Project project;
114 
115     /** Set of modlet resource locations to filter. */
116     private Set<String> modletResourceLocations;
117 
118     /** Set of provider resource locations to filter. */
119     private Set<String> providerResourceLocations;
120 
121     /** Set of temporary resources. */
122     private final Set<File> temporaryResources = new HashSet<File>();
123 
124     /**
125      * Creates a new {@code ProjectClassLoader} instance taking a project and a class path.
126      *
127      * @param project The project to which this class loader is to belong.
128      * @param classpath The class path to use for loading.
129      *
130      * @throws MalformedURLException if {@code classpath} contains unsupported elements.
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      * Gets the project of the instance.
147      *
148      * @return The project of the instance.
149      */
150     public final Project getProject()
151     {
152         return this.project;
153     }
154 
155     /**
156      * Finds a resource with a given name.
157      *
158      * @param name The name of the resource to search.
159      *
160      * @return An {@code URL} object for reading the resource or {@code null}, if no resource matching {@code name} is
161      * found.
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      * Gets all resources matching a given name.
209      *
210      * @param name The name of the resources to get.
211      *
212      * @return An enumeration of {@code URL} objects of found resources.
213      *
214      * @throws IOException if getting resources fails.
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      * Gets a set of modlet resource locations to filter.
294      * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
295      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
296      * modlet resource locations property.</p>
297      *
298      * @return A set of modlet resource locations to filter.
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      * Gets a set of provider resource locations to filter.
312      * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
313      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
314      * provider resource locations property.</p>
315      *
316      * @return A set of provider resource locations to filter.
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      * Gets a set of modlet names to exclude.
330      * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
331      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
332      * modlet excludes property.</p>
333      *
334      * @return A set of modlet names to exclude.
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      * Gets a set of modlet names excluded by default.
348      *
349      * @return An unmodifiable set of modlet names excluded by default.
350      *
351      * @throws IOException if reading configuration resources fails.
352      */
353     public static Set<String> getDefaultModletExcludes() throws IOException
354     {
355         return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultModletExcludes" );
356     }
357 
358     /**
359      * Gets a set of modlets excluded during resource loading.
360      * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
361      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
362      * excluded modlets property.</p>
363      *
364      * @return A set of modlets excluded during resource loading.
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      * Gets a set of provider names to exclude.
378      * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
379      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
380      * provider excludes property.</p>
381      *
382      * @return A set of providers to exclude.
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      * Gets a set of provider names excluded by default.
396      *
397      * @return An unmodifiable set of provider names excluded by default.
398      *
399      * @throws IOException if reading configuration resources fails.
400      */
401     public static Set<String> getDefaultProviderExcludes() throws IOException
402     {
403         return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultProviderExcludes" );
404     }
405 
406     /**
407      * Gets a set of providers excluded during resource loading.
408      * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
409      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
410      * excluded providers property.</p>
411      *
412      * @return A set of providers excluded during resource loading.
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      * Gets a set of service class names to exclude.
426      * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
427      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
428      * service excludes property.</p>
429      *
430      * @return A set of service class names to exclude.
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      * Gets a set of service class names excluded by default.
444      *
445      * @return An unmodifiable set of service class names excluded by default.
446      *
447      * @throws IOException if reading configuration resources fails.
448      */
449     public static Set<String> getDefaultServiceExcludes() throws IOException
450     {
451         return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultServiceExcludes" );
452     }
453 
454     /**
455      * Gets a set of services excluded during resource loading.
456      * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
457      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
458      * excluded services property.</p>
459      *
460      * @return Services excluded during resource loading.
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      * Gets a set of schema public identifiers to exclude.
474      * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
475      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
476      * schema excludes property.</p>
477      *
478      * @return A set of schema public identifiers to exclude.
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      * Gets a set of schema public identifiers excluded by default.
492      *
493      * @return An unmodifiable set of schema public identifiers excluded by default.
494      *
495      * @throws IOException if reading configuration resources fails.
496      */
497     public static Set<String> getDefaultSchemaExcludes() throws IOException
498     {
499         return readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultSchemaExcludes" );
500     }
501 
502     /**
503      * Gets a set of schemas excluded during resource loading.
504      * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
505      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
506      * excluded schemas property.</p>
507      *
508      * @return Schemas excluded during resource loading.
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      * Closes the class loader.
522      * @throws IOException if closing the class loader fails.
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      * Removes temporary resources.
546      * @throws Throwable if finalization fails.
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 }