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: JomcTask.java 4613 2012-09-22 10:07:08Z schulte $ 029 * 030 */ 031package org.jomc.ant; 032 033import java.io.BufferedReader; 034import java.io.File; 035import java.io.IOException; 036import java.io.InputStream; 037import java.io.StringReader; 038import java.io.StringWriter; 039import java.net.MalformedURLException; 040import java.net.SocketTimeoutException; 041import java.net.URI; 042import java.net.URISyntaxException; 043import java.net.URL; 044import java.net.URLConnection; 045import java.util.ArrayList; 046import java.util.Collection; 047import java.util.Enumeration; 048import java.util.HashSet; 049import java.util.Iterator; 050import java.util.LinkedList; 051import java.util.List; 052import java.util.Map; 053import java.util.Properties; 054import java.util.Set; 055import java.util.logging.Level; 056import javax.xml.bind.JAXBException; 057import javax.xml.bind.Marshaller; 058import javax.xml.transform.ErrorListener; 059import javax.xml.transform.Transformer; 060import javax.xml.transform.TransformerConfigurationException; 061import javax.xml.transform.TransformerException; 062import javax.xml.transform.TransformerFactory; 063import javax.xml.transform.stream.StreamSource; 064import org.apache.tools.ant.BuildException; 065import org.apache.tools.ant.Project; 066import org.apache.tools.ant.PropertyHelper; 067import org.apache.tools.ant.Task; 068import org.apache.tools.ant.types.Path; 069import org.apache.tools.ant.types.Reference; 070import org.jomc.ant.types.KeyValueType; 071import org.jomc.ant.types.NameType; 072import org.jomc.ant.types.PropertiesFormatType; 073import org.jomc.ant.types.PropertiesResourceType; 074import org.jomc.ant.types.ResourceType; 075import org.jomc.ant.types.TransformerResourceType; 076import org.jomc.model.ModelObject; 077import org.jomc.modlet.DefaultModelContext; 078import org.jomc.modlet.DefaultModletProvider; 079import org.jomc.modlet.Model; 080import org.jomc.modlet.ModelContext; 081import org.jomc.modlet.ModelContextFactory; 082import org.jomc.modlet.ModelException; 083import org.jomc.modlet.ModelValidationReport; 084import org.jomc.modlet.ModletProvider; 085 086/** 087 * Base class for executing tasks. 088 * 089 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 090 * @version $JOMC: JomcTask.java 4613 2012-09-22 10:07:08Z schulte $ 091 * @see #execute() 092 */ 093public class JomcTask extends Task 094{ 095 096 /** The class path to process. */ 097 private Path classpath; 098 099 /** The identifier of the model to process. */ 100 private String model; 101 102 /** {@code ModelContext} attributes to apply. */ 103 private List<KeyValueType> modelContextAttributes; 104 105 /** The name of the {@code ModelContextFactory} implementation class backing the task. */ 106 private String modelContextFactoryClassName; 107 108 /** Controls processing of models. */ 109 private boolean modelProcessingEnabled = true; 110 111 /** The location to search for modlets. */ 112 private String modletLocation; 113 114 /** The {@code http://jomc.org/modlet} namespace schema system id of the context backing the task. */ 115 private String modletSchemaSystemId; 116 117 /** The location to search for providers. */ 118 private String providerLocation; 119 120 /** The location to search for platform providers. */ 121 private String platformProviderLocation; 122 123 /** The global transformation parameters to apply. */ 124 private List<KeyValueType> transformationParameters; 125 126 /** The global transformation parameter resources to apply. */ 127 private List<PropertiesResourceType> transformationParameterResources; 128 129 /** The global transformation output properties to apply. */ 130 private List<KeyValueType> transformationOutputProperties; 131 132 /** The flag indicating JAXP schema validation of modlet resources is enabled. */ 133 private boolean modletResourceValidationEnabled = true; 134 135 /** Property controlling the execution of the task. */ 136 private Object _if; 137 138 /** Property controlling the execution of the task. */ 139 private Object unless; 140 141 /** Creates a new {@code JomcTask} instance. */ 142 public JomcTask() 143 { 144 super(); 145 } 146 147 /** 148 * Gets an object controlling the execution of the task. 149 * 150 * @return An object controlling the execution of the task or {@code null}. 151 * 152 * @see #setIf(java.lang.Object) 153 */ 154 public final Object getIf() 155 { 156 return this._if; 157 } 158 159 /** 160 * Sets an object controlling the execution of the task. 161 * 162 * @param value The new object controlling the execution of the task or {@code null}. 163 * 164 * @see #getIf() 165 */ 166 public final void setIf( final Object value ) 167 { 168 this._if = value; 169 } 170 171 /** 172 * Gets an object controlling the execution of the task. 173 * 174 * @return An object controlling the execution of the task or {@code null}. 175 * 176 * @see #setUnless(java.lang.Object) 177 */ 178 public final Object getUnless() 179 { 180 if ( this.unless == null ) 181 { 182 this.unless = Boolean.TRUE; 183 } 184 185 return this.unless; 186 } 187 188 /** 189 * Sets an object controlling the execution of the task. 190 * 191 * @param value The new object controlling the execution of the task or {@code null}. 192 * 193 * @see #getUnless() 194 */ 195 public final void setUnless( final Object value ) 196 { 197 this.unless = value; 198 } 199 200 /** 201 * Creates a new {@code classpath} element instance. 202 * 203 * @return A new {@code classpath} element instance. 204 */ 205 public final Path createClasspath() 206 { 207 return this.getClasspath().createPath(); 208 } 209 210 /** 211 * Gets the class path to process. 212 * 213 * @return The class path to process. 214 * 215 * @see #setClasspath(org.apache.tools.ant.types.Path) 216 */ 217 public final Path getClasspath() 218 { 219 if ( this.classpath == null ) 220 { 221 this.classpath = new Path( this.getProject() ); 222 } 223 224 return this.classpath; 225 } 226 227 /** 228 * Adds to the class path to process. 229 * 230 * @param value The path to add to the list of class path elements. 231 * 232 * @see #getClasspath() 233 */ 234 public final void setClasspath( final Path value ) 235 { 236 this.getClasspath().add( value ); 237 } 238 239 /** 240 * Adds a reference to a class path defined elsewhere. 241 * 242 * @param value A reference to a class path. 243 * 244 * @see #getClasspath() 245 */ 246 public final void setClasspathRef( final Reference value ) 247 { 248 this.getClasspath().setRefid( value ); 249 } 250 251 /** 252 * Gets the identifier of the model to process. 253 * 254 * @return The identifier of the model to process. 255 * 256 * @see #setModel(java.lang.String) 257 */ 258 public final String getModel() 259 { 260 if ( this.model == null ) 261 { 262 this.model = ModelObject.MODEL_PUBLIC_ID; 263 } 264 265 return this.model; 266 } 267 268 /** 269 * Sets the identifier of the model to process. 270 * 271 * @param value The new identifier of the model to process or {@code null}. 272 * 273 * @see #getModel() 274 */ 275 public final void setModel( final String value ) 276 { 277 this.model = value; 278 } 279 280 /** 281 * Gets the {@code ModelContext} attributes to apply. 282 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make 283 * to the returned list will be present inside the object. This is why there is no {@code set} method for the 284 * model context attributes property.</p> 285 * 286 * @return The {@code ModelContext} attributes to apply. 287 * 288 * @see #createModelContextAttribute() 289 * @see #newModelContext(java.lang.ClassLoader) 290 */ 291 public final List<KeyValueType> getModelContextAttributes() 292 { 293 if ( this.modelContextAttributes == null ) 294 { 295 this.modelContextAttributes = new LinkedList<KeyValueType>(); 296 } 297 298 return this.modelContextAttributes; 299 } 300 301 /** 302 * Creates a new {@code modelContextAttribute} element instance. 303 * 304 * @return A new {@code modelContextAttribute} element instance. 305 * 306 * @see #getModelContextAttributes() 307 */ 308 public KeyValueType createModelContextAttribute() 309 { 310 final KeyValueType modelContextAttribute = new KeyValueType(); 311 this.getModelContextAttributes().add( modelContextAttribute ); 312 return modelContextAttribute; 313 } 314 315 /** 316 * Gets the name of the {@code ModelContextFactory} implementation class backing the task. 317 * 318 * @return The name of the {@code ModelContextFactory} implementation class backing the task or {@code null}. 319 * 320 * @see #setModelContextFactoryClassName(java.lang.String) 321 */ 322 public final String getModelContextFactoryClassName() 323 { 324 return this.modelContextFactoryClassName; 325 } 326 327 /** 328 * Sets the name of the {@code ModelContextFactory} implementation class backing the task. 329 * 330 * @param value The new name of the {@code ModelContextFactory} implementation class backing the task or 331 * {@code null}. 332 * 333 * @see #getModelContextFactoryClassName() 334 */ 335 public final void setModelContextFactoryClassName( final String value ) 336 { 337 this.modelContextFactoryClassName = value; 338 } 339 340 /** 341 * Gets a flag indicating the processing of models is enabled. 342 * 343 * @return {@code true}, if processing of models is enabled; {@code false}, else. 344 * 345 * @see #setModelProcessingEnabled(boolean) 346 */ 347 public final boolean isModelProcessingEnabled() 348 { 349 return this.modelProcessingEnabled; 350 } 351 352 /** 353 * Sets the flag indicating the processing of models is enabled. 354 * 355 * @param value {@code true}, to enable processing of models; {@code false}, to disable processing of models. 356 * 357 * @see #isModelProcessingEnabled() 358 */ 359 public final void setModelProcessingEnabled( final boolean value ) 360 { 361 this.modelProcessingEnabled = value; 362 } 363 364 /** 365 * Gets the location searched for modlets. 366 * 367 * @return The location searched for modlets or {@code null}. 368 * 369 * @see #setModletLocation(java.lang.String) 370 */ 371 public final String getModletLocation() 372 { 373 return this.modletLocation; 374 } 375 376 /** 377 * Sets the location to search for modlets. 378 * 379 * @param value The new location to search for modlets or {@code null}. 380 * 381 * @see #getModletLocation() 382 */ 383 public final void setModletLocation( final String value ) 384 { 385 this.modletLocation = value; 386 } 387 388 /** 389 * Gets the {@code http://jomc.org/modlet} namespace schema system id of the context backing the task. 390 * 391 * @return The {@code http://jomc.org/modlet} namespace schema system id of the context backing the task or 392 * {@code null}. 393 * 394 * @see #setModletSchemaSystemId(java.lang.String) 395 */ 396 public final String getModletSchemaSystemId() 397 { 398 return this.modletSchemaSystemId; 399 } 400 401 /** 402 * Sets the {@code http://jomc.org/modlet} namespace schema system id of the context backing the task. 403 * 404 * @param value The new {@code http://jomc.org/modlet} namespace schema system id of the context backing the task or 405 * {@code null}. 406 * 407 * @see #getModletSchemaSystemId() 408 */ 409 public final void setModletSchemaSystemId( final String value ) 410 { 411 this.modletSchemaSystemId = value; 412 } 413 414 /** 415 * Gets the location searched for providers. 416 * 417 * @return The location searched for providers or {@code null}. 418 * 419 * @see #setProviderLocation(java.lang.String) 420 */ 421 public final String getProviderLocation() 422 { 423 return this.providerLocation; 424 } 425 426 /** 427 * Sets the location to search for providers. 428 * 429 * @param value The new location to search for providers or {@code null}. 430 * 431 * @see #getProviderLocation() 432 */ 433 public final void setProviderLocation( final String value ) 434 { 435 this.providerLocation = value; 436 } 437 438 /** 439 * Gets the location searched for platform provider resources. 440 * 441 * @return The location searched for platform provider resources or {@code null}. 442 * 443 * @see #setPlatformProviderLocation(java.lang.String) 444 */ 445 public final String getPlatformProviderLocation() 446 { 447 return this.platformProviderLocation; 448 } 449 450 /** 451 * Sets the location to search for platform provider resources. 452 * 453 * @param value The new location to search for platform provider resources or {@code null}. 454 * 455 * @see #getPlatformProviderLocation() 456 */ 457 public final void setPlatformProviderLocation( final String value ) 458 { 459 this.platformProviderLocation = value; 460 } 461 462 /** 463 * Gets the global transformation parameters to apply. 464 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make 465 * to the returned list will be present inside the object. This is why there is no {@code set} method for the 466 * transformation parameters property.</p> 467 * 468 * @return The global transformation parameters to apply. 469 * 470 * @see #createTransformationParameter() 471 * @see #getTransformer(org.jomc.ant.types.TransformerResourceType) 472 */ 473 public final List<KeyValueType> getTransformationParameters() 474 { 475 if ( this.transformationParameters == null ) 476 { 477 this.transformationParameters = new LinkedList<KeyValueType>(); 478 } 479 480 return this.transformationParameters; 481 } 482 483 /** 484 * Creates a new {@code transformationParameter} element instance. 485 * 486 * @return A new {@code transformationParameter} element instance. 487 * 488 * @see #getTransformationParameters() 489 */ 490 public KeyValueType createTransformationParameter() 491 { 492 final KeyValueType transformationParameter = new KeyValueType(); 493 this.getTransformationParameters().add( transformationParameter ); 494 return transformationParameter; 495 } 496 497 /** 498 * Gets the global transformation parameter resources to apply. 499 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make 500 * to the returned list will be present inside the object. This is why there is no {@code set} method for the 501 * transformation parameter resources property.</p> 502 * 503 * @return The global transformation parameter resources to apply. 504 * 505 * @see #createTransformationParameterResource() 506 * @see #getTransformer(org.jomc.ant.types.TransformerResourceType) 507 */ 508 public final List<PropertiesResourceType> getTransformationParameterResources() 509 { 510 if ( this.transformationParameterResources == null ) 511 { 512 this.transformationParameterResources = new LinkedList<PropertiesResourceType>(); 513 } 514 515 return this.transformationParameterResources; 516 } 517 518 /** 519 * Creates a new {@code transformationParameterResource} element instance. 520 * 521 * @return A new {@code transformationParameterResource} element instance. 522 * 523 * @see #getTransformationParameterResources() 524 */ 525 public PropertiesResourceType createTransformationParameterResource() 526 { 527 final PropertiesResourceType transformationParameterResource = new PropertiesResourceType(); 528 this.getTransformationParameterResources().add( transformationParameterResource ); 529 return transformationParameterResource; 530 } 531 532 /** 533 * Gets the global transformation output properties to apply. 534 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make 535 * to the returned list will be present inside the object. This is why there is no {@code set} method for the 536 * transformation output properties property.</p> 537 * 538 * @return The global transformation output properties to apply. 539 * 540 * @see #createTransformationOutputProperty() 541 */ 542 public final List<KeyValueType> getTransformationOutputProperties() 543 { 544 if ( this.transformationOutputProperties == null ) 545 { 546 this.transformationOutputProperties = new LinkedList<KeyValueType>(); 547 } 548 549 return this.transformationOutputProperties; 550 } 551 552 /** 553 * Creates a new {@code transformationOutputProperty} element instance. 554 * 555 * @return A new {@code transformationOutputProperty} element instance. 556 * 557 * @see #getTransformationOutputProperties() 558 */ 559 public KeyValueType createTransformationOutputProperty() 560 { 561 final KeyValueType transformationOutputProperty = new KeyValueType(); 562 this.getTransformationOutputProperties().add( transformationOutputProperty ); 563 return transformationOutputProperty; 564 } 565 566 /** 567 * Gets a flag indicating JAXP schema validation of modlet resources is enabled. 568 * 569 * @return {@code true}, if JAXP schema validation of modlet resources is enabled; {@code false}, else. 570 * 571 * @see #setModletResourceValidationEnabled(boolean) 572 */ 573 public final boolean isModletResourceValidationEnabled() 574 { 575 return this.modletResourceValidationEnabled; 576 } 577 578 /** 579 * Sets the flag indicating JAXP schema validation of modlet resources is enabled. 580 * 581 * @param value {@code true}, to enable JAXP schema validation of modlet resources; {@code false}, to disable JAXP 582 * schema validation of modlet resources. 583 * 584 * @see #isModletResourceValidationEnabled() 585 */ 586 public final void setModletResourceValidationEnabled( final boolean value ) 587 { 588 this.modletResourceValidationEnabled = value; 589 } 590 591 /** 592 * Called by the project to let the task do its work. 593 * 594 * @throws BuildException if execution fails. 595 * 596 * @see #getIf() 597 * @see #getUnless() 598 * @see #preExecuteTask() 599 * @see #executeTask() 600 * @see #postExecuteTask() 601 */ 602 @Override 603 public final void execute() throws BuildException 604 { 605 final PropertyHelper propertyHelper = PropertyHelper.getPropertyHelper( this.getProject() ); 606 607 if ( propertyHelper.testIfCondition( this.getIf() ) && !propertyHelper.testUnlessCondition( this.getUnless() ) ) 608 { 609 try 610 { 611 this.preExecuteTask(); 612 this.executeTask(); 613 } 614 finally 615 { 616 this.postExecuteTask(); 617 } 618 } 619 } 620 621 /** 622 * Called by the {@code execute} method prior to the {@code executeTask} method. 623 * 624 * @throws BuildException if execution fails. 625 * 626 * @see #execute() 627 */ 628 public void preExecuteTask() throws BuildException 629 { 630 this.logSeparator(); 631 this.log( Messages.getMessage( "title" ) ); 632 this.logSeparator(); 633 634 this.assertNotNull( "model", this.getModel() ); 635 this.assertKeysNotNull( this.getModelContextAttributes() ); 636 this.assertKeysNotNull( this.getTransformationParameters() ); 637 this.assertKeysNotNull( this.getTransformationOutputProperties() ); 638 this.assertLocationsNotNull( this.getTransformationParameterResources() ); 639 } 640 641 /** 642 * Called by the {@code execute} method prior to the {@code postExecuteTask} method. 643 * 644 * @throws BuildException if execution fails. 645 * 646 * @see #execute() 647 */ 648 public void executeTask() throws BuildException 649 { 650 this.getProject().log( Messages.getMessage( "unimplementedTask", this.getClass().getName(), "executeTask" ), 651 Project.MSG_WARN ); 652 653 } 654 655 /** 656 * Called by the {@code execute} method after the {@code preExecuteTask}/{@code executeTask} methods even if those 657 * methods threw an exception. 658 * 659 * @throws BuildException if execution fails. 660 * 661 * @see #execute() 662 */ 663 public void postExecuteTask() throws BuildException 664 { 665 this.logSeparator(); 666 } 667 668 /** 669 * Gets a {@code Model} from a given {@code ModelContext}. 670 * 671 * @param context The context to get a {@code Model} from. 672 * 673 * @return The {@code Model} from {@code context}. 674 * 675 * @throws NullPointerException if {@code contexŧ} is {@code null}. 676 * @throws ModelException if getting the model fails. 677 * 678 * @see #getModel() 679 * @see #isModelProcessingEnabled() 680 */ 681 public Model getModel( final ModelContext context ) throws ModelException 682 { 683 if ( context == null ) 684 { 685 throw new NullPointerException( "context" ); 686 } 687 688 Model foundModel = context.findModel( this.getModel() ); 689 690 if ( foundModel != null && this.isModelProcessingEnabled() ) 691 { 692 foundModel = context.processModel( foundModel ); 693 } 694 695 return foundModel; 696 } 697 698 /** 699 * Creates an {@code URL} for a given resource location. 700 * <p>This method first searches the class path of the task for a single resource matching {@code location}. If 701 * such a resource is found, the URL of that resource is returned. If no such resource is found, an attempt is made 702 * to parse the given location to an URL. On successful parsing, that URL is returned. Failing that, the given 703 * location is interpreted as a file name relative to the project's base directory. If that file is found, the URL 704 * of that file is returned. Otherwise {@code null} is returned.</p> 705 * 706 * @param location The resource location to create an {@code URL} from. 707 * 708 * @return An {@code URL} for {@code location} or {@code null}, if parsing {@code location} to an URL fails and 709 * {@code location} points to a non-existent resource. 710 * 711 * @throws NullPointerException if {@code location} is {@code null}. 712 * @throws BuildException if creating an URL fails. 713 */ 714 public URL getResource( final String location ) throws BuildException 715 { 716 if ( location == null ) 717 { 718 throw new NullPointerException( "location" ); 719 } 720 721 try 722 { 723 String absolute = location; 724 if ( !absolute.startsWith( "/" ) ) 725 { 726 absolute = "/" + absolute; 727 } 728 729 URL resource = this.getClass().getResource( absolute ); 730 if ( resource == null ) 731 { 732 try 733 { 734 resource = new URL( location ); 735 } 736 catch ( final MalformedURLException e ) 737 { 738 this.log( e, Project.MSG_DEBUG ); 739 resource = null; 740 } 741 } 742 743 if ( resource == null ) 744 { 745 final File f = this.getProject().resolveFile( location ); 746 747 if ( f.isFile() ) 748 { 749 resource = f.toURI().toURL(); 750 } 751 } 752 753 return resource; 754 } 755 catch ( final MalformedURLException e ) 756 { 757 String m = Messages.getMessage( e ); 758 m = m == null ? "" : " " + m; 759 760 throw new BuildException( Messages.getMessage( "malformedLocation", location, m ), e, this.getLocation() ); 761 } 762 } 763 764 /** 765 * Creates an array of {@code URL}s for a given resource location. 766 * <p>This method first searches the given context for resources matching {@code location}. If such resources are 767 * found, an array of URLs of those resources is returned. If no such resources are found, an attempt is made 768 * to parse the given location to an URL. On successful parsing, that URL is returned. Failing that, the given 769 * location is interpreted as a file name relative to the project's base directory. If that file is found, the URL 770 * of that file is returned. Otherwise an empty array is returned.</p> 771 * 772 * @param context The context to search for resources. 773 * @param location The resource location to create an array of {@code URL}s from. 774 * 775 * @return An array of {@code URL}s for {@code location} or an empty array if parsing {@code location} to an URL 776 * fails and {@code location} points to non-existent resources. 777 * 778 * @throws NullPointerException if {@code context} or {@code location} is {@code null}. 779 * @throws BuildException if creating an URL array fails. 780 */ 781 public URL[] getResources( final ModelContext context, final String location ) throws BuildException 782 { 783 if ( context == null ) 784 { 785 throw new NullPointerException( "context" ); 786 } 787 if ( location == null ) 788 { 789 throw new NullPointerException( "location" ); 790 } 791 792 final Set<URI> uris = new HashSet<URI>(); 793 794 try 795 { 796 for ( final Enumeration<URL> e = context.findResources( location ); e.hasMoreElements(); ) 797 { 798 uris.add( e.nextElement().toURI() ); 799 } 800 } 801 catch ( final URISyntaxException e ) 802 { 803 this.log( e, Project.MSG_DEBUG ); 804 } 805 catch ( final ModelException e ) 806 { 807 this.log( e, Project.MSG_DEBUG ); 808 } 809 810 if ( uris.isEmpty() ) 811 { 812 try 813 { 814 uris.add( new URL( location ).toURI() ); 815 } 816 catch ( final MalformedURLException e ) 817 { 818 this.log( e, Project.MSG_DEBUG ); 819 } 820 catch ( final URISyntaxException e ) 821 { 822 this.log( e, Project.MSG_DEBUG ); 823 } 824 } 825 826 if ( uris.isEmpty() ) 827 { 828 final File f = this.getProject().resolveFile( location ); 829 830 if ( f.isFile() ) 831 { 832 uris.add( f.toURI() ); 833 } 834 } 835 836 int i = 0; 837 final URL[] urls = new URL[ uris.size() ]; 838 839 for ( URI uri : uris ) 840 { 841 try 842 { 843 urls[i++] = uri.toURL(); 844 } 845 catch ( final MalformedURLException e ) 846 { 847 String m = Messages.getMessage( e ); 848 m = m == null ? "" : " " + m; 849 850 throw new BuildException( Messages.getMessage( "malformedLocation", uri.toASCIIString(), m ), e, 851 this.getLocation() ); 852 853 } 854 } 855 856 return urls; 857 } 858 859 /** 860 * Creates an {@code URL} for a given directory location. 861 * <p>This method first attempts to parse the given location to an URL. On successful parsing, that URL is returned. 862 * Failing that, the given location is interpreted as a directory name relative to the project's base directory. If 863 * that directory is found, the URL of that directory is returned. Otherwise {@code null} is returned.</p> 864 * 865 * @param location The directory location to create an {@code URL} from. 866 * 867 * @return An {@code URL} for {@code location} or {@code null}, if parsing {@code location} to an URL fails and 868 * {@code location} points to a non-existent directory. 869 * 870 * @throws NullPointerException if {@code location} is {@code null}. 871 * @throws BuildException if creating an URL fails. 872 */ 873 public URL getDirectory( final String location ) throws BuildException 874 { 875 if ( location == null ) 876 { 877 throw new NullPointerException( "location" ); 878 } 879 880 try 881 { 882 URL resource = null; 883 884 try 885 { 886 resource = new URL( location ); 887 } 888 catch ( final MalformedURLException e ) 889 { 890 this.log( e, Project.MSG_DEBUG ); 891 resource = null; 892 } 893 894 if ( resource == null ) 895 { 896 final File f = this.getProject().resolveFile( location ); 897 898 if ( f.isDirectory() ) 899 { 900 resource = f.toURI().toURL(); 901 } 902 } 903 904 return resource; 905 } 906 catch ( final MalformedURLException e ) 907 { 908 String m = Messages.getMessage( e ); 909 m = m == null ? "" : " " + m; 910 911 throw new BuildException( Messages.getMessage( "malformedLocation", location, m ), e, this.getLocation() ); 912 } 913 } 914 915 /** 916 * Creates a new {@code Transformer} for a given {@code TransformerResourceType}. 917 * 918 * @param resource The resource to create a {@code Transformer} of. 919 * 920 * @return A new {@code Transformer} for {@code resource} or {@code null}, if {@code resource} is not found and 921 * flagged optional. 922 * 923 * @throws TransformerConfigurationException if creating a new {@code Transformer} fails. 924 * 925 * @see #getTransformationParameterResources() 926 * @see #getTransformationParameters() 927 * @see #getResource(java.lang.String) 928 */ 929 public Transformer getTransformer( final TransformerResourceType resource ) throws TransformerConfigurationException 930 { 931 if ( resource == null ) 932 { 933 throw new NullPointerException( "resource" ); 934 } 935 936 InputStream in = null; 937 boolean suppressExceptionOnClose = true; 938 final URL url = this.getResource( resource.getLocation() ); 939 940 try 941 { 942 if ( url != null ) 943 { 944 final ErrorListener errorListener = new ErrorListener() 945 { 946 947 public void warning( final TransformerException exception ) throws TransformerException 948 { 949 if ( getProject() != null ) 950 { 951 getProject().log( Messages.getMessage( exception ), exception, Project.MSG_WARN ); 952 } 953 } 954 955 public void error( final TransformerException exception ) throws TransformerException 956 { 957 throw exception; 958 } 959 960 public void fatalError( final TransformerException exception ) throws TransformerException 961 { 962 throw exception; 963 } 964 965 }; 966 967 final URLConnection con = url.openConnection(); 968 con.setConnectTimeout( resource.getConnectTimeout() ); 969 con.setReadTimeout( resource.getReadTimeout() ); 970 con.connect(); 971 in = con.getInputStream(); 972 973 final TransformerFactory f = TransformerFactory.newInstance(); 974 f.setErrorListener( errorListener ); 975 final Transformer transformer = f.newTransformer( new StreamSource( in, url.toURI().toASCIIString() ) ); 976 transformer.setErrorListener( errorListener ); 977 978 for ( Map.Entry<Object, Object> e : System.getProperties().entrySet() ) 979 { 980 transformer.setParameter( e.getKey().toString(), e.getValue() ); 981 } 982 983 for ( final Iterator<Map.Entry<?, ?>> it = this.getProject().getProperties().entrySet().iterator(); 984 it.hasNext(); ) 985 { 986 final Map.Entry<?, ?> e = it.next(); 987 transformer.setParameter( e.getKey().toString(), e.getValue() ); 988 } 989 990 for ( int i = 0, s0 = this.getTransformationParameterResources().size(); i < s0; i++ ) 991 { 992 for ( Map.Entry<Object, Object> e : 993 this.getProperties( this.getTransformationParameterResources().get( i ) ).entrySet() ) 994 { 995 transformer.setParameter( e.getKey().toString(), e.getValue() ); 996 } 997 } 998 999 for ( int i = 0, s0 = this.getTransformationParameters().size(); i < s0; i++ ) 1000 { 1001 final KeyValueType p = this.getTransformationParameters().get( i ); 1002 transformer.setParameter( p.getKey(), p.getObject( this.getLocation() ) ); 1003 } 1004 1005 for ( int i = 0, s0 = this.getTransformationOutputProperties().size(); i < s0; i++ ) 1006 { 1007 final KeyValueType p = this.getTransformationOutputProperties().get( i ); 1008 transformer.setOutputProperty( p.getKey(), p.getValue() ); 1009 } 1010 1011 for ( int i = 0, s0 = resource.getTransformationParameterResources().size(); i < s0; i++ ) 1012 { 1013 for ( Map.Entry<Object, Object> e : 1014 this.getProperties( resource.getTransformationParameterResources().get( i ) ).entrySet() ) 1015 { 1016 transformer.setParameter( e.getKey().toString(), e.getValue() ); 1017 } 1018 } 1019 1020 for ( int i = 0, s0 = resource.getTransformationParameters().size(); i < s0; i++ ) 1021 { 1022 final KeyValueType p = resource.getTransformationParameters().get( i ); 1023 transformer.setParameter( p.getKey(), p.getObject( this.getLocation() ) ); 1024 } 1025 1026 for ( int i = 0, s0 = resource.getTransformationOutputProperties().size(); i < s0; i++ ) 1027 { 1028 final KeyValueType p = resource.getTransformationOutputProperties().get( i ); 1029 transformer.setOutputProperty( p.getKey(), p.getValue() ); 1030 } 1031 1032 suppressExceptionOnClose = false; 1033 return transformer; 1034 } 1035 else if ( resource.isOptional() ) 1036 { 1037 this.log( Messages.getMessage( "transformerNotFound", resource.getLocation() ), Project.MSG_WARN ); 1038 } 1039 else 1040 { 1041 throw new BuildException( Messages.getMessage( "transformerNotFound", resource.getLocation() ), 1042 this.getLocation() ); 1043 1044 } 1045 } 1046 catch ( final URISyntaxException e ) 1047 { 1048 throw new BuildException( Messages.getMessage( e ), e, this.getLocation() ); 1049 } 1050 catch ( final SocketTimeoutException e ) 1051 { 1052 final String message = Messages.getMessage( e ); 1053 1054 if ( resource.isOptional() ) 1055 { 1056 this.getProject().log( Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" ), 1057 e, Project.MSG_WARN ); 1058 1059 } 1060 else 1061 { 1062 throw new BuildException( Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" ), 1063 e, this.getLocation() ); 1064 1065 } 1066 } 1067 catch ( final IOException e ) 1068 { 1069 final String message = Messages.getMessage( e ); 1070 1071 if ( resource.isOptional() ) 1072 { 1073 this.getProject().log( Messages.getMessage( "resourceFailure", message != null ? " " + message : "" ), 1074 e, Project.MSG_WARN ); 1075 1076 } 1077 else 1078 { 1079 throw new BuildException( Messages.getMessage( "resourceFailure", message != null ? " " + message : "" ), 1080 e, this.getLocation() ); 1081 1082 } 1083 } 1084 finally 1085 { 1086 try 1087 { 1088 if ( in != null ) 1089 { 1090 in.close(); 1091 } 1092 } 1093 catch ( final IOException e ) 1094 { 1095 if ( suppressExceptionOnClose ) 1096 { 1097 this.logMessage( Level.SEVERE, Messages.getMessage( e ), e ); 1098 } 1099 else 1100 { 1101 throw new BuildException( Messages.getMessage( e ), e, this.getLocation() ); 1102 } 1103 } 1104 } 1105 1106 return null; 1107 } 1108 1109 /** 1110 * Creates a new {@code Properties} instance from a {@code PropertiesResourceType}. 1111 * 1112 * @param propertiesResourceType The {@code PropertiesResourceType} specifying the properties to create. 1113 * 1114 * @return The properties for {@code propertiesResourceType}. 1115 * 1116 * @throws NullPointerException if {@code propertiesResourceType} is {@code null}. 1117 * @throws BuildException if loading properties fails. 1118 */ 1119 public Properties getProperties( final PropertiesResourceType propertiesResourceType ) throws BuildException 1120 { 1121 if ( propertiesResourceType == null ) 1122 { 1123 throw new NullPointerException( "propertiesResourceType" ); 1124 } 1125 1126 InputStream in = null; 1127 boolean suppressExceptionOnClose = true; 1128 final Properties properties = new Properties(); 1129 final URL url = this.getResource( propertiesResourceType.getLocation() ); 1130 1131 try 1132 { 1133 if ( url != null ) 1134 { 1135 final URLConnection con = url.openConnection(); 1136 con.setConnectTimeout( propertiesResourceType.getConnectTimeout() ); 1137 con.setReadTimeout( propertiesResourceType.getReadTimeout() ); 1138 con.connect(); 1139 1140 in = con.getInputStream(); 1141 1142 if ( propertiesResourceType.getFormat() == PropertiesFormatType.PLAIN ) 1143 { 1144 properties.load( in ); 1145 } 1146 else if ( propertiesResourceType.getFormat() == PropertiesFormatType.XML ) 1147 { 1148 properties.loadFromXML( in ); 1149 } 1150 } 1151 else if ( propertiesResourceType.isOptional() ) 1152 { 1153 this.log( Messages.getMessage( "propertiesNotFound", propertiesResourceType.getLocation() ), 1154 Project.MSG_WARN ); 1155 1156 } 1157 else 1158 { 1159 throw new BuildException( Messages.getMessage( 1160 "propertiesNotFound", propertiesResourceType.getLocation() ), this.getLocation() ); 1161 1162 } 1163 1164 suppressExceptionOnClose = false; 1165 } 1166 catch ( final SocketTimeoutException e ) 1167 { 1168 final String message = Messages.getMessage( e ); 1169 1170 if ( propertiesResourceType.isOptional() ) 1171 { 1172 this.getProject().log( Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" ), 1173 e, Project.MSG_WARN ); 1174 1175 } 1176 else 1177 { 1178 throw new BuildException( Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" ), 1179 e, this.getLocation() ); 1180 1181 } 1182 } 1183 catch ( final IOException e ) 1184 { 1185 final String message = Messages.getMessage( e ); 1186 1187 if ( propertiesResourceType.isOptional() ) 1188 { 1189 this.getProject().log( Messages.getMessage( "resourceFailure", message != null ? " " + message : "" ), 1190 e, Project.MSG_WARN ); 1191 1192 } 1193 else 1194 { 1195 throw new BuildException( Messages.getMessage( "resourceFailure", message != null ? " " + message : "" ), 1196 e, this.getLocation() ); 1197 1198 } 1199 } 1200 finally 1201 { 1202 try 1203 { 1204 if ( in != null ) 1205 { 1206 in.close(); 1207 } 1208 } 1209 catch ( final IOException e ) 1210 { 1211 if ( suppressExceptionOnClose ) 1212 { 1213 this.logMessage( Level.SEVERE, Messages.getMessage( e ), e ); 1214 } 1215 else 1216 { 1217 throw new BuildException( Messages.getMessage( e ), e, this.getLocation() ); 1218 } 1219 } 1220 } 1221 1222 return properties; 1223 } 1224 1225 /** 1226 * Creates a new {@code ProjectClassLoader} instance. 1227 * 1228 * @return A new {@code ProjectClassLoader} instance. 1229 * 1230 * @throws BuildException if creating a new class loader instance fails. 1231 */ 1232 public ProjectClassLoader newProjectClassLoader() throws BuildException 1233 { 1234 try 1235 { 1236 final ProjectClassLoader classLoader = new ProjectClassLoader( this.getProject(), this.getClasspath() ); 1237 classLoader.getModletExcludes().addAll( ProjectClassLoader.getDefaultModletExcludes() ); 1238 classLoader.getProviderExcludes().addAll( ProjectClassLoader.getDefaultProviderExcludes() ); 1239 classLoader.getSchemaExcludes().addAll( ProjectClassLoader.getDefaultSchemaExcludes() ); 1240 classLoader.getServiceExcludes().addAll( ProjectClassLoader.getDefaultServiceExcludes() ); 1241 1242 if ( this.getModletLocation() != null ) 1243 { 1244 classLoader.getModletResourceLocations().add( this.getModletLocation() ); 1245 } 1246 else 1247 { 1248 classLoader.getModletResourceLocations().add( DefaultModletProvider.getDefaultModletLocation() ); 1249 } 1250 1251 if ( this.getProviderLocation() != null ) 1252 { 1253 classLoader.getProviderResourceLocations().add( 1254 this.getProviderLocation() + "/" + ModletProvider.class.getName() ); 1255 1256 } 1257 else 1258 { 1259 classLoader.getProviderResourceLocations().add( 1260 DefaultModelContext.getDefaultProviderLocation() + "/" + ModletProvider.class.getName() ); 1261 1262 } 1263 1264 return classLoader; 1265 } 1266 catch ( final IOException e ) 1267 { 1268 throw new BuildException( Messages.getMessage( e ), e, this.getLocation() ); 1269 } 1270 } 1271 1272 /** 1273 * Creates a new {@code ModelContext} instance using a given class loader. 1274 * 1275 * @param classLoader The class loader to create a new {@code ModelContext} instance with. 1276 * 1277 * @return A new {@code ModelContext} instance backed by {@code classLoader}. 1278 * 1279 * @throws ModelException if creating a new {@code ModelContext} instance fails. 1280 */ 1281 public ModelContext newModelContext( final ClassLoader classLoader ) throws ModelException 1282 { 1283 final ModelContextFactory modelContextFactory; 1284 if ( this.modelContextFactoryClassName != null ) 1285 { 1286 modelContextFactory = ModelContextFactory.newInstance( this.getModelContextFactoryClassName() ); 1287 } 1288 else 1289 { 1290 modelContextFactory = ModelContextFactory.newInstance(); 1291 } 1292 1293 final ModelContext modelContext = modelContextFactory.newModelContext( classLoader ); 1294 modelContext.setLogLevel( Level.ALL ); 1295 modelContext.setModletSchemaSystemId( this.getModletSchemaSystemId() ); 1296 1297 modelContext.getListeners().add( new ModelContext.Listener() 1298 { 1299 1300 @Override 1301 public void onLog( final Level level, final String message, final Throwable t ) 1302 { 1303 super.onLog( level, message, t ); 1304 logMessage( level, message, t ); 1305 } 1306 1307 } ); 1308 1309 if ( this.getProviderLocation() != null ) 1310 { 1311 modelContext.setAttribute( DefaultModelContext.PROVIDER_LOCATION_ATTRIBUTE_NAME, 1312 this.getProviderLocation() ); 1313 1314 } 1315 1316 if ( this.getPlatformProviderLocation() != null ) 1317 { 1318 modelContext.setAttribute( DefaultModelContext.PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME, 1319 this.getPlatformProviderLocation() ); 1320 1321 } 1322 1323 if ( this.getModletLocation() != null ) 1324 { 1325 modelContext.setAttribute( DefaultModletProvider.MODLET_LOCATION_ATTRIBUTE_NAME, this.getModletLocation() ); 1326 } 1327 1328 modelContext.setAttribute( DefaultModletProvider.VALIDATING_ATTRIBUTE_NAME, 1329 this.isModletResourceValidationEnabled() ); 1330 1331 for ( int i = 0, s0 = this.getModelContextAttributes().size(); i < s0; i++ ) 1332 { 1333 final KeyValueType kv = this.getModelContextAttributes().get( i ); 1334 final Object object = kv.getObject( this.getLocation() ); 1335 1336 if ( object != null ) 1337 { 1338 modelContext.setAttribute( kv.getKey(), object ); 1339 } 1340 else 1341 { 1342 modelContext.clearAttribute( kv.getKey() ); 1343 } 1344 } 1345 1346 return modelContext; 1347 } 1348 1349 /** 1350 * Throws a {@code BuildException} on a given {@code null} value. 1351 * 1352 * @param attributeName The name of a mandatory attribute. 1353 * @param value The value of that attribute. 1354 * 1355 * @throws NullPointerException if {@code attributeName} is {@code null}. 1356 * @throws BuildException if {@code value} is {@code null}. 1357 */ 1358 public final void assertNotNull( final String attributeName, final Object value ) throws BuildException 1359 { 1360 if ( attributeName == null ) 1361 { 1362 throw new NullPointerException( "attributeName" ); 1363 } 1364 1365 if ( value == null ) 1366 { 1367 throw new BuildException( Messages.getMessage( "mandatoryAttribute", attributeName ), this.getLocation() ); 1368 } 1369 } 1370 1371 /** 1372 * Throws a {@code BuildException} on a {@code null} value of a {@code name} property of a given {@code NameType} 1373 * collection. 1374 * 1375 * @param names The collection holding the {@code NameType} instances to test. 1376 * 1377 * @throws NullPointerException if {@code names} is {@code null}. 1378 * @throws BuildException if a {@code name} property of a given {@code NameType} from the {@code names} collection 1379 * holds a {@code null} value. 1380 */ 1381 public final void assertNamesNotNull( final Collection<? extends NameType> names ) throws BuildException 1382 { 1383 if ( names == null ) 1384 { 1385 throw new NullPointerException( "names" ); 1386 } 1387 1388 for ( NameType n : names ) 1389 { 1390 this.assertNotNull( "name", n.getName() ); 1391 } 1392 } 1393 1394 /** 1395 * Throws a {@code BuildException} on a {@code null} value of a {@code key} property of a given {@code KeyValueType} 1396 * collection. 1397 * 1398 * @param keys The collection holding the {@code KeyValueType} instances to test. 1399 * 1400 * @throws NullPointerException if {@code keys} is {@code null}. 1401 * @throws BuildException if a {@code key} property of a given {@code KeyValueType} from the {@code keys} collection 1402 * holds a {@code null} value. 1403 */ 1404 public final void assertKeysNotNull( final Collection<? extends KeyValueType> keys ) throws BuildException 1405 { 1406 if ( keys == null ) 1407 { 1408 throw new NullPointerException( "keys" ); 1409 } 1410 1411 for ( KeyValueType k : keys ) 1412 { 1413 this.assertNotNull( "key", k.getKey() ); 1414 } 1415 } 1416 1417 /** 1418 * Throws a {@code BuildException} on a {@code null} value of a {@code location} property of a given 1419 * {@code ResourceType} collection. 1420 * 1421 * @param locations The collection holding the {@code ResourceType} instances to test. 1422 * 1423 * @throws NullPointerException if {@code locations} is {@code null}. 1424 * @throws BuildException if a {@code location} property of a given {@code ResourceType} from the {@code locations} 1425 * collection holds a {@code null} value. 1426 */ 1427 public final void assertLocationsNotNull( final Collection<? extends ResourceType> locations ) 1428 throws BuildException 1429 { 1430 if ( locations == null ) 1431 { 1432 throw new NullPointerException( "locations" ); 1433 } 1434 1435 for ( ResourceType r : locations ) 1436 { 1437 assertNotNull( "location", r.getLocation() ); 1438 1439 if ( r instanceof TransformerResourceType ) 1440 { 1441 assertKeysNotNull( ( (TransformerResourceType) r ).getTransformationParameters() ); 1442 assertLocationsNotNull( ( (TransformerResourceType) r ).getTransformationParameterResources() ); 1443 assertKeysNotNull( ( (TransformerResourceType) r ).getTransformationOutputProperties() ); 1444 } 1445 } 1446 } 1447 1448 /** Logs a separator string. */ 1449 public final void logSeparator() 1450 { 1451 this.log( Messages.getMessage( "separator" ) ); 1452 } 1453 1454 /** 1455 * Logs a message at a given level. 1456 * 1457 * @param level The level to log at. 1458 * @param message The message to log. 1459 * 1460 * @throws BuildException if logging fails. 1461 */ 1462 public final void logMessage( final Level level, final String message ) throws BuildException 1463 { 1464 BufferedReader reader = null; 1465 boolean suppressExceptionOnClose = true; 1466 1467 try 1468 { 1469 String line = null; 1470 reader = new BufferedReader( new StringReader( message ) ); 1471 1472 while ( ( line = reader.readLine() ) != null ) 1473 { 1474 if ( level.intValue() >= Level.SEVERE.intValue() ) 1475 { 1476 log( line, Project.MSG_ERR ); 1477 } 1478 else if ( level.intValue() >= Level.WARNING.intValue() ) 1479 { 1480 log( line, Project.MSG_WARN ); 1481 } 1482 else if ( level.intValue() >= Level.INFO.intValue() ) 1483 { 1484 log( line, Project.MSG_INFO ); 1485 } 1486 else 1487 { 1488 log( line, Project.MSG_DEBUG ); 1489 } 1490 } 1491 1492 suppressExceptionOnClose = false; 1493 } 1494 catch ( final IOException e ) 1495 { 1496 throw new BuildException( Messages.getMessage( e ), e, this.getLocation() ); 1497 } 1498 finally 1499 { 1500 try 1501 { 1502 if ( reader != null ) 1503 { 1504 reader.close(); 1505 } 1506 } 1507 catch ( final IOException e ) 1508 { 1509 if ( suppressExceptionOnClose ) 1510 { 1511 this.log( e, Project.MSG_ERR ); 1512 } 1513 else 1514 { 1515 throw new BuildException( Messages.getMessage( e ), e, this.getLocation() ); 1516 } 1517 } 1518 } 1519 } 1520 1521 /** 1522 * Logs a message at a given level. 1523 * 1524 * @param level The level to log at. 1525 * @param message The message to log. 1526 * @param throwable The throwable to log. 1527 * 1528 * @throws BuildException if logging fails. 1529 */ 1530 public final void logMessage( final Level level, final String message, final Throwable throwable ) 1531 throws BuildException 1532 { 1533 this.logMessage( level, message ); 1534 1535 if ( level.intValue() >= Level.SEVERE.intValue() ) 1536 { 1537 log( throwable, Project.MSG_ERR ); 1538 } 1539 else if ( level.intValue() >= Level.WARNING.intValue() ) 1540 { 1541 log( throwable, Project.MSG_WARN ); 1542 } 1543 else if ( level.intValue() >= Level.INFO.intValue() ) 1544 { 1545 log( throwable, Project.MSG_INFO ); 1546 } 1547 else 1548 { 1549 log( throwable, Project.MSG_DEBUG ); 1550 } 1551 } 1552 1553 /** 1554 * Logs a validation report. 1555 * 1556 * @param context The context to use for logging the report. 1557 * @param report The report to log. 1558 * 1559 * @throws NullPointerException if {@code context} or {@code report} is {@code null}. 1560 * @throws BuildException if logging fails. 1561 */ 1562 public final void logValidationReport( final ModelContext context, final ModelValidationReport report ) 1563 { 1564 try 1565 { 1566 if ( !report.getDetails().isEmpty() ) 1567 { 1568 this.logSeparator(); 1569 Marshaller marshaller = null; 1570 1571 for ( ModelValidationReport.Detail detail : report.getDetails() ) 1572 { 1573 this.logMessage( detail.getLevel(), "o " + detail.getMessage() ); 1574 1575 if ( detail.getElement() != null ) 1576 { 1577 if ( marshaller == null ) 1578 { 1579 marshaller = context.createMarshaller( this.getModel() ); 1580 marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE ); 1581 } 1582 1583 final StringWriter stringWriter = new StringWriter(); 1584 marshaller.marshal( detail.getElement(), stringWriter ); 1585 this.logMessage( Level.FINEST, stringWriter.toString() ); 1586 } 1587 } 1588 } 1589 } 1590 catch ( final ModelException e ) 1591 { 1592 throw new BuildException( Messages.getMessage( e ), e, this.getLocation() ); 1593 } 1594 catch ( final JAXBException e ) 1595 { 1596 String message = Messages.getMessage( e ); 1597 if ( message == null && e.getLinkedException() != null ) 1598 { 1599 message = Messages.getMessage( e.getLinkedException() ); 1600 } 1601 1602 throw new BuildException( message, e, this.getLocation() ); 1603 } 1604 } 1605 1606 /** 1607 * Creates and returns a copy of this object. 1608 * 1609 * @return A copy of this object. 1610 */ 1611 @Override 1612 public JomcTask clone() 1613 { 1614 try 1615 { 1616 final JomcTask clone = (JomcTask) super.clone(); 1617 clone.classpath = (Path) ( this.classpath != null ? this.classpath.clone() : null ); 1618 1619 if ( this.modelContextAttributes != null ) 1620 { 1621 clone.modelContextAttributes = new ArrayList<KeyValueType>( this.modelContextAttributes.size() ); 1622 1623 for ( KeyValueType e : this.modelContextAttributes ) 1624 { 1625 clone.modelContextAttributes.add( e.clone() ); 1626 } 1627 } 1628 1629 if ( this.transformationParameters != null ) 1630 { 1631 clone.transformationParameters = 1632 new ArrayList<KeyValueType>( this.transformationParameters.size() ); 1633 1634 for ( KeyValueType e : this.transformationParameters ) 1635 { 1636 clone.transformationParameters.add( e.clone() ); 1637 } 1638 } 1639 1640 if ( this.transformationParameterResources != null ) 1641 { 1642 clone.transformationParameterResources = 1643 new ArrayList<PropertiesResourceType>( this.transformationParameterResources.size() ); 1644 1645 for ( PropertiesResourceType e : this.transformationParameterResources ) 1646 { 1647 clone.transformationParameterResources.add( e.clone() ); 1648 } 1649 } 1650 1651 if ( this.transformationOutputProperties != null ) 1652 { 1653 clone.transformationOutputProperties = 1654 new ArrayList<KeyValueType>( this.transformationOutputProperties.size() ); 1655 1656 for ( KeyValueType e : this.transformationOutputProperties ) 1657 { 1658 clone.transformationOutputProperties.add( e.clone() ); 1659 } 1660 } 1661 1662 return clone; 1663 } 1664 catch ( final CloneNotSupportedException e ) 1665 { 1666 throw new AssertionError( e ); 1667 } 1668 } 1669 1670}