001/* 002 * Copyright (C) 2009 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: AbstractModletCommand.java 5303 2016-08-30 02:31:20Z schulte $ 029 * 030 */ 031package org.jomc.cli.commands; 032 033import java.io.BufferedReader; 034import java.io.BufferedWriter; 035import java.io.Closeable; 036import java.io.File; 037import java.io.FileOutputStream; 038import java.io.FileReader; 039import java.io.IOException; 040import java.io.InputStream; 041import java.io.InputStreamReader; 042import java.io.OutputStream; 043import java.io.OutputStreamWriter; 044import java.io.StringWriter; 045import java.net.MalformedURLException; 046import java.net.URI; 047import java.net.URISyntaxException; 048import java.net.URL; 049import java.net.URLClassLoader; 050import java.util.Collections; 051import java.util.Enumeration; 052import java.util.HashSet; 053import java.util.Iterator; 054import java.util.LinkedList; 055import java.util.List; 056import java.util.Map; 057import java.util.Set; 058import java.util.logging.Level; 059import javax.xml.bind.JAXBElement; 060import javax.xml.bind.JAXBException; 061import javax.xml.bind.Marshaller; 062import javax.xml.bind.PropertyException; 063import javax.xml.transform.ErrorListener; 064import javax.xml.transform.Source; 065import javax.xml.transform.Transformer; 066import javax.xml.transform.TransformerConfigurationException; 067import javax.xml.transform.TransformerException; 068import javax.xml.transform.TransformerFactory; 069import org.apache.commons.cli.CommandLine; 070import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; 071import org.jomc.model.ModelObject; 072import org.jomc.modlet.DefaultModelContext; 073import org.jomc.modlet.DefaultModletProvider; 074import org.jomc.modlet.ModelContext; 075import org.jomc.modlet.ModelContextFactory; 076import org.jomc.modlet.ModelException; 077import org.jomc.modlet.ModelValidationReport; 078import org.jomc.modlet.Modlet; 079import org.jomc.modlet.ModletObject; 080import org.jomc.modlet.ModletProcessor; 081import org.jomc.modlet.ModletProvider; 082import org.jomc.modlet.ModletValidator; 083import org.jomc.modlet.Modlets; 084import org.jomc.modlet.ObjectFactory; 085import org.jomc.modlet.Schema; 086import org.jomc.modlet.Schemas; 087import org.jomc.modlet.Service; 088import org.jomc.modlet.ServiceFactory; 089import org.jomc.modlet.Services; 090 091/** 092 * {@code ModelContext} based command implementation. 093 * 094 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 095 */ 096public abstract class AbstractModletCommand extends AbstractCommand 097{ 098 099 /** 100 * Constant to prefix relative resource names with. 101 */ 102 private static final String ABSOLUTE_RESOURCE_NAME_PREFIX = 103 "/" + AbstractModletCommand.class.getPackage().getName().replace( '.', '/' ) + "/"; 104 105 /** 106 * Creates a new {@code AbstractModletCommand} instance. 107 */ 108 public AbstractModletCommand() 109 { 110 super(); 111 } 112 113 @Override 114 public org.apache.commons.cli.Options getOptions() 115 { 116 final org.apache.commons.cli.Options options = super.getOptions(); 117 options.addOption( Options.CLASSPATH_OPTION ); 118 options.addOption( Options.DOCUMENTS_OPTION ); 119 options.addOption( Options.MODEL_CONTEXT_FACTORY_CLASSNAME_OPTION ); 120 options.addOption( Options.MODEL_OPTION ); 121 options.addOption( Options.MODLET_SCHEMA_SYSTEM_ID_OPTION ); 122 options.addOption( Options.MODLET_LOCATION_OPTION ); 123 options.addOption( Options.PROVIDER_LOCATION_OPTION ); 124 options.addOption( Options.PLATFORM_PROVIDER_LOCATION_OPTION ); 125 options.addOption( Options.NO_MODLET_RESOURCE_VALIDATION_OPTION ); 126 return options; 127 } 128 129 /** 130 * Creates a new {@code Transformer} from a given {@code Source}. 131 * 132 * @param source The source to initialize the transformer with. 133 * 134 * @return A {@code Transformer} backed by {@code source}. 135 * 136 * @throws NullPointerException if {@code source} is {@code null}. 137 * @throws CommandExecutionException if creating a transformer fails. 138 */ 139 protected Transformer createTransformer( final Source source ) throws CommandExecutionException 140 { 141 if ( source == null ) 142 { 143 throw new NullPointerException( "source" ); 144 } 145 146 final ErrorListener errorListener = new ErrorListener() 147 { 148 149 public void warning( final TransformerException exception ) throws TransformerException 150 { 151 log( Level.WARNING, null, exception ); 152 } 153 154 public void error( final TransformerException exception ) throws TransformerException 155 { 156 throw exception; 157 } 158 159 public void fatalError( final TransformerException exception ) throws TransformerException 160 { 161 throw exception; 162 } 163 164 }; 165 166 try 167 { 168 final TransformerFactory transformerFactory = TransformerFactory.newInstance(); 169 transformerFactory.setErrorListener( errorListener ); 170 final Transformer transformer = transformerFactory.newTransformer( source ); 171 transformer.setErrorListener( errorListener ); 172 173 for ( final Map.Entry<Object, Object> e : System.getProperties().entrySet() ) 174 { 175 transformer.setParameter( e.getKey().toString(), e.getValue() ); 176 } 177 178 return transformer; 179 } 180 catch ( final TransformerConfigurationException e ) 181 { 182 throw new CommandExecutionException( Messages.getMessage( e ), e ); 183 } 184 } 185 186 /** 187 * Creates a new {@code ModelContext} for a given {@code CommandLine} and {@code ClassLoader}. 188 * 189 * @param commandLine The {@code CommandLine} to create a new {@code ModelContext} with. 190 * @param classLoader The {@code ClassLoader} to create a new {@code ModelContext} with. 191 * 192 * @return A new {@code ModelContext} for {@code classLoader} setup using {@code commandLine}. 193 * 194 * @throws NullPointerException if {@code commandLine} is {@code null}. 195 * @throws CommandExecutionException if creating an new {@code ModelContext} fails. 196 */ 197 protected ModelContext createModelContext( final CommandLine commandLine, final ClassLoader classLoader ) 198 throws CommandExecutionException 199 { 200 if ( commandLine == null ) 201 { 202 throw new NullPointerException( "commandLine" ); 203 } 204 205 final ModelContextFactory modelContextFactory = 206 commandLine.hasOption( Options.MODEL_CONTEXT_FACTORY_CLASSNAME_OPTION.getOpt() ) 207 ? ModelContextFactory.newInstance( commandLine.getOptionValue( 208 Options.MODEL_CONTEXT_FACTORY_CLASSNAME_OPTION.getOpt() ) ) 209 : ModelContextFactory.newInstance(); 210 211 final ModelContext modelContext = modelContextFactory.newModelContext( classLoader ); 212 213 modelContext.setExecutorService( this.getExecutorService( commandLine ) ); 214 215 if ( commandLine.hasOption( Options.MODLET_SCHEMA_SYSTEM_ID_OPTION.getOpt() ) ) 216 { 217 modelContext.setModletSchemaSystemId( 218 commandLine.getOptionValue( Options.MODLET_SCHEMA_SYSTEM_ID_OPTION.getOpt() ) ); 219 220 } 221 222 modelContext.setLogLevel( this.getLogLevel() ); 223 modelContext.getListeners().add( new ModelContext.Listener() 224 { 225 226 @Override 227 public void onLog( final Level level, final String message, final Throwable t ) 228 { 229 super.onLog( level, message, t ); 230 log( level, message, t ); 231 } 232 233 } ); 234 235 if ( commandLine.hasOption( Options.PROVIDER_LOCATION_OPTION.getOpt() ) ) 236 { 237 modelContext.setAttribute( DefaultModelContext.PROVIDER_LOCATION_ATTRIBUTE_NAME, 238 commandLine.getOptionValue( Options.PROVIDER_LOCATION_OPTION.getOpt() ) ); 239 240 } 241 242 if ( commandLine.hasOption( Options.PLATFORM_PROVIDER_LOCATION_OPTION.getOpt() ) ) 243 { 244 modelContext.setAttribute( 245 DefaultModelContext.PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME, 246 commandLine.getOptionValue( Options.PLATFORM_PROVIDER_LOCATION_OPTION.getOpt() ) ); 247 248 } 249 250 if ( commandLine.hasOption( Options.MODLET_LOCATION_OPTION.getOpt() ) ) 251 { 252 modelContext.setAttribute( DefaultModletProvider.MODLET_LOCATION_ATTRIBUTE_NAME, 253 commandLine.getOptionValue( Options.MODLET_LOCATION_OPTION.getOpt() ) ); 254 255 } 256 257 modelContext.setAttribute( DefaultModletProvider.VALIDATING_ATTRIBUTE_NAME, 258 !commandLine.hasOption( Options.NO_MODLET_RESOURCE_VALIDATION_OPTION.getOpt() ) ); 259 260 return modelContext; 261 } 262 263 /** 264 * Gets the identifier of the model to process. 265 * 266 * @param commandLine The command line to get the identifier of the model to process from. 267 * 268 * @return The identifier of the model to process. 269 * 270 * @throws NullPointerException if {@code commandLine} is {@code null}. 271 */ 272 protected String getModel( final CommandLine commandLine ) 273 { 274 if ( commandLine == null ) 275 { 276 throw new NullPointerException( "commandLine" ); 277 } 278 279 return commandLine.hasOption( Options.MODEL_OPTION.getOpt() ) 280 ? commandLine.getOptionValue( Options.MODEL_OPTION.getOpt() ) 281 : ModelObject.MODEL_PUBLIC_ID; 282 283 } 284 285 /** 286 * Logs a validation report. 287 * 288 * @param validationReport The report to log. 289 * @param marshaller The marshaller to use for logging the report. 290 * 291 * @throws CommandExecutionException if logging a report detail element fails. 292 */ 293 protected void log( final ModelValidationReport validationReport, final Marshaller marshaller ) 294 throws CommandExecutionException 295 { 296 Object jaxbFormattedOutput; 297 try 298 { 299 jaxbFormattedOutput = marshaller.getProperty( Marshaller.JAXB_FORMATTED_OUTPUT ); 300 marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE ); 301 } 302 catch ( final PropertyException e ) 303 { 304 this.log( Level.INFO, Messages.getMessage( e ), e ); 305 jaxbFormattedOutput = null; 306 } 307 308 try 309 { 310 for ( final ModelValidationReport.Detail d : validationReport.getDetails() ) 311 { 312 if ( this.isLoggable( d.getLevel() ) ) 313 { 314 this.log( d.getLevel(), "o " + d.getMessage(), null ); 315 316 if ( d.getElement() != null && this.getLogLevel().intValue() < Level.INFO.intValue() ) 317 { 318 final StringWriter stringWriter = new StringWriter(); 319 marshaller.marshal( d.getElement(), stringWriter ); 320 this.log( d.getLevel(), stringWriter.toString(), null ); 321 } 322 } 323 } 324 } 325 catch ( final JAXBException e ) 326 { 327 String message = Messages.getMessage( e ); 328 if ( message == null ) 329 { 330 message = Messages.getMessage( e.getLinkedException() ); 331 } 332 333 throw new CommandExecutionException( message, e ); 334 } 335 finally 336 { 337 try 338 { 339 marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, jaxbFormattedOutput ); 340 } 341 catch ( final PropertyException e ) 342 { 343 this.log( Level.INFO, Messages.getMessage( e ), e ); 344 } 345 } 346 } 347 348 /** 349 * Gets the document files specified by a given command line. 350 * 351 * @param commandLine The command line specifying the document files to get. 352 * 353 * @return The document files specified by {@code commandLine}. 354 * 355 * @throws CommandExecutionException if getting the document files fails. 356 */ 357 protected Set<File> getDocumentFiles( final CommandLine commandLine ) throws CommandExecutionException 358 { 359 try 360 { 361 final Set<File> files = new HashSet<File>( 128 ); 362 363 if ( commandLine.hasOption( Options.DOCUMENTS_OPTION.getOpt() ) ) 364 { 365 final String[] elements = commandLine.getOptionValues( Options.DOCUMENTS_OPTION.getOpt() ); 366 367 if ( elements != null ) 368 { 369 for ( final String e : elements ) 370 { 371 if ( e.startsWith( "@" ) ) 372 { 373 final File file = new File( e.substring( 1 ) ); 374 BufferedReader reader = null; 375 376 try 377 { 378 reader = new BufferedReader( new FileReader( file ) ); 379 380 for ( String line = reader.readLine(); line != null; line = reader.readLine() ) 381 { 382 line = line.trim(); 383 384 if ( !line.startsWith( "#" ) ) 385 { 386 final File f = new File( line ); 387 388 if ( f.exists() ) 389 { 390 if ( this.isLoggable( Level.FINER ) ) 391 { 392 this.log( Level.FINER, 393 Messages.getMessage( "documentFileInfo", 394 f.getAbsolutePath() ), 395 null ); 396 397 } 398 399 files.add( f ); 400 } 401 else if ( this.isLoggable( Level.WARNING ) ) 402 { 403 this.log( Level.WARNING, 404 Messages.getMessage( "documentFileNotFoundWarning", 405 f.getAbsolutePath() ), 406 null ); 407 408 } 409 } 410 } 411 412 reader.close(); 413 reader = null; 414 } 415 finally 416 { 417 try 418 { 419 if ( reader != null ) 420 { 421 reader.close(); 422 } 423 } 424 catch ( final IOException ex ) 425 { 426 this.log( Level.SEVERE, Messages.getMessage( ex ), ex ); 427 } 428 } 429 } 430 else 431 { 432 final File file = new File( e ); 433 434 if ( file.exists() ) 435 { 436 if ( this.isLoggable( Level.FINER ) ) 437 { 438 this.log( Level.FINER, 439 Messages.getMessage( "documentFileInfo", file.getAbsolutePath() ), 440 null ); 441 442 } 443 444 files.add( file ); 445 } 446 else if ( this.isLoggable( Level.WARNING ) ) 447 { 448 this.log( Level.WARNING, 449 Messages.getMessage( "documentFileNotFoundWarning", file.getAbsolutePath() ), 450 null ); 451 452 } 453 } 454 } 455 } 456 } 457 458 return files; 459 } 460 catch ( final IOException e ) 461 { 462 throw new CommandExecutionException( Messages.getMessage( e ), e ); 463 } 464 } 465 466 /** 467 * Class loader backed by a command line. 468 * 469 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 470 * @version $JOMC: AbstractModletCommand.java 5303 2016-08-30 02:31:20Z schulte $ 471 */ 472 public class CommandLineClassLoader extends URLClassLoader 473 { 474 475 /** 476 * {@code Modlets} excluded by the instance. 477 */ 478 private final Modlets excludedModlets = new Modlets(); 479 480 /** 481 * Set of provider resource locations to filter. 482 */ 483 private final Set<String> providerResourceLocations = new HashSet<String>( 128 ); 484 485 /** 486 * Set of modlet resource locations to filter. 487 */ 488 private final Set<String> modletResourceLocations = new HashSet<String>( 128 ); 489 490 /** 491 * Set of temporary resources. 492 */ 493 private final Set<File> temporaryResources = new HashSet<File>( 128 ); 494 495 /** 496 * Creates a new {@code CommandLineClassLoader} taking a command line backing the class loader. 497 * 498 * @param commandLine The command line backing the class loader. 499 * 500 * @throws NullPointerException if {@code commandLine} is {@code null}. 501 * @throws CommandExecutionException if processing {@code commandLine} fails. 502 */ 503 public CommandLineClassLoader( final CommandLine commandLine ) throws CommandExecutionException 504 { 505 super( new URL[ 0 ] ); 506 507 try 508 { 509 if ( commandLine.hasOption( Options.CLASSPATH_OPTION.getOpt() ) ) 510 { 511 final Set<URI> uris = new HashSet<URI>( 128 ); 512 final String[] elements = commandLine.getOptionValues( Options.CLASSPATH_OPTION.getOpt() ); 513 514 if ( elements != null ) 515 { 516 for ( final String e : elements ) 517 { 518 if ( e.startsWith( "@" ) ) 519 { 520 final File file = new File( e.substring( 1 ) ); 521 BufferedReader reader = null; 522 523 try 524 { 525 reader = new BufferedReader( new FileReader( file ) ); 526 527 for ( String line = reader.readLine(); line != null; line = reader.readLine() ) 528 { 529 line = line.trim(); 530 531 if ( !line.startsWith( "#" ) ) 532 { 533 final File f = new File( line ); 534 535 if ( f.exists() ) 536 { 537 uris.add( f.toURI() ); 538 } 539 else if ( isLoggable( Level.WARNING ) ) 540 { 541 log( Level.WARNING, 542 Messages.getMessage( "classpathElementNotFoundWarning", 543 f.getAbsolutePath() ), 544 null ); 545 546 } 547 } 548 } 549 550 reader.close(); 551 reader = null; 552 } 553 finally 554 { 555 try 556 { 557 if ( reader != null ) 558 { 559 reader.close(); 560 } 561 } 562 catch ( final IOException ex ) 563 { 564 log( Level.SEVERE, Messages.getMessage( ex ), ex ); 565 } 566 } 567 } 568 else 569 { 570 final File file = new File( e ); 571 572 if ( file.exists() ) 573 { 574 uris.add( file.toURI() ); 575 } 576 else if ( isLoggable( Level.WARNING ) ) 577 { 578 log( Level.WARNING, 579 Messages.getMessage( "classpathElementNotFoundWarning", 580 file.getAbsolutePath() ), 581 null ); 582 583 } 584 } 585 } 586 } 587 588 for ( final URI uri : uris ) 589 { 590 if ( isLoggable( Level.FINEST ) ) 591 { 592 log( Level.FINEST, 593 Messages.getMessage( "classpathElementInfo", uri.toASCIIString() ), 594 null ); 595 596 } 597 598 this.addURL( uri.toURL() ); 599 } 600 601 // Assumes the default modlet location matches the location of resources of the applications' 602 // dependencies. 603 this.modletResourceLocations.add( DefaultModletProvider.getDefaultModletLocation() ); 604 605 // Assumes the default provider location matches the location of resources of the applications' 606 // dependencies. 607 final String providerLocationPrefix = DefaultModelContext.getDefaultProviderLocation() + "/"; 608 this.providerResourceLocations.add( providerLocationPrefix + ModletProcessor.class.getName() ); 609 this.providerResourceLocations.add( providerLocationPrefix + ModletProvider.class.getName() ); 610 this.providerResourceLocations.add( providerLocationPrefix + ModletValidator.class.getName() ); 611 this.providerResourceLocations.add( providerLocationPrefix + ServiceFactory.class.getName() ); 612 } 613 } 614 catch ( final IOException e ) 615 { 616 throw new CommandExecutionException( Messages.getMessage( e ), e ); 617 } 618 } 619 620 /** 621 * Gets the {@code Modlets} excluded by the instance. 622 * 623 * @return The {@code Modlets} excluded by the instance. 624 */ 625 public Modlets getExcludedModlets() 626 { 627 return this.excludedModlets; 628 } 629 630 /** 631 * Finds a resource with a given name. 632 * 633 * @param name The name of the resource to search. 634 * 635 * @return An {@code URL} object for reading the resource or {@code null}, if no resource matching {@code name} is 636 * found. 637 */ 638 @Override 639 public URL findResource( final String name ) //JDK: As of JDK 23 throws IOException 640 { 641 try 642 { 643 URL resource = super.findResource( name ); 644 645 if ( resource != null ) 646 { 647 if ( this.providerResourceLocations.contains( name ) ) 648 { 649 resource = this.filterProviders( resource ); 650 } 651 else if ( this.modletResourceLocations.contains( name ) ) 652 { 653 resource = this.filterModlets( resource ); 654 } 655 } 656 657 return resource; 658 } 659 catch ( final IOException e ) 660 { 661 log( Level.SEVERE, Messages.getMessage( e ), e ); 662 return null; 663 } 664 catch ( final JAXBException e ) 665 { 666 log( Level.SEVERE, Messages.getMessage( e ), e ); 667 return null; 668 } 669 catch ( final ModelException e ) 670 { 671 log( Level.SEVERE, Messages.getMessage( e ), e ); 672 return null; 673 } 674 } 675 676 /** 677 * Finds all resources matching a given name. 678 * 679 * @param name The name of the resources to search. 680 * 681 * @return An enumeration of {@code URL} objects of resources matching name. 682 * 683 * @throws IOException if getting resources fails. 684 */ 685 @Override 686 public Enumeration<URL> findResources( final String name ) throws IOException 687 { 688 try 689 { 690 Enumeration<URL> resources = super.findResources( name ); 691 692 if ( this.providerResourceLocations.contains( name ) 693 || this.modletResourceLocations.contains( name ) ) 694 { 695 final List<URI> filtered = new LinkedList<URI>(); 696 697 while ( resources.hasMoreElements() ) 698 { 699 final URL resource = resources.nextElement(); 700 701 if ( this.providerResourceLocations.contains( name ) ) 702 { 703 filtered.add( this.filterProviders( resource ).toURI() ); 704 } 705 else if ( this.modletResourceLocations.contains( name ) ) 706 { 707 filtered.add( this.filterModlets( resource ).toURI() ); 708 } 709 } 710 711 final Iterator<URI> it = filtered.iterator(); 712 713 resources = new Enumeration<URL>() 714 { 715 716 public boolean hasMoreElements() 717 { 718 return it.hasNext(); 719 } 720 721 public URL nextElement() 722 { 723 try 724 { 725 return it.next().toURL(); 726 } 727 catch ( final MalformedURLException e ) 728 { 729 throw new AssertionError( e ); 730 } 731 } 732 733 }; 734 } 735 736 return resources; 737 } 738 catch ( final URISyntaxException e ) 739 { 740 // JDK: As of JDK 6, new IOException( message, e ); 741 throw (IOException) new IOException( Messages.getMessage( e ) ).initCause( e ); 742 } 743 catch ( final JAXBException e ) 744 { 745 String message = Messages.getMessage( e ); 746 if ( message == null && e.getLinkedException() != null ) 747 { 748 message = Messages.getMessage( e.getLinkedException() ); 749 } 750 751 // JDK: As of JDK 6, new IOException( message, e ); 752 throw (IOException) new IOException( message ).initCause( e ); 753 } 754 catch ( final ModelException e ) 755 { 756 // JDK: As of JDK 6, new IOException( message, e ); 757 throw (IOException) new IOException( Messages.getMessage( e ) ).initCause( e ); 758 } 759 } 760 761 /** 762 * Closes the class loader. 763 * 764 * @throws IOException if closing the class loader fails. 765 */ 766 @Override 767 @IgnoreJRERequirement 768 public void close() throws IOException 769 { 770 for ( final Iterator<File> it = this.temporaryResources.iterator(); it.hasNext(); ) 771 { 772 final File temporaryResource = it.next(); 773 774 if ( temporaryResource.exists() && temporaryResource.delete() ) 775 { 776 it.remove(); 777 } 778 } 779 780 if ( Closeable.class.isAssignableFrom( CommandLineClassLoader.class ) ) 781 { 782 super.close(); 783 } 784 } 785 786 /** 787 * Removes temporary resources. 788 * 789 * @throws Throwable if finalization fails. 790 */ 791 @Override 792 protected void finalize() throws Throwable 793 { 794 for ( final Iterator<File> it = this.temporaryResources.iterator(); it.hasNext(); ) 795 { 796 final File temporaryResource = it.next(); 797 798 if ( temporaryResource.exists() && !temporaryResource.delete() ) 799 { 800 temporaryResource.deleteOnExit(); 801 } 802 803 it.remove(); 804 } 805 806 super.finalize(); 807 } 808 809 private URL filterProviders( final URL resource ) throws IOException 810 { 811 InputStream in = null; 812 BufferedReader reader = null; 813 OutputStream out = null; 814 BufferedWriter writer = null; 815 final Set<String> providerExcludes = this.getProviderExcludes(); 816 final List<String> lines = new LinkedList<String>(); 817 818 try 819 { 820 URL filteredResource = resource; 821 boolean filtered = false; 822 in = resource.openStream(); 823 reader = new BufferedReader( new InputStreamReader( in, "UTF-8" ) ); 824 825 for ( String line = reader.readLine(); line != null; line = reader.readLine() ) 826 { 827 if ( !providerExcludes.contains( line.trim() ) ) 828 { 829 lines.add( line ); 830 } 831 else 832 { 833 filtered = true; 834 log( Level.FINE, 835 Messages.getMessage( "providerExclusionInfo", resource.toExternalForm(), line ), 836 null ); 837 838 } 839 } 840 841 reader.close(); 842 reader = null; 843 in = null; 844 845 if ( filtered ) 846 { 847 final File tmpResource = File.createTempFile( this.getClass().getName(), ".rsrc" ); 848 this.temporaryResources.add( tmpResource ); 849 850 out = new FileOutputStream( tmpResource ); 851 writer = new BufferedWriter( new OutputStreamWriter( out, "UTF-8" ) ); 852 853 for ( final String line : lines ) 854 { 855 writer.write( line ); 856 writer.newLine(); 857 } 858 859 writer.close(); 860 writer = null; 861 out = null; 862 863 filteredResource = tmpResource.toURI().toURL(); 864 } 865 866 return filteredResource; 867 } 868 finally 869 { 870 try 871 { 872 if ( reader != null ) 873 { 874 reader.close(); 875 } 876 } 877 catch ( final IOException e ) 878 { 879 log( Level.SEVERE, Messages.getMessage( e ), e ); 880 } 881 finally 882 { 883 try 884 { 885 if ( in != null ) 886 { 887 in.close(); 888 } 889 } 890 catch ( final IOException e ) 891 { 892 log( Level.SEVERE, Messages.getMessage( e ), e ); 893 } 894 finally 895 { 896 try 897 { 898 if ( writer != null ) 899 { 900 writer.close(); 901 } 902 } 903 catch ( final IOException e ) 904 { 905 log( Level.SEVERE, Messages.getMessage( e ), e ); 906 } 907 finally 908 { 909 try 910 { 911 if ( out != null ) 912 { 913 out.close(); 914 } 915 } 916 catch ( final IOException e ) 917 { 918 log( Level.SEVERE, Messages.getMessage( e ), e ); 919 } 920 } 921 } 922 } 923 } 924 } 925 926 private URL filterModlets( final URL resource ) throws ModelException, IOException, JAXBException 927 { 928 URL filteredResource = resource; 929 final Set<String> excludedModletNames = this.getModletExcludes(); 930 final ModelContext modelContext = ModelContextFactory.newInstance().newModelContext(); 931 Object o = modelContext.createUnmarshaller( ModletObject.MODEL_PUBLIC_ID ).unmarshal( resource ); 932 if ( o instanceof JAXBElement<?> ) 933 { 934 o = ( (JAXBElement<?>) o ).getValue(); 935 } 936 937 Modlets modlets = null; 938 boolean filtered = false; 939 940 if ( o instanceof Modlets ) 941 { 942 modlets = (Modlets) o; 943 } 944 else if ( o instanceof Modlet ) 945 { 946 modlets = new Modlets(); 947 modlets.getModlet().add( (Modlet) o ); 948 } 949 950 if ( modlets != null ) 951 { 952 for ( final Iterator<Modlet> it = modlets.getModlet().iterator(); it.hasNext(); ) 953 { 954 final Modlet m = it.next(); 955 956 if ( excludedModletNames.contains( m.getName() ) ) 957 { 958 it.remove(); 959 filtered = true; 960 synchronized ( this ) 961 { 962 this.getExcludedModlets().getModlet().add( m ); 963 } 964 log( Level.FINE, 965 Messages.getMessage( "modletExclusionInfo", resource.toExternalForm(), m.getName() ), 966 null ); 967 968 continue; 969 } 970 971 if ( this.filterModlet( m, resource.toExternalForm() ) ) 972 { 973 filtered = true; 974 } 975 } 976 977 if ( filtered ) 978 { 979 final File tmpResource = File.createTempFile( this.getClass().getName(), ".rsrc" ); 980 this.temporaryResources.add( tmpResource ); 981 modelContext.createMarshaller( ModletObject.MODEL_PUBLIC_ID ). 982 marshal( new ObjectFactory().createModlets( modlets ), tmpResource ); 983 984 filteredResource = tmpResource.toURI().toURL(); 985 } 986 } 987 988 return filteredResource; 989 } 990 991 private boolean filterModlet( final Modlet modlet, final String resourceInfo ) throws IOException 992 { 993 boolean filteredSchemas = false; 994 boolean filteredServices = false; 995 final Set<String> excludedSchemas = this.getSchemaExcludes(); 996 final Set<String> excludedServices = this.getServiceExcludes(); 997 998 if ( modlet.getSchemas() != null ) 999 { 1000 final Schemas schemas = new Schemas(); 1001 1002 for ( final Schema s : modlet.getSchemas().getSchema() ) 1003 { 1004 if ( !excludedSchemas.contains( s.getPublicId() ) ) 1005 { 1006 schemas.getSchema().add( s ); 1007 } 1008 else 1009 { 1010 log( Level.FINE, 1011 Messages.getMessage( "schemaExclusionInfo", resourceInfo, s.getContextId() ), 1012 null ); 1013 1014 filteredSchemas = true; 1015 } 1016 } 1017 1018 if ( filteredSchemas ) 1019 { 1020 modlet.setSchemas( schemas ); 1021 } 1022 } 1023 1024 if ( modlet.getServices() != null ) 1025 { 1026 final Services services = new Services(); 1027 1028 for ( final Service s : modlet.getServices().getService() ) 1029 { 1030 if ( !excludedServices.contains( s.getClazz() ) ) 1031 { 1032 services.getService().add( s ); 1033 } 1034 else 1035 { 1036 log( Level.FINE, 1037 Messages.getMessage( "serviceExclusionInfo", resourceInfo, s.getClazz() ), 1038 null ); 1039 1040 filteredServices = true; 1041 } 1042 } 1043 1044 if ( filteredServices ) 1045 { 1046 modlet.setServices( services ); 1047 } 1048 } 1049 1050 return filteredSchemas || filteredServices; 1051 } 1052 1053 /** 1054 * Gets a set of modlet names to filter. 1055 * 1056 * @return An unmodifiable set of modlet names to filter. 1057 * 1058 * @throws IOException if reading configuration resources fails. 1059 */ 1060 private Set<String> getModletExcludes() throws IOException 1061 { 1062 return this.readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultModletExcludes" ); 1063 } 1064 1065 /** 1066 * Gets a set of provider names to filter. 1067 * 1068 * @return An unmodifiable set of provider names to filter. 1069 * 1070 * @throws IOException if reading configuration resources fails. 1071 */ 1072 private Set<String> getProviderExcludes() throws IOException 1073 { 1074 return this.readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultProviderExcludes" ); 1075 } 1076 1077 /** 1078 * Gets a set of service class names to filter. 1079 * 1080 * @return An unmodifiable set of service class names to filter. 1081 * 1082 * @throws IOException if reading configuration resources fails. 1083 */ 1084 private Set<String> getServiceExcludes() throws IOException 1085 { 1086 return this.readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultServiceExcludes" ); 1087 } 1088 1089 /** 1090 * Gets a set of schema public identifiers excluded by default. 1091 * 1092 * @return An unmodifiable set of schema public identifiers excluded by default. 1093 * 1094 * @throws IOException if reading configuration resources fails. 1095 */ 1096 private Set<String> getSchemaExcludes() throws IOException 1097 { 1098 return this.readDefaultExcludes( ABSOLUTE_RESOURCE_NAME_PREFIX + "DefaultSchemaExcludes" ); 1099 } 1100 1101 private Set<String> readDefaultExcludes( final String location ) throws IOException 1102 { 1103 InputStream in = null; 1104 BufferedReader reader = null; 1105 final Set<String> defaultExcludes = new HashSet<String>(); 1106 1107 try 1108 { 1109 in = CommandLineClassLoader.class.getResourceAsStream( location ); 1110 assert in != null : "Expected resource '" + location + "' not found."; 1111 reader = new BufferedReader( new InputStreamReader( in, "UTF-8" ) ); 1112 1113 for ( String line = reader.readLine(); line != null; line = reader.readLine() ) 1114 { 1115 final String normalized = line.trim(); 1116 1117 if ( normalized.length() > 0 && !normalized.contains( "#" ) ) 1118 { 1119 defaultExcludes.add( normalized ); 1120 } 1121 } 1122 1123 reader.close(); 1124 reader = null; 1125 in = null; 1126 1127 return Collections.unmodifiableSet( defaultExcludes ); 1128 } 1129 finally 1130 { 1131 try 1132 { 1133 if ( reader != null ) 1134 { 1135 reader.close(); 1136 } 1137 } 1138 catch ( final IOException e ) 1139 { 1140 // Suppressed. 1141 } 1142 finally 1143 { 1144 try 1145 { 1146 if ( in != null ) 1147 { 1148 in.close(); 1149 } 1150 } 1151 catch ( final IOException e ) 1152 { 1153 // Suppressed. 1154 } 1155 } 1156 } 1157 } 1158 1159 } 1160 1161}