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