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