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