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}