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: MergeModulesCommand.java 5251 2016-04-25 19:46:04Z 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.model.Module; 054import org.jomc.model.Modules; 055import org.jomc.model.ObjectFactory; 056import org.jomc.model.modlet.DefaultModelProvider; 057import org.jomc.modlet.ModelContext; 058import org.jomc.modlet.ModelException; 059import org.jomc.modlet.ModelValidationReport; 060 061/** 062 * {@code merge-modules} command implementation. 063 * 064 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 065 */ 066public final class MergeModulesCommand extends AbstractModelCommand 067{ 068 069 /** 070 * Creates a new {@code MergeModulesCommand} instance. 071 */ 072 public MergeModulesCommand() 073 { 074 super(); 075 } 076 077 @Override 078 public org.apache.commons.cli.Options getOptions() 079 { 080 final org.apache.commons.cli.Options options = super.getOptions(); 081 Option option = (Option) Options.DOCUMENT_OPTION.clone(); 082 option.setRequired( true ); 083 options.addOption( option ); 084 085 options.addOption( Options.DOCUMENT_ENCODING_OPTION ); 086 options.addOption( Options.STYLESHEET_OPTION ); 087 088 option = (Option) Options.MODULE_OPTION.clone(); 089 option.setRequired( true ); 090 options.addOption( option ); 091 092 options.addOption( Options.MODULE_VERSION_OPTION ); 093 options.addOption( Options.MODULE_VENDOR_OPTION ); 094 options.addOption( Options.MODULE_INCLUDES_OPTION ); 095 options.addOption( Options.MODULE_EXCLUDES_OPTION ); 096 options.addOption( Options.RESOURCES_OPTION ); 097 return options; 098 } 099 100 public String getName() 101 { 102 return "merge-modules"; 103 } 104 105 public String getAbbreviatedName() 106 { 107 return "mm"; 108 } 109 110 public String getShortDescription( final Locale locale ) 111 { 112 return Messages.getMessage( "mergeModulesShortDescription" ); 113 } 114 115 public String getLongDescription( final Locale locale ) 116 { 117 return null; 118 } 119 120 protected void executeCommand( final CommandLine commandLine ) throws CommandExecutionException 121 { 122 if ( commandLine == null ) 123 { 124 throw new NullPointerException( "commandLine" ); 125 } 126 127 CommandLineClassLoader classLoader = null; 128 129 try 130 { 131 classLoader = new CommandLineClassLoader( commandLine ); 132 final Modules modules = new Modules(); 133 final ModelContext context = this.createModelContext( commandLine, classLoader ); 134 final String model = this.getModel( commandLine ); 135 final Marshaller marshaller = context.createMarshaller( model ); 136 final Unmarshaller unmarshaller = context.createUnmarshaller( model ); 137 138 if ( !commandLine.hasOption( Options.NO_MODLET_RESOURCE_VALIDATION_OPTION.getOpt() ) ) 139 { 140 unmarshaller.setSchema( context.createSchema( model ) ); 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 moduleVersion = null; 150 if ( commandLine.hasOption( Options.MODULE_VERSION_OPTION.getOpt() ) ) 151 { 152 moduleVersion = commandLine.getOptionValue( Options.MODULE_VERSION_OPTION.getOpt() ); 153 } 154 155 String moduleVendor = null; 156 if ( commandLine.hasOption( Options.MODULE_VENDOR_OPTION.getOpt() ) ) 157 { 158 moduleVendor = commandLine.getOptionValue( Options.MODULE_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 Module ) 177 { 178 modules.getModule().add( (Module) o ); 179 } 180 else if ( o instanceof Modules ) 181 { 182 modules.getModule().addAll( ( (Modules) o ).getModule() ); 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 DefaultModelProvider.getDefaultModuleLocation() 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 Module ) 232 { 233 modules.getModule().add( (Module) o ); 234 } 235 else if ( o instanceof Modules ) 236 { 237 modules.getModule().addAll( ( (Modules) o ).getModule() ); 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.MODULE_INCLUDES_OPTION.getOpt() ) ) 251 { 252 final String[] values = commandLine.getOptionValues( Options.MODULE_INCLUDES_OPTION.getOpt() ); 253 254 if ( values != null ) 255 { 256 final List<String> includes = Arrays.asList( values ); 257 258 for ( final Iterator<Module> it = modules.getModule().iterator(); it.hasNext(); ) 259 { 260 final Module m = it.next(); 261 if ( !includes.contains( m.getName() ) ) 262 { 263 this.log( Level.INFO, 264 Messages.getMessage( "moduleExclusionInfo", m.getName() ), 265 null ); 266 267 it.remove(); 268 } 269 else 270 { 271 this.log( Level.INFO, 272 Messages.getMessage( "moduleInclusionInfo", m.getName() ), 273 null ); 274 275 } 276 } 277 } 278 } 279 280 if ( commandLine.hasOption( Options.MODULE_EXCLUDES_OPTION.getOpt() ) ) 281 { 282 final String[] values = commandLine.getOptionValues( Options.MODULE_EXCLUDES_OPTION.getOpt() ); 283 284 if ( values != null ) 285 { 286 for ( final String exclude : values ) 287 { 288 final Module m = modules.getModule( exclude ); 289 290 if ( m != null ) 291 { 292 this.log( Level.INFO, 293 Messages.getMessage( "moduleExclusionInfo", m.getName() ), 294 null ); 295 296 modules.getModule().remove( m ); 297 } 298 } 299 } 300 } 301 302 Module classpathModule = null; 303 if ( !commandLine.hasOption( Options.NO_CLASSPATH_RESOLUTION_OPTION.getOpt() ) ) 304 { 305 classpathModule = modules.getClasspathModule( Modules.getDefaultClasspathModuleName(), classLoader ); 306 if ( classpathModule != null && modules.getModule( Modules.getDefaultClasspathModuleName() ) == null ) 307 { 308 modules.getModule().add( classpathModule ); 309 } 310 else 311 { 312 classpathModule = null; 313 } 314 } 315 316 final ModelValidationReport validationReport = context.validateModel( 317 model, new JAXBSource( marshaller, new ObjectFactory().createModules( modules ) ) ); 318 319 this.log( validationReport, marshaller ); 320 321 if ( !validationReport.isModelValid() ) 322 { 323 throw new CommandExecutionException( Messages.getMessage( "invalidModel", model ) ); 324 } 325 326 if ( classpathModule != null ) 327 { 328 modules.getModule().remove( classpathModule ); 329 } 330 331 Module mergedModule = 332 modules.getMergedModule( commandLine.getOptionValue( Options.MODULE_OPTION.getOpt() ) ); 333 334 mergedModule.setVersion( moduleVersion ); 335 mergedModule.setVendor( moduleVendor ); 336 337 final File moduleFile = new File( commandLine.getOptionValue( Options.DOCUMENT_OPTION.getOpt() ) ); 338 339 if ( stylesheetFile != null ) 340 { 341 final Transformer transformer = this.createTransformer( new StreamSource( stylesheetFile ) ); 342 final JAXBSource source = 343 new JAXBSource( marshaller, new ObjectFactory().createModule( mergedModule ) ); 344 345 final JAXBResult result = new JAXBResult( unmarshaller ); 346 unmarshaller.setSchema( null ); 347 transformer.transform( source, result ); 348 349 if ( result.getResult() instanceof JAXBElement<?> 350 && ( (JAXBElement<?>) result.getResult() ).getValue() instanceof Module ) 351 { 352 mergedModule = (Module) ( (JAXBElement<?>) result.getResult() ).getValue(); 353 } 354 else 355 { 356 throw new CommandExecutionException( Messages.getMessage( "illegalTransformationResultError", 357 stylesheetFile.getAbsolutePath() ) ); 358 359 } 360 } 361 362 marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE ); 363 364 if ( commandLine.hasOption( Options.DOCUMENT_ENCODING_OPTION.getOpt() ) ) 365 { 366 marshaller.setProperty( Marshaller.JAXB_ENCODING, 367 commandLine.getOptionValue( Options.DOCUMENT_ENCODING_OPTION.getOpt() ) ); 368 369 } 370 371 marshaller.setSchema( context.createSchema( model ) ); 372 marshaller.marshal( new ObjectFactory().createModule( mergedModule ), moduleFile ); 373 374 if ( this.isLoggable( Level.INFO ) ) 375 { 376 this.log( Level.INFO, Messages.getMessage( "writingResource", moduleFile.getAbsolutePath() ), null ); 377 } 378 379 classLoader.close(); 380 classLoader = null; 381 } 382 catch ( final IOException e ) 383 { 384 throw new CommandExecutionException( Messages.getMessage( e ), e ); 385 } 386 catch ( final TransformerException e ) 387 { 388 String message = Messages.getMessage( e ); 389 if ( message == null ) 390 { 391 message = Messages.getMessage( e.getException() ); 392 } 393 394 throw new CommandExecutionException( message, e ); 395 } 396 catch ( final JAXBException e ) 397 { 398 String message = Messages.getMessage( e ); 399 if ( message == null ) 400 { 401 message = Messages.getMessage( e.getLinkedException() ); 402 } 403 404 throw new CommandExecutionException( message, e ); 405 } 406 catch ( final ModelException e ) 407 { 408 throw new CommandExecutionException( Messages.getMessage( e ), e ); 409 } 410 finally 411 { 412 try 413 { 414 if ( classLoader != null ) 415 { 416 classLoader.close(); 417 } 418 } 419 catch ( final IOException e ) 420 { 421 this.log( Level.SEVERE, Messages.getMessage( e ), e ); 422 } 423 } 424 } 425 426}