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