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