View Javadoc

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