001/*
002 * Copyright (C) 2009 Christian Schulte <cs@schulte.it>
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: MergeModletsCommand.java 5263 2016-05-01 23:44:12Z schulte $
029 *
030 */
031package org.jomc.cli.commands;
032
033import java.io.File;
034import java.io.IOException;
035import java.net.URL;
036import java.util.Arrays;
037import java.util.Enumeration;
038import java.util.Iterator;
039import java.util.List;
040import java.util.Locale;
041import java.util.logging.Level;
042import javax.xml.bind.JAXBElement;
043import javax.xml.bind.JAXBException;
044import javax.xml.bind.Marshaller;
045import javax.xml.bind.Unmarshaller;
046import javax.xml.bind.util.JAXBResult;
047import javax.xml.bind.util.JAXBSource;
048import javax.xml.transform.Transformer;
049import javax.xml.transform.TransformerException;
050import javax.xml.transform.stream.StreamSource;
051import org.apache.commons.cli.CommandLine;
052import org.apache.commons.cli.Option;
053import org.jomc.modlet.DefaultModletProvider;
054import org.jomc.modlet.ModelContext;
055import org.jomc.modlet.ModelException;
056import org.jomc.modlet.ModelValidationReport;
057import org.jomc.modlet.Modlet;
058import org.jomc.modlet.ModletObject;
059import org.jomc.modlet.Modlets;
060import org.jomc.modlet.ObjectFactory;
061
062/**
063 * {@code merge-modlets} command implementation.
064 *
065 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
066 */
067public final class MergeModletsCommand extends AbstractModletCommand
068{
069
070    /**
071     * Creates a new {@code MergeModletsCommand} instance.
072     */
073    public MergeModletsCommand()
074    {
075        super();
076    }
077
078    @Override
079    public org.apache.commons.cli.Options getOptions()
080    {
081        final org.apache.commons.cli.Options options = super.getOptions();
082        Option option = (Option) Options.DOCUMENT_OPTION.clone();
083        option.setRequired( true );
084        options.addOption( option );
085
086        options.addOption( Options.DOCUMENT_ENCODING_OPTION );
087        options.addOption( Options.STYLESHEET_OPTION );
088
089        option = (Option) Options.MODLET_OPTION.clone();
090        option.setRequired( true );
091        options.addOption( option );
092
093        options.addOption( Options.MODLET_VERSION_OPTION );
094        options.addOption( Options.MODLET_VENDOR_OPTION );
095        options.addOption( Options.MODLET_INCLUDES_OPTION );
096        options.addOption( Options.MODLET_EXCLUDES_OPTION );
097        options.addOption( Options.RESOURCES_OPTION );
098        return options;
099    }
100
101    public String getName()
102    {
103        return "merge-modlets";
104    }
105
106    public String getAbbreviatedName()
107    {
108        return "mmd";
109    }
110
111    public String getShortDescription( final Locale locale )
112    {
113        return Messages.getMessage( "mergeModletsShortDescription" );
114    }
115
116    public String getLongDescription( final Locale locale )
117    {
118        return null;
119    }
120
121    protected void executeCommand( final CommandLine commandLine ) throws CommandExecutionException
122    {
123        if ( commandLine == null )
124        {
125            throw new NullPointerException( "commandLine" );
126        }
127
128        CommandLineClassLoader classLoader = null;
129
130        try
131        {
132            classLoader = new CommandLineClassLoader( commandLine );
133            final Modlets modlets = new Modlets();
134            final ModelContext context = this.createModelContext( commandLine, classLoader );
135            final Marshaller marshaller = context.createMarshaller( ModletObject.MODEL_PUBLIC_ID );
136            final Unmarshaller unmarshaller = context.createUnmarshaller( ModletObject.MODEL_PUBLIC_ID );
137
138            if ( !commandLine.hasOption( Options.NO_MODLET_RESOURCE_VALIDATION_OPTION.getOpt() ) )
139            {
140                unmarshaller.setSchema( context.createSchema( ModletObject.MODEL_PUBLIC_ID ) );
141            }
142
143            File stylesheetFile = null;
144            if ( commandLine.hasOption( Options.STYLESHEET_OPTION.getOpt() ) )
145            {
146                stylesheetFile = new File( commandLine.getOptionValue( Options.STYLESHEET_OPTION.getOpt() ) );
147            }
148
149            String modletVersion = null;
150            if ( commandLine.hasOption( Options.MODLET_VERSION_OPTION.getOpt() ) )
151            {
152                modletVersion = commandLine.getOptionValue( Options.MODLET_VERSION_OPTION.getOpt() );
153            }
154
155            String modletVendor = null;
156            if ( commandLine.hasOption( Options.MODLET_VENDOR_OPTION.getOpt() ) )
157            {
158                modletVendor = commandLine.getOptionValue( Options.MODLET_VENDOR_OPTION.getOpt() );
159            }
160
161            if ( commandLine.hasOption( Options.DOCUMENTS_OPTION.getOpt() ) )
162            {
163                for ( final File f : this.getDocumentFiles( commandLine ) )
164                {
165                    if ( this.isLoggable( Level.FINEST ) )
166                    {
167                        this.log( Level.FINEST, Messages.getMessage( "readingResource", f.getAbsolutePath() ), null );
168                    }
169
170                    Object o = unmarshaller.unmarshal( f );
171                    if ( o instanceof JAXBElement<?> )
172                    {
173                        o = ( (JAXBElement<?>) o ).getValue();
174                    }
175
176                    if ( o instanceof Modlet )
177                    {
178                        modlets.getModlet().add( (Modlet) o );
179                    }
180                    else if ( o instanceof Modlets )
181                    {
182                        modlets.getModlet().addAll( ( (Modlets) o ).getModlet() );
183                    }
184                    else if ( this.isLoggable( Level.WARNING ) )
185                    {
186                        this.log( Level.WARNING,
187                                  Messages.getMessage( "failureProcessing", f.getAbsolutePath(), o.toString() ),
188                                  null );
189
190                    }
191                }
192            }
193
194            if ( commandLine.hasOption( Options.CLASSPATH_OPTION.getOpt() ) )
195            {
196                String[] resourceNames = null;
197
198                if ( commandLine.hasOption( Options.RESOURCES_OPTION.getOpt() ) )
199                {
200                    resourceNames = commandLine.getOptionValues( Options.RESOURCES_OPTION.getOpt() );
201                }
202
203                if ( resourceNames == null )
204                {
205                    resourceNames = new String[]
206                    {
207                        DefaultModletProvider.getDefaultModletLocation()
208                    };
209                }
210
211                for ( final String resource : resourceNames )
212                {
213                    for ( final Enumeration<URL> e = classLoader.getResources( resource ); e.hasMoreElements(); )
214                    {
215                        final URL url = e.nextElement();
216
217                        if ( this.isLoggable( Level.FINEST ) )
218                        {
219                            this.log( Level.FINEST,
220                                      Messages.getMessage( "readingResource", url.toExternalForm() ),
221                                      null );
222
223                        }
224
225                        Object o = unmarshaller.unmarshal( url );
226                        if ( o instanceof JAXBElement<?> )
227                        {
228                            o = ( (JAXBElement<?>) o ).getValue();
229                        }
230
231                        if ( o instanceof Modlet )
232                        {
233                            modlets.getModlet().add( (Modlet) o );
234                        }
235                        else if ( o instanceof Modlets )
236                        {
237                            modlets.getModlet().addAll( ( (Modlets) o ).getModlet() );
238                        }
239                        else if ( this.isLoggable( Level.WARNING ) )
240                        {
241                            this.log( Level.WARNING,
242                                      Messages.getMessage( "failureProcessing", url.toExternalForm(), o.toString() ),
243                                      null );
244
245                        }
246                    }
247                }
248            }
249
250            if ( commandLine.hasOption( Options.MODLET_INCLUDES_OPTION.getOpt() ) )
251            {
252                final String[] values = commandLine.getOptionValues( Options.MODLET_INCLUDES_OPTION.getOpt() );
253
254                if ( values != null )
255                {
256                    final List<String> includes = Arrays.asList( values );
257
258                    for ( final Iterator<Modlet> it = modlets.getModlet().iterator(); it.hasNext(); )
259                    {
260                        final Modlet m = it.next();
261
262                        if ( !includes.contains( m.getName() ) )
263                        {
264                            this.log( Level.INFO,
265                                      Messages.getMessage( "modletNameExclusionInfo", m.getName() ),
266                                      null );
267
268                            it.remove();
269                        }
270                        else
271                        {
272                            this.log( Level.INFO,
273                                      Messages.getMessage( "modletNameInclusionInfo", m.getName() ),
274                                      null );
275
276                        }
277                    }
278                }
279            }
280
281            if ( commandLine.hasOption( Options.MODLET_EXCLUDES_OPTION.getOpt() ) )
282            {
283                final String[] values = commandLine.getOptionValues( Options.MODLET_EXCLUDES_OPTION.getOpt() );
284
285                if ( values != null )
286                {
287                    for ( final String exclude : values )
288                    {
289                        final Modlet m = modlets.getModlet( exclude );
290
291                        if ( m != null )
292                        {
293                            this.log( Level.INFO,
294                                      Messages.getMessage( "modletNameExclusionInfo", m.getName() ),
295                                      null );
296
297                            modlets.getModlet().remove( m );
298                        }
299                    }
300                }
301            }
302
303            final ModelValidationReport validationReport =
304                context.validateModel( ModletObject.MODEL_PUBLIC_ID,
305                                       new JAXBSource( marshaller, new ObjectFactory().createModlets( modlets ) ) );
306
307            this.log( validationReport, marshaller );
308
309            if ( !validationReport.isModelValid() )
310            {
311                throw new CommandExecutionException( Messages.getMessage( "invalidModel",
312                                                                          ModletObject.MODEL_PUBLIC_ID ) );
313
314            }
315
316            Modlet mergedModlet = modlets.getMergedModlet(
317                commandLine.getOptionValue( Options.MODLET_OPTION.getOpt() ), this.getModel( commandLine ) );
318
319            mergedModlet.setVersion( modletVersion );
320            mergedModlet.setVendor( modletVendor );
321
322            final File modletFile = new File( commandLine.getOptionValue( Options.DOCUMENT_OPTION.getOpt() ) );
323
324            if ( stylesheetFile != null )
325            {
326                final Transformer transformer = this.createTransformer( new StreamSource( stylesheetFile ) );
327                final JAXBSource source =
328                    new JAXBSource( marshaller, new ObjectFactory().createModlet( mergedModlet ) );
329
330                final JAXBResult result = new JAXBResult( unmarshaller );
331                unmarshaller.setSchema( null );
332                transformer.transform( source, result );
333
334                if ( result.getResult() instanceof JAXBElement<?>
335                         && ( (JAXBElement<?>) result.getResult() ).getValue() instanceof Modlet )
336                {
337                    mergedModlet = (Modlet) ( (JAXBElement<?>) result.getResult() ).getValue();
338                }
339                else
340                {
341                    throw new CommandExecutionException( Messages.getMessage( "illegalTransformationResultError",
342                                                                              stylesheetFile.getAbsolutePath() ) );
343
344                }
345            }
346
347            marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
348
349            if ( commandLine.hasOption( Options.DOCUMENT_ENCODING_OPTION.getOpt() ) )
350            {
351                marshaller.setProperty( Marshaller.JAXB_ENCODING,
352                                        commandLine.getOptionValue( Options.DOCUMENT_ENCODING_OPTION.getOpt() ) );
353
354            }
355
356            marshaller.setSchema( context.createSchema( ModletObject.MODEL_PUBLIC_ID ) );
357            marshaller.marshal( new ObjectFactory().createModlet( mergedModlet ), modletFile );
358
359            if ( this.isLoggable( Level.INFO ) )
360            {
361                this.log( Level.INFO, Messages.getMessage( "writingResource", modletFile.getAbsolutePath() ), null );
362            }
363
364            classLoader.close();
365            classLoader = null;
366        }
367        catch ( final IOException e )
368        {
369            throw new CommandExecutionException( Messages.getMessage( e ), e );
370        }
371        catch ( final TransformerException e )
372        {
373            String message = Messages.getMessage( e );
374            if ( message == null )
375            {
376                message = Messages.getMessage( e.getException() );
377            }
378
379            throw new CommandExecutionException( message, e );
380        }
381        catch ( final JAXBException e )
382        {
383            String message = Messages.getMessage( e );
384            if ( message == null )
385            {
386                message = Messages.getMessage( e.getLinkedException() );
387            }
388
389            throw new CommandExecutionException( message, e );
390        }
391        catch ( final ModelException e )
392        {
393            throw new CommandExecutionException( Messages.getMessage( e ), e );
394        }
395        finally
396        {
397            try
398            {
399                if ( classLoader != null )
400                {
401                    classLoader.close();
402                }
403            }
404            catch ( final IOException e )
405            {
406                this.log( Level.SEVERE, Messages.getMessage( e ), e );
407            }
408        }
409    }
410
411}