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