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: JomcModelTask.java 4704 2013-01-02 05:15:52Z schulte $ 029 * 030 */ 031package org.jomc.ant; 032 033import java.io.IOException; 034import java.io.InputStream; 035import java.net.SocketTimeoutException; 036import java.net.URISyntaxException; 037import java.net.URL; 038import java.net.URLConnection; 039import java.util.HashSet; 040import java.util.Set; 041import java.util.logging.Level; 042import javax.xml.bind.JAXBElement; 043import javax.xml.bind.JAXBException; 044import javax.xml.bind.Unmarshaller; 045import javax.xml.transform.Source; 046import javax.xml.transform.stream.StreamSource; 047import org.apache.tools.ant.BuildException; 048import org.apache.tools.ant.Project; 049import org.jomc.ant.types.KeyValueType; 050import org.jomc.ant.types.ModuleResourceType; 051import org.jomc.ant.types.ResourceType; 052import org.jomc.model.Module; 053import org.jomc.model.Modules; 054import org.jomc.model.modlet.DefaultModelProcessor; 055import org.jomc.model.modlet.DefaultModelProvider; 056import org.jomc.model.modlet.DefaultModelValidator; 057import org.jomc.model.modlet.ModelHelper; 058import org.jomc.modlet.Model; 059import org.jomc.modlet.ModelContext; 060import org.jomc.modlet.ModelException; 061import org.jomc.tools.modlet.ToolsModelProcessor; 062import org.jomc.tools.modlet.ToolsModelProvider; 063 064/** 065 * Base class for executing model based tasks. 066 * 067 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 068 * @version $JOMC: JomcModelTask.java 4704 2013-01-02 05:15:52Z schulte $ 069 */ 070public class JomcModelTask extends JomcTask 071{ 072 073 /** Controls model object class path resolution. */ 074 private boolean modelObjectClasspathResolutionEnabled = true; 075 076 /** The location to search for modules. */ 077 private String moduleLocation; 078 079 /** The location to search for transformers. */ 080 private String transformerLocation; 081 082 /** Module resources. */ 083 private Set<ModuleResourceType> moduleResources; 084 085 /** The flag indicating JAXP schema validation of model resources is enabled. */ 086 private boolean modelResourceValidationEnabled = true; 087 088 /** The flag indicating Java validation is enabled. */ 089 private boolean javaValidationEnabled = true; 090 091 /** Creates a new {@code JomcModelTask} instance. */ 092 public JomcModelTask() 093 { 094 super(); 095 } 096 097 /** 098 * Gets the location searched for modules. 099 * 100 * @return The location searched for modules or {@code null}. 101 * 102 * @see #setModuleLocation(java.lang.String) 103 */ 104 public final String getModuleLocation() 105 { 106 return this.moduleLocation; 107 } 108 109 /** 110 * Sets the location to search for modules. 111 * 112 * @param value The new location to search for modules or {@code null}. 113 * 114 * @see #getModuleLocation() 115 */ 116 public final void setModuleLocation( final String value ) 117 { 118 this.moduleLocation = value; 119 } 120 121 /** 122 * Gets the location searched for transformers. 123 * 124 * @return The location searched for transformers or {@code null}. 125 * 126 * @see #setTransformerLocation(java.lang.String) 127 */ 128 public final String getTransformerLocation() 129 { 130 return this.transformerLocation; 131 } 132 133 /** 134 * Sets the location to search for transformers. 135 * 136 * @param value The new location to search for transformers or {@code null}. 137 * 138 * @see #getTransformerLocation() 139 */ 140 public final void setTransformerLocation( final String value ) 141 { 142 this.transformerLocation = value; 143 } 144 145 /** 146 * Gets a flag indicating model object class path resolution is enabled. 147 * 148 * @return {@code true}, if model object class path resolution is enabled; {@code false}, else. 149 * 150 * @see #setModelObjectClasspathResolutionEnabled(boolean) 151 */ 152 public final boolean isModelObjectClasspathResolutionEnabled() 153 { 154 return this.modelObjectClasspathResolutionEnabled; 155 } 156 157 /** 158 * Sets the flag indicating model object class path resolution is enabled. 159 * 160 * @param value {@code true}, to enable model object class path resolution; {@code false}, to disable model object 161 * class path resolution. 162 * 163 * @see #isModelObjectClasspathResolutionEnabled() 164 */ 165 public final void setModelObjectClasspathResolutionEnabled( final boolean value ) 166 { 167 this.modelObjectClasspathResolutionEnabled = value; 168 } 169 170 /** 171 * Gets a set of module resources. 172 * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make 173 * to the returned set will be present inside the object. This is why there is no {@code set} method for the 174 * module resources property.</p> 175 * 176 * @return A set of module resources. 177 * 178 * @see #createModuleResource() 179 */ 180 public Set<ModuleResourceType> getModuleResources() 181 { 182 if ( this.moduleResources == null ) 183 { 184 this.moduleResources = new HashSet<ModuleResourceType>(); 185 } 186 187 return this.moduleResources; 188 } 189 190 /** 191 * Creates a new {@code moduleResource} element instance. 192 * 193 * @return A new {@code moduleResource} element instance. 194 * 195 * @see #getModuleResources() 196 */ 197 public ModuleResourceType createModuleResource() 198 { 199 final ModuleResourceType moduleResource = new ModuleResourceType(); 200 this.getModuleResources().add( moduleResource ); 201 return moduleResource; 202 } 203 204 /** 205 * Gets a flag indicating JAXP schema validation of model resources is enabled. 206 * 207 * @return {@code true}, if JAXP schema validation of model resources is enabled; {@code false}, else. 208 * 209 * @see #setModelResourceValidationEnabled(boolean) 210 */ 211 public final boolean isModelResourceValidationEnabled() 212 { 213 return this.modelResourceValidationEnabled; 214 } 215 216 /** 217 * Sets the flag indicating JAXP schema validation of model resources is enabled. 218 * 219 * @param value {@code true}, to enable JAXP schema validation of model resources; {@code false}, to disable JAXP 220 * schema validation of model resources. 221 * 222 * @see #isModelResourceValidationEnabled() 223 */ 224 public final void setModelResourceValidationEnabled( final boolean value ) 225 { 226 this.modelResourceValidationEnabled = value; 227 } 228 229 /** 230 * Gets a flag indicating Java validation is enabled. 231 * 232 * @return {@code true}, if Java validation is enabled; {@code false}, else. 233 * 234 * @see #setJavaValidationEnabled(boolean) 235 * 236 * @since 1.4 237 */ 238 public final boolean isJavaValidationEnabled() 239 { 240 return this.javaValidationEnabled; 241 } 242 243 /** 244 * Sets the flag indicating Java validation is enabled. 245 * 246 * @param value {@code true}, to enable Java validation; {@code false}, to disable Java validation. 247 * 248 * @see #isJavaValidationEnabled() 249 * 250 * @since 1.4 251 */ 252 public final void setJavaValidationEnabled( final boolean value ) 253 { 254 this.javaValidationEnabled = value; 255 } 256 257 /** 258 * Gets a {@code Model} from a given {@code ModelContext}. 259 * 260 * @param context The context to get a {@code Model} from. 261 * 262 * @return The {@code Model} from {@code context}. 263 * 264 * @throws NullPointerException if {@code contexŧ} is {@code null}. 265 * @throws BuildException if no model is found. 266 * @throws ModelException if getting the model fails. 267 * 268 * @see #getModel() 269 * @see #isModelObjectClasspathResolutionEnabled() 270 * @see #isModelProcessingEnabled() 271 */ 272 @Override 273 public Model getModel( final ModelContext context ) throws BuildException, ModelException 274 { 275 if ( context == null ) 276 { 277 throw new NullPointerException( "context" ); 278 } 279 280 Model model = new Model(); 281 model.setIdentifier( this.getModel() ); 282 Modules modules = new Modules(); 283 ModelHelper.setModules( model, modules ); 284 Unmarshaller unmarshaller = null; 285 286 for ( ResourceType resource : this.getModuleResources() ) 287 { 288 final URL[] urls = this.getResources( context, resource.getLocation() ); 289 290 if ( urls.length == 0 ) 291 { 292 if ( resource.isOptional() ) 293 { 294 this.logMessage( Level.WARNING, Messages.getMessage( "moduleResourceNotFound", 295 resource.getLocation() ) ); 296 297 } 298 else 299 { 300 throw new BuildException( Messages.getMessage( "moduleResourceNotFound", resource.getLocation() ), 301 this.getLocation() ); 302 303 } 304 } 305 306 for ( int i = urls.length - 1; i >= 0; i-- ) 307 { 308 InputStream in = null; 309 boolean suppressExceptionOnClose = true; 310 311 try 312 { 313 this.logMessage( Level.FINEST, Messages.getMessage( "reading", urls[i].toExternalForm() ) ); 314 315 final URLConnection con = urls[i].openConnection(); 316 con.setConnectTimeout( resource.getConnectTimeout() ); 317 con.setReadTimeout( resource.getReadTimeout() ); 318 con.connect(); 319 in = con.getInputStream(); 320 321 final Source source = new StreamSource( in, urls[i].toURI().toASCIIString() ); 322 323 if ( unmarshaller == null ) 324 { 325 unmarshaller = context.createUnmarshaller( this.getModel() ); 326 if ( this.isModelResourceValidationEnabled() ) 327 { 328 unmarshaller.setSchema( context.createSchema( this.getModel() ) ); 329 } 330 } 331 332 Object o = unmarshaller.unmarshal( source ); 333 if ( o instanceof JAXBElement<?> ) 334 { 335 o = ( (JAXBElement<?>) o ).getValue(); 336 } 337 338 if ( o instanceof Module ) 339 { 340 modules.getModule().add( (Module) o ); 341 } 342 else 343 { 344 this.log( Messages.getMessage( "unsupportedModuleResource", urls[i].toExternalForm() ), 345 Project.MSG_WARN ); 346 347 } 348 349 suppressExceptionOnClose = false; 350 } 351 catch ( final SocketTimeoutException e ) 352 { 353 String message = Messages.getMessage( e ); 354 message = Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" ); 355 356 if ( resource.isOptional() ) 357 { 358 this.getProject().log( message, e, Project.MSG_WARN ); 359 } 360 else 361 { 362 throw new BuildException( message, e, this.getLocation() ); 363 } 364 } 365 catch ( final IOException e ) 366 { 367 String message = Messages.getMessage( e ); 368 message = Messages.getMessage( "resourceFailure", message != null ? " " + message : "" ); 369 370 if ( resource.isOptional() ) 371 { 372 this.getProject().log( message, e, Project.MSG_WARN ); 373 } 374 else 375 { 376 throw new BuildException( message, e, this.getLocation() ); 377 } 378 } 379 catch ( final URISyntaxException e ) 380 { 381 throw new BuildException( Messages.getMessage( e ), e, this.getLocation() ); 382 } 383 catch ( final JAXBException e ) 384 { 385 String message = Messages.getMessage( e ); 386 if ( message == null ) 387 { 388 message = Messages.getMessage( e.getLinkedException() ); 389 } 390 391 throw new BuildException( message, e, this.getLocation() ); 392 } 393 finally 394 { 395 try 396 { 397 if ( in != null ) 398 { 399 in.close(); 400 } 401 } 402 catch ( final IOException e ) 403 { 404 if ( suppressExceptionOnClose ) 405 { 406 this.logMessage( Level.SEVERE, Messages.getMessage( e ), e ); 407 } 408 else 409 { 410 throw new BuildException( Messages.getMessage( e ), e, this.getLocation() ); 411 } 412 } 413 } 414 } 415 } 416 417 model = context.findModel( model ); 418 modules = ModelHelper.getModules( model ); 419 420 if ( modules != null && this.isModelObjectClasspathResolutionEnabled() ) 421 { 422 final Module classpathModule = 423 modules.getClasspathModule( Modules.getDefaultClasspathModuleName(), context.getClassLoader() ); 424 425 if ( classpathModule != null && modules.getModule( Modules.getDefaultClasspathModuleName() ) == null ) 426 { 427 modules.getModule().add( classpathModule ); 428 } 429 } 430 431 if ( this.isModelProcessingEnabled() ) 432 { 433 model = context.processModel( model ); 434 } 435 436 return model; 437 } 438 439 /** {@inheritDoc} */ 440 @Override 441 public void preExecuteTask() throws BuildException 442 { 443 super.preExecuteTask(); 444 this.assertLocationsNotNull( this.getModuleResources() ); 445 } 446 447 /** {@inheritDoc} */ 448 @Override 449 public ModelContext newModelContext( final ClassLoader classLoader ) throws ModelException 450 { 451 final ModelContext modelContext = super.newModelContext( classLoader ); 452 453 if ( this.getTransformerLocation() != null ) 454 { 455 modelContext.setAttribute( DefaultModelProcessor.TRANSFORMER_LOCATION_ATTRIBUTE_NAME, 456 this.getTransformerLocation() ); 457 458 } 459 460 if ( this.getModuleLocation() != null ) 461 { 462 modelContext.setAttribute( DefaultModelProvider.MODULE_LOCATION_ATTRIBUTE_NAME, this.getModuleLocation() ); 463 } 464 465 modelContext.setAttribute( ToolsModelProvider.MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME, 466 this.isModelObjectClasspathResolutionEnabled() ); 467 468 modelContext.setAttribute( ToolsModelProcessor.MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME, 469 this.isModelObjectClasspathResolutionEnabled() ); 470 471 modelContext.setAttribute( DefaultModelProvider.VALIDATING_ATTRIBUTE_NAME, 472 this.isModelResourceValidationEnabled() ); 473 474 modelContext.setAttribute( DefaultModelValidator.VALIDATE_JAVA_ATTRIBUTE_NAME, this.isJavaValidationEnabled() ); 475 476 for ( int i = 0, s0 = this.getModelContextAttributes().size(); i < s0; i++ ) 477 { 478 final KeyValueType kv = this.getModelContextAttributes().get( i ); 479 final Object object = kv.getObject( this.getLocation() ); 480 481 if ( object != null ) 482 { 483 modelContext.setAttribute( kv.getKey(), object ); 484 } 485 else 486 { 487 modelContext.clearAttribute( kv.getKey() ); 488 } 489 } 490 491 492 return modelContext; 493 } 494 495 /** {@inheritDoc} */ 496 @Override 497 public JomcModelTask clone() 498 { 499 final JomcModelTask clone = (JomcModelTask) super.clone(); 500 501 if ( this.moduleResources != null ) 502 { 503 clone.moduleResources = new HashSet<ModuleResourceType>( this.moduleResources.size() ); 504 for ( ModuleResourceType e : this.moduleResources ) 505 { 506 clone.moduleResources.add( e.clone() ); 507 } 508 } 509 510 return clone; 511 } 512 513}