View Javadoc
1   /*
2    * Copyright (C) 2009 Christian Schulte <cs@schulte.it>
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: AbstractModletCommand.java 5303 2016-08-30 02:31:20Z schulte $
29   *
30   */
31  package org.jomc.cli.commands;
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.FileReader;
39  import java.io.IOException;
40  import java.io.InputStream;
41  import java.io.InputStreamReader;
42  import java.io.OutputStream;
43  import java.io.OutputStreamWriter;
44  import java.io.StringWriter;
45  import java.net.MalformedURLException;
46  import java.net.URI;
47  import java.net.URISyntaxException;
48  import java.net.URL;
49  import java.net.URLClassLoader;
50  import java.util.Collections;
51  import java.util.Enumeration;
52  import java.util.HashSet;
53  import java.util.Iterator;
54  import java.util.LinkedList;
55  import java.util.List;
56  import java.util.Map;
57  import java.util.Set;
58  import java.util.logging.Level;
59  import javax.xml.bind.JAXBElement;
60  import javax.xml.bind.JAXBException;
61  import javax.xml.bind.Marshaller;
62  import javax.xml.bind.PropertyException;
63  import javax.xml.transform.ErrorListener;
64  import javax.xml.transform.Source;
65  import javax.xml.transform.Transformer;
66  import javax.xml.transform.TransformerConfigurationException;
67  import javax.xml.transform.TransformerException;
68  import javax.xml.transform.TransformerFactory;
69  import org.apache.commons.cli.CommandLine;
70  import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
71  import org.jomc.model.ModelObject;
72  import org.jomc.modlet.DefaultModelContext;
73  import org.jomc.modlet.DefaultModletProvider;
74  import org.jomc.modlet.ModelContext;
75  import org.jomc.modlet.ModelContextFactory;
76  import org.jomc.modlet.ModelException;
77  import org.jomc.modlet.ModelValidationReport;
78  import org.jomc.modlet.Modlet;
79  import org.jomc.modlet.ModletObject;
80  import org.jomc.modlet.ModletProcessor;
81  import org.jomc.modlet.ModletProvider;
82  import org.jomc.modlet.ModletValidator;
83  import org.jomc.modlet.Modlets;
84  import org.jomc.modlet.ObjectFactory;
85  import org.jomc.modlet.Schema;
86  import org.jomc.modlet.Schemas;
87  import org.jomc.modlet.Service;
88  import org.jomc.modlet.ServiceFactory;
89  import org.jomc.modlet.Services;
90  
91  /**
92   * {@code ModelContext} based command implementation.
93   *
94   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
95   */
96  public abstract class AbstractModletCommand extends AbstractCommand
97  {
98  
99      /**
100      * Constant to prefix relative resource names with.
101      */
102     private static final String ABSOLUTE_RESOURCE_NAME_PREFIX =
103         "/" + AbstractModletCommand.class.getPackage().getName().replace( '.', '/' ) + "/";
104 
105     /**
106      * Creates a new {@code AbstractModletCommand} instance.
107      */
108     public AbstractModletCommand()
109     {
110         super();
111     }
112 
113     @Override
114     public org.apache.commons.cli.Options getOptions()
115     {
116         final org.apache.commons.cli.Options options = super.getOptions();
117         options.addOption( Options.CLASSPATH_OPTION );
118         options.addOption( Options.DOCUMENTS_OPTION );
119         options.addOption( Options.MODEL_CONTEXT_FACTORY_CLASSNAME_OPTION );
120         options.addOption( Options.MODEL_OPTION );
121         options.addOption( Options.MODLET_SCHEMA_SYSTEM_ID_OPTION );
122         options.addOption( Options.MODLET_LOCATION_OPTION );
123         options.addOption( Options.PROVIDER_LOCATION_OPTION );
124         options.addOption( Options.PLATFORM_PROVIDER_LOCATION_OPTION );
125         options.addOption( Options.NO_MODLET_RESOURCE_VALIDATION_OPTION );
126         return options;
127     }
128 
129     /**
130      * Creates a new {@code Transformer} from a given {@code Source}.
131      *
132      * @param source The source to initialize the transformer with.
133      *
134      * @return A {@code Transformer} backed by {@code source}.
135      *
136      * @throws NullPointerException if {@code source} is {@code null}.
137      * @throws CommandExecutionException if creating a transformer fails.
138      */
139     protected Transformer createTransformer( final Source source ) throws CommandExecutionException
140     {
141         if ( source == null )
142         {
143             throw new NullPointerException( "source" );
144         }
145 
146         final ErrorListener errorListener = new ErrorListener()
147         {
148 
149             public void warning( final TransformerException exception ) throws TransformerException
150             {
151                 log( Level.WARNING, null, exception );
152             }
153 
154             public void error( final TransformerException exception ) throws TransformerException
155             {
156                 throw exception;
157             }
158 
159             public void fatalError( final TransformerException exception ) throws TransformerException
160             {
161                 throw exception;
162             }
163 
164         };
165 
166         try
167         {
168             final TransformerFactory transformerFactory = TransformerFactory.newInstance();
169             transformerFactory.setErrorListener( errorListener );
170             final Transformer transformer = transformerFactory.newTransformer( source );
171             transformer.setErrorListener( errorListener );
172 
173             for ( final Map.Entry<Object, Object> e : System.getProperties().entrySet() )
174             {
175                 transformer.setParameter( e.getKey().toString(), e.getValue() );
176             }
177 
178             return transformer;
179         }
180         catch ( final TransformerConfigurationException e )
181         {
182             throw new CommandExecutionException( Messages.getMessage( e ), e );
183         }
184     }
185 
186     /**
187      * Creates a new {@code ModelContext} for a given {@code CommandLine} and {@code ClassLoader}.
188      *
189      * @param commandLine The {@code CommandLine} to create a new {@code ModelContext} with.
190      * @param classLoader The {@code ClassLoader} to create a new {@code ModelContext} with.
191      *
192      * @return A new {@code ModelContext} for {@code classLoader} setup using {@code commandLine}.
193      *
194      * @throws NullPointerException if {@code commandLine} is {@code null}.
195      * @throws CommandExecutionException if creating an new {@code ModelContext} fails.
196      */
197     protected ModelContext createModelContext( final CommandLine commandLine, final ClassLoader classLoader )
198         throws CommandExecutionException
199     {
200         if ( commandLine == null )
201         {
202             throw new NullPointerException( "commandLine" );
203         }
204 
205         final ModelContextFactory modelContextFactory =
206             commandLine.hasOption( Options.MODEL_CONTEXT_FACTORY_CLASSNAME_OPTION.getOpt() )
207                 ? ModelContextFactory.newInstance( commandLine.getOptionValue(
208                 Options.MODEL_CONTEXT_FACTORY_CLASSNAME_OPTION.getOpt() ) )
209                 : ModelContextFactory.newInstance();
210 
211         final ModelContext modelContext = modelContextFactory.newModelContext( classLoader );
212 
213         modelContext.setExecutorService( this.getExecutorService( commandLine ) );
214 
215         if ( commandLine.hasOption( Options.MODLET_SCHEMA_SYSTEM_ID_OPTION.getOpt() ) )
216         {
217             modelContext.setModletSchemaSystemId(
218                 commandLine.getOptionValue( Options.MODLET_SCHEMA_SYSTEM_ID_OPTION.getOpt() ) );
219 
220         }
221 
222         modelContext.setLogLevel( this.getLogLevel() );
223         modelContext.getListeners().add( new ModelContext.Listener()
224         {
225 
226             @Override
227             public void onLog( final Level level, final String message, final Throwable t )
228             {
229                 super.onLog( level, message, t );
230                 log( level, message, t );
231             }
232 
233         } );
234 
235         if ( commandLine.hasOption( Options.PROVIDER_LOCATION_OPTION.getOpt() ) )
236         {
237             modelContext.setAttribute( DefaultModelContext.PROVIDER_LOCATION_ATTRIBUTE_NAME,
238                                        commandLine.getOptionValue( Options.PROVIDER_LOCATION_OPTION.getOpt() ) );
239 
240         }
241 
242         if ( commandLine.hasOption( Options.PLATFORM_PROVIDER_LOCATION_OPTION.getOpt() ) )
243         {
244             modelContext.setAttribute(
245                 DefaultModelContext.PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME,
246                 commandLine.getOptionValue( Options.PLATFORM_PROVIDER_LOCATION_OPTION.getOpt() ) );
247 
248         }
249 
250         if ( commandLine.hasOption( Options.MODLET_LOCATION_OPTION.getOpt() ) )
251         {
252             modelContext.setAttribute( DefaultModletProvider.MODLET_LOCATION_ATTRIBUTE_NAME,
253                                        commandLine.getOptionValue( Options.MODLET_LOCATION_OPTION.getOpt() ) );
254 
255         }
256 
257         modelContext.setAttribute( DefaultModletProvider.VALIDATING_ATTRIBUTE_NAME,
258                                    !commandLine.hasOption( Options.NO_MODLET_RESOURCE_VALIDATION_OPTION.getOpt() ) );
259 
260         return modelContext;
261     }
262 
263     /**
264      * Gets the identifier of the model to process.
265      *
266      * @param commandLine The command line to get the identifier of the model to process from.
267      *
268      * @return The identifier of the model to process.
269      *
270      * @throws NullPointerException if {@code commandLine} is {@code null}.
271      */
272     protected String getModel( final CommandLine commandLine )
273     {
274         if ( commandLine == null )
275         {
276             throw new NullPointerException( "commandLine" );
277         }
278 
279         return commandLine.hasOption( Options.MODEL_OPTION.getOpt() )
280                    ? commandLine.getOptionValue( Options.MODEL_OPTION.getOpt() )
281                    : ModelObject.MODEL_PUBLIC_ID;
282 
283     }
284 
285     /**
286      * Logs a validation report.
287      *
288      * @param validationReport The report to log.
289      * @param marshaller The marshaller to use for logging the report.
290      *
291      * @throws CommandExecutionException if logging a report detail element fails.
292      */
293     protected void log( final ModelValidationReport validationReport, final Marshaller marshaller )
294         throws CommandExecutionException
295     {
296         Object jaxbFormattedOutput;
297         try
298         {
299             jaxbFormattedOutput = marshaller.getProperty( Marshaller.JAXB_FORMATTED_OUTPUT );
300             marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
301         }
302         catch ( final PropertyException e )
303         {
304             this.log( Level.INFO, Messages.getMessage( e ), e );
305             jaxbFormattedOutput = null;
306         }
307 
308         try
309         {
310             for ( final ModelValidationReport.Detail d : validationReport.getDetails() )
311             {
312                 if ( this.isLoggable( d.getLevel() ) )
313                 {
314                     this.log( d.getLevel(), "o " + d.getMessage(), null );
315 
316                     if ( d.getElement() != null && this.getLogLevel().intValue() < Level.INFO.intValue() )
317                     {
318                         final StringWriter stringWriter = new StringWriter();
319                         marshaller.marshal( d.getElement(), stringWriter );
320                         this.log( d.getLevel(), stringWriter.toString(), null );
321                     }
322                 }
323             }
324         }
325         catch ( final JAXBException e )
326         {
327             String message = Messages.getMessage( e );
328             if ( message == null )
329             {
330                 message = Messages.getMessage( e.getLinkedException() );
331             }
332 
333             throw new CommandExecutionException( message, e );
334         }
335         finally
336         {
337             try
338             {
339                 marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, jaxbFormattedOutput );
340             }
341             catch ( final PropertyException e )
342             {
343                 this.log( Level.INFO, Messages.getMessage( e ), e );
344             }
345         }
346     }
347 
348     /**
349      * Gets the document files specified by a given command line.
350      *
351      * @param commandLine The command line specifying the document files to get.
352      *
353      * @return The document files specified by {@code commandLine}.
354      *
355      * @throws CommandExecutionException if getting the document files fails.
356      */
357     protected Set<File> getDocumentFiles( final CommandLine commandLine ) throws CommandExecutionException
358     {
359         try
360         {
361             final Set<File> files = new HashSet<File>( 128 );
362 
363             if ( commandLine.hasOption( Options.DOCUMENTS_OPTION.getOpt() ) )
364             {
365                 final String[] elements = commandLine.getOptionValues( Options.DOCUMENTS_OPTION.getOpt() );
366 
367                 if ( elements != null )
368                 {
369                     for ( final String e : elements )
370                     {
371                         if ( e.startsWith( "@" ) )
372                         {
373                             final File file = new File( e.substring( 1 ) );
374                             BufferedReader reader = null;
375 
376                             try
377                             {
378                                 reader = new BufferedReader( new FileReader( file ) );
379 
380                                 for ( String line = reader.readLine(); line != null; line = reader.readLine() )
381                                 {
382                                     line = line.trim();
383 
384                                     if ( !line.startsWith( "#" ) )
385                                     {
386                                         final File f = new File( line );
387 
388                                         if ( f.exists() )
389                                         {
390                                             if ( this.isLoggable( Level.FINER ) )
391                                             {
392                                                 this.log( Level.FINER,
393                                                           Messages.getMessage( "documentFileInfo",
394                                                                                f.getAbsolutePath() ),
395                                                           null );
396 
397                                             }
398 
399                                             files.add( f );
400                                         }
401                                         else if ( this.isLoggable( Level.WARNING ) )
402                                         {
403                                             this.log( Level.WARNING,
404                                                       Messages.getMessage( "documentFileNotFoundWarning",
405                                                                            f.getAbsolutePath() ),
406                                                       null );
407 
408                                         }
409                                     }
410                                 }
411 
412                                 reader.close();
413                                 reader = null;
414                             }
415                             finally
416                             {
417                                 try
418                                 {
419                                     if ( reader != null )
420                                     {
421                                         reader.close();
422                                     }
423                                 }
424                                 catch ( final IOException ex )
425                                 {
426                                     this.log( Level.SEVERE, Messages.getMessage( ex ), ex );
427                                 }
428                             }
429                         }
430                         else
431                         {
432                             final File file = new File( e );
433 
434                             if ( file.exists() )
435                             {
436                                 if ( this.isLoggable( Level.FINER ) )
437                                 {
438                                     this.log( Level.FINER,
439                                               Messages.getMessage( "documentFileInfo", file.getAbsolutePath() ),
440                                               null );
441 
442                                 }
443 
444                                 files.add( file );
445                             }
446                             else if ( this.isLoggable( Level.WARNING ) )
447                             {
448                                 this.log( Level.WARNING,
449                                           Messages.getMessage( "documentFileNotFoundWarning", file.getAbsolutePath() ),
450                                           null );
451 
452                             }
453                         }
454                     }
455                 }
456             }
457 
458             return files;
459         }
460         catch ( final IOException e )
461         {
462             throw new CommandExecutionException( Messages.getMessage( e ), e );
463         }
464     }
465 
466     /**
467      * Class loader backed by a command line.
468      *
469      * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
470      * @version $JOMC: AbstractModletCommand.java 5303 2016-08-30 02:31:20Z schulte $
471      */
472     public class CommandLineClassLoader extends URLClassLoader
473     {
474 
475         /**
476          * {@code Modlets} excluded by the instance.
477          */
478         private final Modlets excludedModlets = new Modlets();
479 
480         /**
481          * Set of provider resource locations to filter.
482          */
483         private final Set<String> providerResourceLocations = new HashSet<String>( 128 );
484 
485         /**
486          * Set of modlet resource locations to filter.
487          */
488         private final Set<String> modletResourceLocations = new HashSet<String>( 128 );
489 
490         /**
491          * Set of temporary resources.
492          */
493         private final Set<File> temporaryResources = new HashSet<File>( 128 );
494 
495         /**
496          * Creates a new {@code CommandLineClassLoader} taking a command line backing the class loader.
497          *
498          * @param commandLine The command line backing the class loader.
499          *
500          * @throws NullPointerException if {@code commandLine} is {@code null}.
501          * @throws CommandExecutionException if processing {@code commandLine} fails.
502          */
503         public CommandLineClassLoader( final CommandLine commandLine ) throws CommandExecutionException
504         {
505             super( new URL[ 0 ] );
506 
507             try
508             {
509                 if ( commandLine.hasOption( Options.CLASSPATH_OPTION.getOpt() ) )
510                 {
511                     final Set<URI> uris = new HashSet<URI>( 128 );
512                     final String[] elements = commandLine.getOptionValues( Options.CLASSPATH_OPTION.getOpt() );
513 
514                     if ( elements != null )
515                     {
516                         for ( final String e : elements )
517                         {
518                             if ( e.startsWith( "@" ) )
519                             {
520                                 final File file = new File( e.substring( 1 ) );
521                                 BufferedReader reader = null;
522 
523                                 try
524                                 {
525                                     reader = new BufferedReader( new FileReader( file ) );
526 
527                                     for ( String line = reader.readLine(); line != null; line = reader.readLine() )
528                                     {
529                                         line = line.trim();
530 
531                                         if ( !line.startsWith( "#" ) )
532                                         {
533                                             final File f = new File( line );
534 
535                                             if ( f.exists() )
536                                             {
537                                                 uris.add( f.toURI() );
538                                             }
539                                             else if ( isLoggable( Level.WARNING ) )
540                                             {
541                                                 log( Level.WARNING,
542                                                      Messages.getMessage( "classpathElementNotFoundWarning",
543                                                                           f.getAbsolutePath() ),
544                                                      null );
545 
546                                             }
547                                         }
548                                     }
549 
550                                     reader.close();
551                                     reader = null;
552                                 }
553                                 finally
554                                 {
555                                     try
556                                     {
557                                         if ( reader != null )
558                                         {
559                                             reader.close();
560                                         }
561                                     }
562                                     catch ( final IOException ex )
563                                     {
564                                         log( Level.SEVERE, Messages.getMessage( ex ), ex );
565                                     }
566                                 }
567                             }
568                             else
569                             {
570                                 final File file = new File( e );
571 
572                                 if ( file.exists() )
573                                 {
574                                     uris.add( file.toURI() );
575                                 }
576                                 else if ( isLoggable( Level.WARNING ) )
577                                 {
578                                     log( Level.WARNING,
579                                          Messages.getMessage( "classpathElementNotFoundWarning",
580                                                               file.getAbsolutePath() ),
581                                          null );
582 
583                                 }
584                             }
585                         }
586                     }
587 
588                     for ( final URI uri : uris )
589                     {
590                         if ( isLoggable( Level.FINEST ) )
591                         {
592                             log( Level.FINEST,
593                                  Messages.getMessage( "classpathElementInfo", uri.toASCIIString() ),
594                                  null );
595 
596                         }
597 
598                         this.addURL( uri.toURL() );
599                     }
600 
601                     // Assumes the default modlet location matches the location of resources of the applications'
602                     // dependencies.
603                     this.modletResourceLocations.add( DefaultModletProvider.getDefaultModletLocation() );
604 
605                     // Assumes the default provider location matches the location of resources of the applications'
606                     // dependencies.
607                     final String providerLocationPrefix = DefaultModelContext.getDefaultProviderLocation() + "/";
608                     this.providerResourceLocations.add( providerLocationPrefix + ModletProcessor.class.getName() );
609                     this.providerResourceLocations.add( providerLocationPrefix + ModletProvider.class.getName() );
610                     this.providerResourceLocations.add( providerLocationPrefix + ModletValidator.class.getName() );
611                     this.providerResourceLocations.add( providerLocationPrefix + ServiceFactory.class.getName() );
612                 }
613             }
614             catch ( final IOException e )
615             {
616                 throw new CommandExecutionException( Messages.getMessage( e ), e );
617             }
618         }
619 
620         /**
621          * Gets the {@code Modlets} excluded by the instance.
622          *
623          * @return The {@code Modlets} excluded by the instance.
624          */
625         public Modlets getExcludedModlets()
626         {
627             return this.excludedModlets;
628         }
629 
630         /**
631          * Finds a resource with a given name.
632          *
633          * @param name The name of the resource to search.
634          *
635          * @return An {@code URL} object for reading the resource or {@code null}, if no resource matching {@code name} is
636          * found.
637          */
638         @Override
639         public URL findResource( final String name ) //JDK: As of JDK 23 throws IOException
640         {
641             try
642             {
643                 URL resource = super.findResource( name );
644 
645                 if ( resource != null )
646                 {
647                     if ( this.providerResourceLocations.contains( name ) )
648                     {
649                         resource = this.filterProviders( resource );
650                     }
651                     else if ( this.modletResourceLocations.contains( name ) )
652                     {
653                         resource = this.filterModlets( resource );
654                     }
655                 }
656 
657                 return resource;
658             }
659             catch ( final IOException e )
660             {
661                 log( Level.SEVERE, Messages.getMessage( e ), e );
662                 return null;
663             }
664             catch ( final JAXBException e )
665             {
666                 log( Level.SEVERE, Messages.getMessage( e ), e );
667                 return null;
668             }
669             catch ( final ModelException e )
670             {
671                 log( Level.SEVERE, Messages.getMessage( e ), e );
672                 return null;
673             }
674         }
675 
676         /**
677          * Finds all resources matching a given name.
678          *
679          * @param name The name of the resources to search.
680          *
681          * @return An enumeration of {@code URL} objects of resources matching name.
682          *
683          * @throws IOException if getting resources fails.
684          */
685         @Override
686         public Enumeration<URL> findResources( final String name ) throws IOException
687         {
688             try
689             {
690                 Enumeration<URL> resources = super.findResources( name );
691 
692                 if ( this.providerResourceLocations.contains( name )
693                          || this.modletResourceLocations.contains( name ) )
694                 {
695                     final List<URI> filtered = new LinkedList<URI>();
696 
697                     while ( resources.hasMoreElements() )
698                     {
699                         final URL resource = resources.nextElement();
700 
701                         if ( this.providerResourceLocations.contains( name ) )
702                         {
703                             filtered.add( this.filterProviders( resource ).toURI() );
704                         }
705                         else if ( this.modletResourceLocations.contains( name ) )
706                         {
707                             filtered.add( this.filterModlets( resource ).toURI() );
708                         }
709                     }
710 
711                     final Iterator<URI> it = filtered.iterator();
712 
713                     resources = new Enumeration<URL>()
714                     {
715 
716                         public boolean hasMoreElements()
717                         {
718                             return it.hasNext();
719                         }
720 
721                         public URL nextElement()
722                         {
723                             try
724                             {
725                                 return it.next().toURL();
726                             }
727                             catch ( final MalformedURLException e )
728                             {
729                                 throw new AssertionError( e );
730                             }
731                         }
732 
733                     };
734                 }
735 
736                 return resources;
737             }
738             catch ( final URISyntaxException e )
739             {
740                 // JDK: As of JDK 6, new IOException( message, e );
741                 throw (IOException) new IOException( Messages.getMessage( e ) ).initCause( e );
742             }
743             catch ( final JAXBException e )
744             {
745                 String message = Messages.getMessage( e );
746                 if ( message == null && e.getLinkedException() != null )
747                 {
748                     message = Messages.getMessage( e.getLinkedException() );
749                 }
750 
751                 // JDK: As of JDK 6, new IOException( message, e );
752                 throw (IOException) new IOException( message ).initCause( e );
753             }
754             catch ( final ModelException e )
755             {
756                 // JDK: As of JDK 6, new IOException( message, e );
757                 throw (IOException) new IOException( Messages.getMessage( e ) ).initCause( e );
758             }
759         }
760 
761         /**
762          * Closes the class loader.
763          *
764          * @throws IOException if closing the class loader fails.
765          */
766         @Override
767         @IgnoreJRERequirement
768         public void close() throws IOException
769         {
770             for ( final Iterator<File> it = this.temporaryResources.iterator(); it.hasNext(); )
771             {
772                 final File temporaryResource = it.next();
773 
774                 if ( temporaryResource.exists() && temporaryResource.delete() )
775                 {
776                     it.remove();
777                 }
778             }
779 
780             if ( Closeable.class.isAssignableFrom( CommandLineClassLoader.class ) )
781             {
782                 super.close();
783             }
784         }
785 
786         /**
787          * Removes temporary resources.
788          *
789          * @throws Throwable if finalization fails.
790          */
791         @Override
792         protected void finalize() throws Throwable
793         {
794             for ( final Iterator<File> it = this.temporaryResources.iterator(); it.hasNext(); )
795             {
796                 final File temporaryResource = it.next();
797 
798                 if ( temporaryResource.exists() && !temporaryResource.delete() )
799                 {
800                     temporaryResource.deleteOnExit();
801                 }
802 
803                 it.remove();
804             }
805 
806             super.finalize();
807         }
808 
809         private URL filterProviders( final URL resource ) throws IOException
810         {
811             InputStream in = null;
812             BufferedReader reader = null;
813             OutputStream out = null;
814             BufferedWriter writer = null;
815             final Set<String> providerExcludes = this.getProviderExcludes();
816             final List<String> lines = new LinkedList<String>();
817 
818             try
819             {
820                 URL filteredResource = resource;
821                 boolean filtered = false;
822                 in = resource.openStream();
823                 reader = new BufferedReader( new InputStreamReader( in, "UTF-8" ) );
824 
825                 for ( String line = reader.readLine(); line != null; line = reader.readLine() )
826                 {
827                     if ( !providerExcludes.contains( line.trim() ) )
828                     {
829                         lines.add( line );
830                     }
831                     else
832                     {
833                         filtered = true;
834                         log( Level.FINE,
835                              Messages.getMessage( "providerExclusionInfo", resource.toExternalForm(), line ),
836                              null );
837 
838                     }
839                 }
840 
841                 reader.close();
842                 reader = null;
843                 in = null;
844 
845                 if ( filtered )
846                 {
847                     final File tmpResource = File.createTempFile( this.getClass().getName(), ".rsrc" );
848                     this.temporaryResources.add( tmpResource );
849 
850                     out = new FileOutputStream( tmpResource );
851                     writer = new BufferedWriter( new OutputStreamWriter( out, "UTF-8" ) );
852 
853                     for ( final String line : lines )
854                     {
855                         writer.write( line );
856                         writer.newLine();
857                     }
858 
859                     writer.close();
860                     writer = null;
861                     out = null;
862 
863                     filteredResource = tmpResource.toURI().toURL();
864                 }
865 
866                 return filteredResource;
867             }
868             finally
869             {
870                 try
871                 {
872                     if ( reader != null )
873                     {
874                         reader.close();
875                     }
876                 }
877                 catch ( final IOException e )
878                 {
879                     log( Level.SEVERE, Messages.getMessage( e ), e );
880                 }
881                 finally
882                 {
883                     try
884                     {
885                         if ( in != null )
886                         {
887                             in.close();
888                         }
889                     }
890                     catch ( final IOException e )
891                     {
892                         log( Level.SEVERE, Messages.getMessage( e ), e );
893                     }
894                     finally
895                     {
896                         try
897                         {
898                             if ( writer != null )
899                             {
900                                 writer.close();
901                             }
902                         }
903                         catch ( final IOException e )
904                         {
905                             log( Level.SEVERE, Messages.getMessage( e ), e );
906                         }
907                         finally
908                         {
909                             try
910                             {
911                                 if ( out != null )
912                                 {
913                                     out.close();
914                                 }
915                             }
916                             catch ( final IOException e )
917                             {
918                                 log( Level.SEVERE, Messages.getMessage( e ), e );
919                             }
920                         }
921                     }
922                 }
923             }
924         }
925 
926         private URL filterModlets( final URL resource ) throws ModelException, IOException, JAXBException
927         {
928             URL filteredResource = resource;
929             final Set<String> excludedModletNames = this.getModletExcludes();
930             final ModelContext modelContext = ModelContextFactory.newInstance().newModelContext();
931             Object o = modelContext.createUnmarshaller( ModletObject.MODEL_PUBLIC_ID ).unmarshal( resource );
932             if ( o instanceof JAXBElement<?> )
933             {
934                 o = ( (JAXBElement<?>) o ).getValue();
935             }
936 
937             Modlets modlets = null;
938             boolean filtered = false;
939 
940             if ( o instanceof Modlets )
941             {
942                 modlets = (Modlets) o;
943             }
944             else if ( o instanceof Modlet )
945             {
946                 modlets = new Modlets();
947                 modlets.getModlet().add( (Modlet) o );
948             }
949 
950             if ( modlets != null )
951             {
952                 for ( final Iterator<Modlet> it = modlets.getModlet().iterator(); it.hasNext(); )
953                 {
954                     final Modlet m = it.next();
955 
956                     if ( excludedModletNames.contains( m.getName() ) )
957                     {
958                         it.remove();
959                         filtered = true;
960                         synchronized ( this )
961                         {
962                             this.getExcludedModlets().getModlet().add( m );
963                         }
964                         log( Level.FINE,
965                              Messages.getMessage( "modletExclusionInfo", resource.toExternalForm(), m.getName() ),
966                              null );
967 
968                         continue;
969                     }
970 
971                     if ( this.filterModlet( m, resource.toExternalForm() ) )
972                     {
973                         filtered = true;
974                     }
975                 }
976 
977                 if ( filtered )
978                 {
979                     final File tmpResource = File.createTempFile( this.getClass().getName(), ".rsrc" );
980                     this.temporaryResources.add( tmpResource );
981                     modelContext.createMarshaller( ModletObject.MODEL_PUBLIC_ID ).
982                         marshal( new ObjectFactory().createModlets( modlets ), tmpResource );
983 
984                     filteredResource = tmpResource.toURI().toURL();
985                 }
986             }
987 
988             return filteredResource;
989         }
990 
991         private boolean filterModlet( final Modlet modlet, final String resourceInfo ) throws IOException
992         {
993             boolean filteredSchemas = false;
994             boolean filteredServices = false;
995             final Set<String> excludedSchemas = this.getSchemaExcludes();
996             final Set<String> excludedServices = this.getServiceExcludes();
997 
998             if ( modlet.getSchemas() != null )
999             {
1000                 final Schemas schemas = new Schemas();
1001 
1002                 for ( final Schema s : modlet.getSchemas().getSchema() )
1003                 {
1004                     if ( !excludedSchemas.contains( s.getPublicId() ) )
1005                     {
1006                         schemas.getSchema().add( s );
1007                     }
1008                     else
1009                     {
1010                         log( Level.FINE,
1011                              Messages.getMessage( "schemaExclusionInfo", resourceInfo, s.getContextId() ),
1012                              null );
1013 
1014                         filteredSchemas = true;
1015                     }
1016                 }
1017 
1018                 if ( filteredSchemas )
1019                 {
1020                     modlet.setSchemas( schemas );
1021                 }
1022             }
1023 
1024             if ( modlet.getServices() != null )
1025             {
1026                 final Services services = new Services();
1027 
1028                 for ( final Service s : modlet.getServices().getService() )
1029                 {
1030                     if ( !excludedServices.contains( s.getClazz() ) )
1031                     {
1032                         services.getService().add( s );
1033                     }
1034                     else
1035                     {
1036                         log( Level.FINE,
1037                              Messages.getMessage( "serviceExclusionInfo", resourceInfo, s.getClazz() ),
1038                              null );
1039 
1040                         filteredServices = true;
1041                     }
1042                 }
1043 
1044                 if ( filteredServices )
1045                 {
1046                     modlet.setServices( services );
1047                 }
1048             }
1049 
1050             return filteredSchemas || filteredServices;
1051         }
1052 
1053         /**
1054          * Gets a set of modlet names to filter.
1055          *
1056          * @return An unmodifiable set of modlet names to filter.
1057          *
1058          * @throws IOException if reading configuration resources fails.
1059          */
1060         private Set<String> getModletExcludes() throws IOException
1061         {
1062             return this.readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultModletExcludes" );
1063         }
1064 
1065         /**
1066          * Gets a set of provider names to filter.
1067          *
1068          * @return An unmodifiable set of provider names to filter.
1069          *
1070          * @throws IOException if reading configuration resources fails.
1071          */
1072         private Set<String> getProviderExcludes() throws IOException
1073         {
1074             return this.readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultProviderExcludes" );
1075         }
1076 
1077         /**
1078          * Gets a set of service class names to filter.
1079          *
1080          * @return An unmodifiable set of service class names to filter.
1081          *
1082          * @throws IOException if reading configuration resources fails.
1083          */
1084         private Set<String> getServiceExcludes() throws IOException
1085         {
1086             return this.readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultServiceExcludes" );
1087         }
1088 
1089         /**
1090          * Gets a set of schema public identifiers excluded by default.
1091          *
1092          * @return An unmodifiable set of schema public identifiers excluded by default.
1093          *
1094          * @throws IOException if reading configuration resources fails.
1095          */
1096         private Set<String> getSchemaExcludes() throws IOException
1097         {
1098             return this.readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultSchemaExcludes" );
1099         }
1100 
1101         private Set<String> readDefaultExcludes( final String location ) throws IOException
1102         {
1103             InputStream in = null;
1104             BufferedReader reader = null;
1105             final Set<String> defaultExcludes = new HashSet<String>();
1106 
1107             try
1108             {
1109                 in = CommandLineClassLoader.class.getResourceAsStream( location );
1110                 assert in != null : "Expected resource '" + location + "' not found.";
1111                 reader = new BufferedReader( new InputStreamReader( in, "UTF-8" ) );
1112 
1113                 for ( String line = reader.readLine(); line != null; line = reader.readLine() )
1114                 {
1115                     final String normalized = line.trim();
1116 
1117                     if ( normalized.length() > 0 && !normalized.contains( "#" ) )
1118                     {
1119                         defaultExcludes.add( normalized );
1120                     }
1121                 }
1122 
1123                 reader.close();
1124                 reader = null;
1125                 in = null;
1126 
1127                 return Collections.unmodifiableSet( defaultExcludes );
1128             }
1129             finally
1130             {
1131                 try
1132                 {
1133                     if ( reader != null )
1134                     {
1135                         reader.close();
1136                     }
1137                 }
1138                 catch ( final IOException e )
1139                 {
1140                     // Suppressed.
1141                 }
1142                 finally
1143                 {
1144                     try
1145                     {
1146                         if ( in != null )
1147                         {
1148                             in.close();
1149                         }
1150                     }
1151                     catch ( final IOException e )
1152                     {
1153                         // Suppressed.
1154                     }
1155                 }
1156             }
1157         }
1158 
1159     }
1160 
1161 }