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