001    /*
002     *   Copyright (C) Christian Schulte, 2005-206
003     *   All rights reserved.
004     *
005     *   Redistribution and use in source and binary forms, with or without
006     *   modification, are permitted provided that the following conditions
007     *   are met:
008     *
009     *     o Redistributions of source code must retain the above copyright
010     *       notice, this list of conditions and the following disclaimer.
011     *
012     *     o Redistributions in binary form must reproduce the above copyright
013     *       notice, this list of conditions and the following disclaimer in
014     *       the documentation and/or other materials provided with the
015     *       distribution.
016     *
017     *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
018     *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
019     *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
020     *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
021     *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022     *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023     *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024     *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025     *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026     *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027     *
028     *   $JOMC: JomcResourceTransformer.java 4200 2012-01-25 09:46:13Z schulte2005 $
029     *
030     */
031    package org.jomc.mojo;
032    
033    import java.io.File;
034    import java.io.IOException;
035    import java.io.InputStream;
036    import java.io.OutputStream;
037    import java.net.MalformedURLException;
038    import java.net.URISyntaxException;
039    import java.net.URL;
040    import java.util.Iterator;
041    import java.util.List;
042    import java.util.Map;
043    import java.util.jar.JarEntry;
044    import java.util.jar.JarOutputStream;
045    import javax.xml.bind.JAXBElement;
046    import javax.xml.bind.JAXBException;
047    import javax.xml.bind.Marshaller;
048    import javax.xml.bind.Unmarshaller;
049    import javax.xml.bind.util.JAXBResult;
050    import javax.xml.bind.util.JAXBSource;
051    import javax.xml.transform.Transformer;
052    import javax.xml.transform.TransformerConfigurationException;
053    import javax.xml.transform.TransformerException;
054    import javax.xml.transform.TransformerFactory;
055    import javax.xml.transform.stream.StreamSource;
056    import org.apache.maven.plugins.shade.resource.ResourceTransformer;
057    import org.codehaus.plexus.logging.AbstractLogEnabled;
058    import org.codehaus.plexus.util.StringUtils;
059    import org.jomc.model.ModelObject;
060    import org.jomc.model.Module;
061    import org.jomc.model.Modules;
062    import org.jomc.model.modlet.DefaultModelProvider;
063    import org.jomc.modlet.DefaultModelContext;
064    import org.jomc.modlet.DefaultModletProvider;
065    import org.jomc.modlet.ModelContext;
066    import org.jomc.modlet.ModelContextFactory;
067    import org.jomc.modlet.ModelException;
068    import org.jomc.modlet.Modlet;
069    import org.jomc.modlet.ModletObject;
070    import org.jomc.modlet.Modlets;
071    
072    /**
073     * Maven Shade Plugin {@code ResourceTransformer} implementation for shading JOMC resources.
074     *
075     * <p><b>Maven Shade Plugin Usage</b><pre>
076     * &lt;transformer implementation="org.jomc.mojo.JomcResourceTransformer"&gt;
077     *   &lt;model&gt;http://jomc.org/model&lt;/model&gt;
078     *   &lt;modelContextFactoryClassName&gt;class name&lt;/modelContextFactoryClassName&gt;
079     *     &lt;modelContextAttributes&gt;
080     *       &lt;modelContextAttribute&gt;
081     *         &lt;key&gt;The name of the attribute&lt;/key&gt;
082     *         &lt;value&gt;The name of the attribute&lt;/value&gt;
083     *         &lt;type&gt;The name of the class of the object.&lt;/type&gt;
084     *       &lt;/modelContextAttribute&gt;
085     *     &lt;/modelContextAttributes/&gt;
086     *   &lt;moduleEncoding&gt;${project.build.sourceEncoding}&lt;/moduleEncoding&gt;
087     *   &lt;moduleName&gt;${project.name}&lt;/moduleName&gt;
088     *   &lt;moduleVersion&gt;${project.version}&lt;/moduleVersion&gt;
089     *   &lt;moduleVendor&gt;${project.organization.name}&lt;/moduleVendor&gt;
090     *   &lt;moduleResource&gt;META-INF/custom-jomc.xml&lt;/moduleResource&gt;
091     *   &lt;moduleResources&gt;
092     *     &lt;moduleResource&gt;META-INF/jomc.xml&lt;/moduleResource&gt;
093     *   &lt;/moduleResources&gt;
094     *   &lt;moduleIncludes&gt;
095     *     &lt;moduleInclude&gt;module name&lt;/moduleInclude&gt;
096     *   &lt;/moduleIncludes&gt;
097     *   &lt;moduleExcludes&gt;
098     *     &lt;moduleExclude&gt;module name&lt;/moduleExclude&gt;
099     *   &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.2.xsd&lt;/modletSchemaSystemId&gt;
120     * &lt;/transformer&gt;
121     * </pre></p>
122     *
123     * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
124     * @version $JOMC: JomcResourceTransformer.java 4200 2012-01-25 09:46:13Z schulte2005 $
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    }