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: JomcContainerDescriptorHandler.java 4987 2014-11-15 19:51:47Z schulte $
29   *
30   */
31  package org.jomc.mojo;
32  
33  import java.io.File;
34  import java.io.IOException;
35  import java.io.InputStream;
36  import java.net.MalformedURLException;
37  import java.net.URISyntaxException;
38  import java.net.URL;
39  import java.util.Collections;
40  import java.util.Iterator;
41  import java.util.LinkedList;
42  import java.util.List;
43  import java.util.Map;
44  import javax.xml.bind.JAXBElement;
45  import javax.xml.bind.JAXBException;
46  import javax.xml.bind.Marshaller;
47  import javax.xml.bind.Unmarshaller;
48  import javax.xml.bind.util.JAXBResult;
49  import javax.xml.bind.util.JAXBSource;
50  import javax.xml.transform.Transformer;
51  import javax.xml.transform.TransformerConfigurationException;
52  import javax.xml.transform.TransformerException;
53  import javax.xml.transform.TransformerFactory;
54  import javax.xml.transform.stream.StreamSource;
55  import org.apache.maven.plugin.assembly.filter.ContainerDescriptorHandler;
56  import org.codehaus.plexus.archiver.Archiver;
57  import org.codehaus.plexus.archiver.ArchiverException;
58  import org.codehaus.plexus.archiver.ResourceIterator;
59  import org.codehaus.plexus.archiver.UnArchiver;
60  import org.codehaus.plexus.components.io.fileselectors.FileInfo;
61  import org.codehaus.plexus.logging.AbstractLogEnabled;
62  import org.codehaus.plexus.util.StringUtils;
63  import org.jomc.model.ModelObject;
64  import org.jomc.model.Module;
65  import org.jomc.model.Modules;
66  import org.jomc.model.modlet.DefaultModelProvider;
67  import org.jomc.modlet.DefaultModelContext;
68  import org.jomc.modlet.DefaultModletProvider;
69  import org.jomc.modlet.ModelContext;
70  import org.jomc.modlet.ModelContextFactory;
71  import org.jomc.modlet.ModelException;
72  import org.jomc.modlet.Modlet;
73  import org.jomc.modlet.ModletObject;
74  import org.jomc.modlet.Modlets;
75  
76  /**
77   * Maven Assembly Plugin {@code ContainerDescriptorHandler} implementation for assembling JOMC resources.
78   *
79   * <p><b>Maven Assembly Plugin Usage</b><pre>
80   * &lt;containerDescriptorHandler&gt;
81   *   &lt;handlerName&gt;JOMC&lt;/handlerName&gt;
82   *   &lt;configuration&gt;
83   *     &lt;model&gt;http://jomc.org/model&lt;/model&gt;
84   *     &lt;modelContextFactoryClassName&gt;class name&lt;/modelContextFactoryClassName&gt;
85   *     &lt;modelContextAttributes&gt;
86   *       &lt;modelContextAttribute&gt;
87   *         &lt;key&gt;The name of the attribute&lt;/key&gt;
88   *         &lt;value&gt;The name of the attribute&lt;/value&gt;
89   *         &lt;type&gt;The name of the class of the object.&lt;/type&gt;
90   *       &lt;/modelContextAttribute&gt;
91   *     &lt;/modelContextAttributes/&gt;
92   *     &lt;moduleEncoding&gt;${project.build.sourceEncoding}&lt;/moduleEncoding&gt;
93   *     &lt;moduleName&gt;${project.name}&lt;/moduleName&gt;
94   *     &lt;moduleVersion&gt;${project.version}&lt;/moduleVersion&gt;
95   *     &lt;moduleVendor&gt;${project.organization.name}&lt;/moduleVendor&gt;
96   *     &lt;moduleResource&gt;META-INF/custom-jomc.xml&lt;/moduleResource&gt;
97   *     &lt;moduleResources&gt;
98   *       &lt;moduleResource&gt;META-INF/jomc.xml&lt;/moduleResource&gt;
99   *     &lt;/moduleResources&gt;
100  *     &lt;moduleIncludes&gt;
101  *       &lt;moduleInclude&gt;module name&lt;/moduleInclude&gt;
102  *     &lt;/moduleIncludes&gt;
103  *     &lt;moduleExcludes&gt;
104  *       &lt;moduleExclude&gt;module name&lt;/moduleExclude&gt;
105  *     &lt;/moduleExcludes&gt;
106  *     &lt;modletEncoding&gt;${project.build.sourceEncoding}&lt;/modletEncoding&gt;
107  *     &lt;modletName&gt;${project.name}&lt;/modletName&gt;
108  *     &lt;modletVersion&gt;${project.version}&lt;/modletVersion&gt;
109  *     &lt;modletVendor&gt;${project.organization.name}&lt;/modletVendor&gt;
110  *     &lt;modletResource&gt;META-INF/custom-jomc-modlet.xml&lt;/modletResource&gt;
111  *     &lt;modletResources&gt;
112  *       &lt;modletResource&gt;META-INF/jomc-modlet.xml&lt;/modletResource&gt;
113  *     &lt;/modletResources&gt;
114  *     &lt;modletIncludes&gt;
115  *       &lt;modletInclude&gt;modlet name&lt;/modletInclude&gt;
116  *     &lt;/modletIncludes&gt;
117  *     &lt;modletExcludes&gt;
118  *       &lt;modletExclude&gt;modlet name&lt;/modletExclude&gt;
119  *     &lt;/modletExcludes&gt;
120  *     &lt;modelObjectStylesheet&gt;Location of a XSLT document to use for transforming the merged model document.&lt;/modelObjectStylesheet&gt;
121  *     &lt;modletObjectStylesheet&gt;Location of a XSLT document to use for transforming the merged modlet document.&lt;/modletObjectStylesheet&gt;
122  *     &lt;providerLocation&gt;META-INF/custom-services&lt;/providerLocation&gt;
123  *     &lt;platformProviderLocation&gt;${java.home}/jre/lib/custom-jomc.properties&lt;/platformProviderLocation&gt;
124  *     &lt;modletLocation&gt;META-INF/custom-jomc-modlet.xml&lt;/modletLocation&gt;
125  *     &lt;modletSchemaSystemId&gt;http://custom.host.tld/custom/path/jomc-modlet-1.8.xsd&lt;/modletSchemaSystemId&gt;
126  *   &lt;/configuration&gt;
127  * &lt;/containerDescriptorHandler&gt;
128  * </pre></p>
129  *
130  * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
131  * @version $JOMC: JomcContainerDescriptorHandler.java 4987 2014-11-15 19:51:47Z schulte $
132  * @since 1.2
133  * @plexus.component role="org.apache.maven.plugin.assembly.filter.ContainerDescriptorHandler"
134  *                   role-hint="JOMC"
135  */
136 public class JomcContainerDescriptorHandler extends AbstractLogEnabled implements ContainerDescriptorHandler
137 {
138 
139     /** Prefix prepended to log messages. */
140     private static final String LOG_PREFIX = "[JOMC] ";
141 
142     /** The identifier of the model to process. */
143     private String model = ModelObject.MODEL_PUBLIC_ID;
144 
145     /** The encoding of the assembled module. */
146     private String moduleEncoding;
147 
148     /** The name of the assembled module. */
149     private String moduleName;
150 
151     /** The version of the assembled module. */
152     private String moduleVersion;
153 
154     /** The vendor of the assembled module. */
155     private String moduleVendor;
156 
157     /** The resource name of the assembled module. */
158     private String moduleResource = DefaultModelProvider.getDefaultModuleLocation();
159 
160     /** Names of resources to process. */
161     private String[] moduleResources =
162     {
163         DefaultModelProvider.getDefaultModuleLocation()
164     };
165 
166     /** Included modules. */
167     private List<String> moduleIncludes;
168 
169     /** Excluded modules. */
170     private List<String> moduleExcludes;
171 
172     /** The encoding of the assembled modlet. */
173     private String modletEncoding;
174 
175     /** The name of the assembled modlet. */
176     private String modletName;
177 
178     /** The version of the assembled modlet. */
179     private String modletVersion;
180 
181     /** The vendor of the assembled modlet. */
182     private String modletVendor;
183 
184     /** The resource name of the assembled modlet resources. */
185     private String modletResource = DefaultModletProvider.getDefaultModletLocation();
186 
187     /** Names of modlet resources to process. */
188     private String[] modletResources =
189     {
190         DefaultModletProvider.getDefaultModletLocation()
191     };
192 
193     /** Included modlets. */
194     private List<String> modletIncludes;
195 
196     /** Excluded modlets. */
197     private List<String> modletExcludes;
198 
199     /** Location of a XSLT document to use for transforming the merged model document. */
200     private String modelObjectStylesheet;
201 
202     /** Location of a XSLT document to use for transforming the merged modlet document. */
203     private String modletObjectStylesheet;
204 
205     /** The location to search for providers. */
206     private String providerLocation;
207 
208     /** The location to search for platform providers. */
209     private String platformProviderLocation;
210 
211     /** The system id of the modlet schema. */
212     private String modletSchemaSystemId;
213 
214     /** The location to search for modlets. */
215     private String modletLocation;
216 
217     /** Name of the {@code ModelContextFactory} implementation class. */
218     private String modelContextFactoryClassName;
219 
220     /** {@code ModelContext} attributes to apply. */
221     private List<ModelContextAttribute> modelContextAttributes;
222 
223     /** Modlet resources. */
224     private Modlets modlets = new Modlets();
225 
226     /** Model resources. */
227     private Modules modules = new Modules();
228 
229     /** The JOMC JAXB marshaller of the instance. */
230     private Marshaller jomcMarshaller;
231 
232     /** The JOMC JAXB unmarshaller of the instance. */
233     private Unmarshaller jomcUnmarshaller;
234 
235     /** The modlet JAXB marshaller of the instance. */
236     private Marshaller modletMarshaller;
237 
238     /** The modlet JAXB unmarshaller of the instance. */
239     private Unmarshaller modletUnmarshaller;
240 
241     /** Creates a new {@code JomcContainerDescriptorHandler} instance. */
242     public JomcContainerDescriptorHandler()
243     {
244         super();
245     }
246 
247     public void finalizeArchiveCreation( final Archiver archiver ) throws ArchiverException
248     {
249         if ( StringUtils.isEmpty( this.model ) )
250         {
251             throw new ArchiverException( Messages.getMessage( "mandatoryParameter", "model" ) );
252         }
253         if ( StringUtils.isEmpty( this.modletName ) )
254         {
255             throw new ArchiverException( Messages.getMessage( "mandatoryParameter", "modletName" ) );
256         }
257         if ( StringUtils.isEmpty( this.modletResource ) )
258         {
259             throw new ArchiverException( Messages.getMessage( "mandatoryParameter", "modletResource" ) );
260         }
261         if ( StringUtils.isEmpty( this.moduleName ) )
262         {
263             throw new ArchiverException( Messages.getMessage( "mandatoryParameter", "moduleName" ) );
264         }
265         if ( StringUtils.isEmpty( this.moduleResource ) )
266         {
267             throw new ArchiverException( Messages.getMessage( "mandatoryParameter", "moduleResource" ) );
268         }
269 
270         try
271         {
272             // This will prompt the isSelected() call, below, for all resources added to the archive. This needs to be
273             // corrected in the AbstractArchiver, where runArchiveFinalizers() is called before regular resources are
274             // added, which is done because the manifest needs to be added first, and the manifest-creation component is
275             // a finalizer in the assembly plugin.
276             for ( final ResourceIterator it = archiver.getResources(); it.hasNext(); it.next() );
277 
278             if ( !this.modules.getModule().isEmpty() )
279             {
280                 if ( this.moduleIncludes != null )
281                 {
282                     for ( final Iterator<Module> it = this.modules.getModule().iterator(); it.hasNext(); )
283                     {
284                         final Module m = it.next();
285 
286                         if ( !this.moduleIncludes.contains( m.getName() ) )
287                         {
288                             it.remove();
289 
290                             if ( this.getLogger() != null && this.getLogger().isInfoEnabled() )
291                             {
292                                 this.getLogger().info( LOG_PREFIX + Messages.getMessage(
293                                     "excludingModule", m.getName() ) );
294 
295                             }
296                         }
297                     }
298                 }
299 
300                 if ( this.moduleExcludes != null )
301                 {
302                     for ( String exclude : this.moduleExcludes )
303                     {
304                         final Module excluded = this.modules.getModule( exclude );
305 
306                         if ( excluded != null )
307                         {
308                             this.modules.getModule().remove( excluded );
309 
310                             if ( this.getLogger() != null && this.getLogger().isInfoEnabled() )
311                             {
312                                 this.getLogger().info( LOG_PREFIX + Messages.getMessage(
313                                     "excludingModule", excluded.getName() ) );
314 
315                             }
316                         }
317                     }
318                 }
319 
320                 if ( this.getLogger() != null && this.getLogger().isInfoEnabled() )
321                 {
322                     for ( Module m : this.modules.getModule() )
323                     {
324                         this.getLogger().info( LOG_PREFIX + Messages.getMessage( "includingModule", m.getName() ) );
325                     }
326                 }
327 
328                 final Module mergedModule = this.modules.getMergedModule( this.moduleName );
329                 mergedModule.setVersion( this.moduleVersion );
330                 mergedModule.setVendor( this.moduleVendor );
331 
332                 final JAXBElement<Module> transformedModule = this.transformModelObject(
333                     new org.jomc.model.ObjectFactory().createModule( mergedModule ), Module.class );
334 
335                 final File moduleFile = File.createTempFile( "maven-assembly-plugin", ".tmp" );
336                 moduleFile.deleteOnExit();
337 
338                 this.marshalModelObject( transformedModule, moduleFile );
339 
340                 archiver.addFile( moduleFile, normalizeResourceName( this.moduleResource ) );
341             }
342 
343             if ( !this.modlets.getModlet().isEmpty() )
344             {
345                 if ( this.modletIncludes != null )
346                 {
347                     for ( final Iterator<Modlet> it = this.modlets.getModlet().iterator(); it.hasNext(); )
348                     {
349                         final Modlet m = it.next();
350 
351                         if ( !this.modletIncludes.contains( m.getName() ) )
352                         {
353                             it.remove();
354 
355                             if ( this.getLogger() != null && this.getLogger().isInfoEnabled() )
356                             {
357                                 this.getLogger().info( LOG_PREFIX + Messages.getMessage(
358                                     "excludingModlet", m.getName() ) );
359 
360                             }
361                         }
362                     }
363                 }
364 
365                 if ( this.modletExcludes != null )
366                 {
367                     for ( String exclude : this.modletExcludes )
368                     {
369                         final Modlet excluded = this.modlets.getModlet( exclude );
370 
371                         if ( excluded != null )
372                         {
373                             this.modlets.getModlet().remove( excluded );
374 
375                             if ( this.getLogger() != null && this.getLogger().isInfoEnabled() )
376                             {
377                                 this.getLogger().info( LOG_PREFIX + Messages.getMessage(
378                                     "excludingModlet", excluded.getName() ) );
379 
380                             }
381                         }
382                     }
383                 }
384 
385                 if ( this.getLogger() != null && this.getLogger().isInfoEnabled() )
386                 {
387                     for ( Modlet m : this.modlets.getModlet() )
388                     {
389                         this.getLogger().info( LOG_PREFIX + Messages.getMessage( "includingModlet", m.getName() ) );
390                     }
391                 }
392 
393                 final Modlet mergedModlet = this.modlets.getMergedModlet( this.modletName, this.model );
394                 mergedModlet.setVendor( this.modletVendor );
395                 mergedModlet.setVersion( this.modletVersion );
396 
397                 final JAXBElement<Modlet> transformedModlet = this.transformModletObject(
398                     new org.jomc.modlet.ObjectFactory().createModlet( mergedModlet ), Modlet.class );
399 
400                 final File modletFile = File.createTempFile( "maven-assembly-plugin", ".tmp" );
401                 modletFile.deleteOnExit();
402 
403                 this.marshalModletObject( transformedModlet, modletFile );
404 
405                 archiver.addFile( modletFile, normalizeResourceName( this.modletResource ) );
406             }
407         }
408         catch ( final InstantiationException e )
409         {
410             throw new ArchiverException( Messages.getMessage( e ), e );
411         }
412         catch ( final TransformerConfigurationException e )
413         {
414             String message = Messages.getMessage( e );
415             if ( message == null && e.getException() != null )
416             {
417                 message = Messages.getMessage( e.getException() );
418             }
419 
420             throw new ArchiverException( message, e );
421         }
422         catch ( final TransformerException e )
423         {
424             String message = Messages.getMessage( e );
425             if ( message == null && e.getException() != null )
426             {
427                 message = Messages.getMessage( e.getException() );
428             }
429 
430             throw new ArchiverException( message, e );
431         }
432         catch ( final JAXBException e )
433         {
434             String message = Messages.getMessage( e );
435             if ( message == null && e.getLinkedException() != null )
436             {
437                 message = Messages.getMessage( e.getLinkedException() );
438             }
439 
440             throw new ArchiverException( message, e );
441         }
442         catch ( final ModelException e )
443         {
444             throw new ArchiverException( Messages.getMessage( e ), e );
445         }
446         catch ( final IOException e )
447         {
448             throw new ArchiverException( Messages.getMessage( e ), e );
449         }
450         catch ( final URISyntaxException e )
451         {
452             throw new ArchiverException( Messages.getMessage( e ), e );
453         }
454         finally
455         {
456             this.modlets = new Modlets();
457             this.modules = new Modules();
458             this.jomcMarshaller = null;
459             this.jomcUnmarshaller = null;
460             this.modletMarshaller = null;
461             this.modletUnmarshaller = null;
462         }
463     }
464 
465     public void finalizeArchiveExtraction( final UnArchiver unarchiver ) throws ArchiverException
466     {
467     }
468 
469     public List<String> getVirtualFiles()
470     {
471         final List<String> virtualFiles = new LinkedList<String>();
472 
473         if ( !this.modlets.getModlet().isEmpty() )
474         {
475             virtualFiles.add( normalizeResourceName( this.modletResource ) );
476         }
477 
478         if ( !this.modules.getModule().isEmpty() )
479         {
480             virtualFiles.add( normalizeResourceName( this.moduleResource ) );
481         }
482 
483         return virtualFiles.isEmpty() ? null : Collections.unmodifiableList( virtualFiles );
484     }
485 
486     public boolean isSelected( final FileInfo fileInfo ) throws IOException
487     {
488         try
489         {
490             boolean selected = true;
491             final String name = normalizeResourceName( fileInfo.getName() );
492 
493             if ( this.moduleResources != null )
494             {
495                 for ( String r : this.moduleResources )
496                 {
497                     if ( name.equals( normalizeResourceName( r ) ) )
498                     {
499                         Object modelObject = this.unmarshalModelObject( fileInfo.getContents() );
500 
501                         if ( modelObject instanceof JAXBElement<?> )
502                         {
503                             modelObject = ( (JAXBElement<?>) modelObject ).getValue();
504                         }
505                         if ( modelObject instanceof Modules )
506                         {
507                             this.modules.getModule().addAll( ( (Modules) modelObject ).getModule() );
508                         }
509                         if ( modelObject instanceof Module )
510                         {
511                             this.modules.getModule().add( (Module) modelObject );
512                         }
513 
514                         if ( this.getLogger() != null && this.getLogger().isDebugEnabled() )
515                         {
516                             this.getLogger().debug( LOG_PREFIX + Messages.getMessage(
517                                 "processingModuleResource", name ) );
518 
519                         }
520 
521                         selected = false;
522                         break;
523                     }
524                 }
525             }
526 
527             if ( selected && this.modletResources != null )
528             {
529                 for ( String r : this.modletResources )
530                 {
531                     if ( name.equals( normalizeResourceName( r ) ) )
532                     {
533                         Object modletObject = this.unmarshalModletObject( fileInfo.getContents() );
534 
535                         if ( modletObject instanceof JAXBElement<?> )
536                         {
537                             modletObject = ( (JAXBElement<?>) modletObject ).getValue();
538                         }
539                         if ( modletObject instanceof Modlets )
540                         {
541                             this.modlets.getModlet().addAll( ( (Modlets) modletObject ).getModlet() );
542                         }
543                         if ( modletObject instanceof Modlet )
544                         {
545                             this.modlets.getModlet().add( (Modlet) modletObject );
546                         }
547 
548                         if ( this.getLogger() != null && this.getLogger().isDebugEnabled() )
549                         {
550                             this.getLogger().debug( LOG_PREFIX + Messages.getMessage(
551                                 "processingModletResource", name ) );
552 
553                         }
554 
555                         selected = false;
556                         break;
557                     }
558                 }
559             }
560 
561             if ( selected && ( name.equals( normalizeResourceName( this.modletResource ) )
562                                || name.equals( normalizeResourceName( this.moduleResource ) ) ) )
563             {
564                 if ( this.getLogger() != null && this.getLogger().isWarnEnabled() )
565                 {
566                     this.getLogger().warn( LOG_PREFIX + Messages.getMessage( "overridingResource", name ) );
567                 }
568 
569                 selected = false;
570             }
571 
572             return selected;
573         }
574         catch ( final InstantiationException e )
575         {
576             // JDK: As of JDK 6, "new IOException( message, cause )".
577             throw (IOException) new IOException( Messages.getMessage( e ) ).initCause( e );
578         }
579         catch ( final JAXBException e )
580         {
581             String message = Messages.getMessage( e );
582             if ( message == null && e.getLinkedException() != null )
583             {
584                 message = Messages.getMessage( e.getLinkedException() );
585             }
586 
587             // JDK: As of JDK 6, "new IOException( message, cause )".
588             throw (IOException) new IOException( message ).initCause( e );
589         }
590         catch ( final ModelException e )
591         {
592             // JDK: As of JDK 6, "new IOException( message, cause )".
593             throw (IOException) new IOException( Messages.getMessage( e ) ).initCause( e );
594         }
595     }
596 
597     /**
598      * Creates an {@code URL} for a given resource location.
599      * <p>This method first searches the class loader of the class for a single resource matching {@code location}. If
600      * such a resource is found, the URL of that resource is returned. If no such resource is found, an attempt is made
601      * to parse the given location to an URL. On successful parsing, that URL is returned. Failing that, the given
602      * location is interpreted as a file name. If that file is found, the URL of that file is returned. Otherwise an
603      * {@code IOException} is thrown.</p>
604      *
605      * @param location The location to create an {@code URL} from.
606      *
607      * @return An {@code URL} for {@code location}.
608      *
609      * @throws NullPointerException if {@code location} is {@code null}.
610      * @throws IOException if creating an URL fails.
611      */
612     protected URL getResource( final String location ) throws IOException
613     {
614         if ( location == null )
615         {
616             throw new NullPointerException( "location" );
617         }
618 
619         try
620         {
621             String absolute = location;
622             if ( !absolute.startsWith( "/" ) )
623             {
624                 absolute = "/" + location;
625             }
626 
627             URL resource = this.getClass().getResource( absolute );
628             if ( resource == null )
629             {
630                 try
631                 {
632                     resource = new URL( location );
633                 }
634                 catch ( final MalformedURLException e )
635                 {
636                     if ( this.getLogger() != null && this.getLogger().isDebugEnabled() )
637                     {
638                         this.getLogger().debug( Messages.getMessage( e ), e );
639                     }
640 
641                     resource = null;
642                 }
643             }
644 
645             if ( resource == null )
646             {
647                 final File f = new File( location );
648 
649                 if ( f.isFile() )
650                 {
651                     resource = f.toURI().toURL();
652                 }
653             }
654 
655             if ( resource == null )
656             {
657                 throw new IOException( Messages.getMessage( "resourceNotFound", location ) );
658             }
659 
660             return resource;
661         }
662         catch ( final MalformedURLException e )
663         {
664             String m = Messages.getMessage( e );
665             m = m == null ? "" : " " + m;
666 
667             // JDK: As of JDK 6, "new IOException( message, cause )".
668             throw (IOException) new IOException( Messages.getMessage(
669                 "malformedLocation", location, m ) ).initCause( e );
670 
671         }
672     }
673 
674     private Object unmarshalModelObject( final InputStream in )
675         throws ModelException, JAXBException, InstantiationException
676     {
677         if ( in == null )
678         {
679             throw new NullPointerException( "in" );
680         }
681 
682         if ( this.jomcUnmarshaller == null )
683         {
684             this.jomcUnmarshaller = this.createModelContext().createUnmarshaller( this.model );
685         }
686 
687         return this.jomcUnmarshaller.unmarshal( in );
688     }
689 
690     private void marshalModelObject( final JAXBElement<? extends ModelObject> element, final File file )
691         throws ModelException, JAXBException, InstantiationException
692     {
693         if ( element == null )
694         {
695             throw new NullPointerException( "element" );
696         }
697         if ( file == null )
698         {
699             throw new NullPointerException( "file" );
700         }
701 
702         if ( this.jomcMarshaller == null )
703         {
704             final ModelContext modelContext = this.createModelContext();
705             this.jomcMarshaller = modelContext.createMarshaller( this.model );
706             this.jomcMarshaller.setSchema( modelContext.createSchema( this.model ) );
707             this.jomcMarshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
708 
709             if ( this.moduleEncoding != null )
710             {
711                 this.jomcMarshaller.setProperty( Marshaller.JAXB_ENCODING, this.moduleEncoding );
712             }
713         }
714 
715         this.jomcMarshaller.marshal( element, file );
716     }
717 
718     private <T> JAXBElement<T> transformModelObject( final JAXBElement<? extends ModelObject> element,
719                                                      final Class<T> boundType )
720         throws ModelException, TransformerException, JAXBException, IOException, URISyntaxException,
721                InstantiationException
722     {
723         if ( element == null )
724         {
725             throw new NullPointerException( "element" );
726         }
727         if ( !boundType.isInstance( element.getValue() ) )
728         {
729             throw new IllegalArgumentException( element.toString() );
730         }
731 
732         @SuppressWarnings( "unchecked" )
733         JAXBElement<T> transformed = (JAXBElement<T>) element;
734 
735         if ( this.modelObjectStylesheet != null )
736         {
737             final Transformer transformer = TransformerFactory.newInstance().newTransformer(
738                 new StreamSource( this.getResource( this.modelObjectStylesheet ).toURI().toASCIIString() ) );
739 
740             final ModelContext modelContext = this.createModelContext();
741             final Marshaller marshaller = modelContext.createMarshaller( this.model );
742             final Unmarshaller unmarshaller = modelContext.createUnmarshaller( this.model );
743             final JAXBSource source = new JAXBSource( marshaller, element );
744             final JAXBResult result = new JAXBResult( unmarshaller );
745 
746             for ( Map.Entry<Object, Object> e : System.getProperties().entrySet() )
747             {
748                 transformer.setParameter( e.getKey().toString(), e.getValue() );
749             }
750 
751             transformer.transform( source, result );
752 
753             if ( result.getResult() instanceof JAXBElement<?>
754                  && boundType.isInstance( ( (JAXBElement<?>) result.getResult() ).getValue() ) )
755             {
756                 @SuppressWarnings( "unchecked" ) final JAXBElement<T> e = (JAXBElement<T>) result.getResult();
757                 transformed = e;
758             }
759             else
760             {
761                 throw new ModelException( Messages.getMessage(
762                     "illegalModuleTransformationResult", this.modelObjectStylesheet ) );
763 
764             }
765         }
766 
767         return transformed;
768     }
769 
770     private Object unmarshalModletObject( final InputStream in )
771         throws ModelException, JAXBException, InstantiationException
772     {
773         if ( in == null )
774         {
775             throw new NullPointerException( "in" );
776         }
777 
778         if ( this.modletUnmarshaller == null )
779         {
780             this.modletUnmarshaller = this.createModelContext().createUnmarshaller( ModletObject.MODEL_PUBLIC_ID );
781         }
782 
783         return this.modletUnmarshaller.unmarshal( in );
784     }
785 
786     private void marshalModletObject( final JAXBElement<? extends ModletObject> element, final File file )
787         throws ModelException, JAXBException, InstantiationException
788     {
789         if ( element == null )
790         {
791             throw new NullPointerException( "element" );
792         }
793         if ( file == null )
794         {
795             throw new NullPointerException( "file" );
796         }
797 
798         if ( this.modletMarshaller == null )
799         {
800             final ModelContext modletContext = this.createModelContext();
801             this.modletMarshaller = modletContext.createMarshaller( ModletObject.MODEL_PUBLIC_ID );
802             this.modletMarshaller.setSchema( modletContext.createSchema( ModletObject.MODEL_PUBLIC_ID ) );
803             this.modletMarshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
804 
805             if ( this.modletEncoding != null )
806             {
807                 this.modletMarshaller.setProperty( Marshaller.JAXB_ENCODING, this.modletEncoding );
808             }
809         }
810 
811         this.modletMarshaller.marshal( element, file );
812     }
813 
814     private <T> JAXBElement<T> transformModletObject( final JAXBElement<? extends ModletObject> element,
815                                                       final Class<T> boundType )
816         throws ModelException, TransformerException, JAXBException, IOException, URISyntaxException,
817                InstantiationException
818     {
819         if ( element == null )
820         {
821             throw new NullPointerException( "element" );
822         }
823         if ( !boundType.isInstance( element.getValue() ) )
824         {
825             throw new IllegalArgumentException( element.toString() );
826         }
827 
828         @SuppressWarnings( "unchecked" )
829         JAXBElement<T> transformed = (JAXBElement<T>) element;
830 
831         if ( this.modletObjectStylesheet != null )
832         {
833             final Transformer transformer = TransformerFactory.newInstance().newTransformer(
834                 new StreamSource( this.getResource( this.modletObjectStylesheet ).toURI().toASCIIString() ) );
835 
836             final ModelContext modletContext = this.createModelContext();
837             final Marshaller marshaller = modletContext.createMarshaller( ModletObject.MODEL_PUBLIC_ID );
838             final Unmarshaller unmarshaller = modletContext.createUnmarshaller( ModletObject.MODEL_PUBLIC_ID );
839             final JAXBSource source = new JAXBSource( marshaller, element );
840             final JAXBResult result = new JAXBResult( unmarshaller );
841 
842             for ( Map.Entry<Object, Object> e : System.getProperties().entrySet() )
843             {
844                 transformer.setParameter( e.getKey().toString(), e.getValue() );
845             }
846 
847             transformer.transform( source, result );
848 
849             if ( result.getResult() instanceof JAXBElement<?>
850                  && boundType.isInstance( ( (JAXBElement<?>) result.getResult() ).getValue() ) )
851             {
852                 @SuppressWarnings( "unchecked" ) final JAXBElement<T> e = (JAXBElement<T>) result.getResult();
853                 transformed = e;
854             }
855             else
856             {
857                 throw new ModelException( Messages.getMessage(
858                     "illegalModletTransformationResult", this.modletObjectStylesheet ) );
859 
860             }
861         }
862 
863         return transformed;
864     }
865 
866     private static String normalizeResourceName( final String name )
867     {
868         String normalized = name;
869 
870         if ( normalized != null )
871         {
872             normalized = normalized.replace( '\\', '/' );
873 
874             if ( normalized.startsWith( "/" ) )
875             {
876                 normalized = normalized.substring( 1 );
877             }
878 
879             if ( normalized.endsWith( "/" ) )
880             {
881                 normalized = normalized.substring( 0, normalized.length() );
882             }
883         }
884 
885         return normalized;
886     }
887 
888     private ModelContext createModelContext() throws ModelException, InstantiationException
889     {
890         final ModelContextFactory modelContextFactory;
891         if ( this.modelContextFactoryClassName != null )
892         {
893             modelContextFactory = ModelContextFactory.newInstance( this.modelContextFactoryClassName );
894         }
895         else
896         {
897             modelContextFactory = ModelContextFactory.newInstance();
898         }
899 
900         final ModelContext modelContext = modelContextFactory.newModelContext();
901         modelContext.setModletSchemaSystemId( this.modletSchemaSystemId );
902 
903         if ( this.providerLocation != null )
904         {
905             modelContext.setAttribute( DefaultModelContext.PROVIDER_LOCATION_ATTRIBUTE_NAME, this.providerLocation );
906         }
907 
908         if ( this.platformProviderLocation != null )
909         {
910             modelContext.setAttribute( DefaultModelContext.PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME,
911                                        this.platformProviderLocation );
912 
913         }
914 
915         if ( this.modletLocation != null )
916         {
917             modelContext.setAttribute( DefaultModletProvider.MODLET_LOCATION_ATTRIBUTE_NAME, this.modletLocation );
918         }
919 
920         if ( this.modelContextAttributes != null )
921         {
922             for ( ModelContextAttribute e : this.modelContextAttributes )
923             {
924                 final Object object = e.getObject( modelContext );
925 
926                 if ( object != null )
927                 {
928                     modelContext.setAttribute( e.getKey(), object );
929                 }
930                 else
931                 {
932                     modelContext.clearAttribute( e.getKey() );
933                 }
934             }
935         }
936 
937         return modelContext;
938     }
939 
940 }