001 /* 002 * Copyright (c) 2009 The JOMC Project 003 * Copyright (c) 2005 Christian Schulte <cs@jomc.org> 004 * All rights reserved. 005 * 006 * Redistribution and use in source and binary forms, with or without 007 * modification, are permitted provided that the following conditions 008 * are met: 009 * 010 * o Redistributions of source code must retain the above copyright 011 * notice, this list of conditions and the following disclaimer. 012 * 013 * o Redistributions in binary form must reproduce the above copyright 014 * notice, this list of conditions and the following disclaimer in 015 * the documentation and/or other materials provided with the 016 * distribution. 017 * 018 * THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS" 019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 020 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 021 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR 022 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 023 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 024 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 025 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 027 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 028 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 * 030 * $Id: JavaClasses.java 992 2009-11-19 00:11:06Z schulte2005 $ 031 * 032 */ 033 package org.jomc.tools; 034 035 import java.io.ByteArrayInputStream; 036 import java.io.ByteArrayOutputStream; 037 import java.io.File; 038 import java.io.IOException; 039 import java.io.InputStream; 040 import java.net.URL; 041 import java.text.MessageFormat; 042 import java.util.List; 043 import java.util.ResourceBundle; 044 import java.util.logging.Level; 045 import java.util.zip.GZIPInputStream; 046 import java.util.zip.GZIPOutputStream; 047 import javax.xml.bind.JAXBElement; 048 import javax.xml.bind.JAXBException; 049 import javax.xml.bind.Marshaller; 050 import javax.xml.bind.Unmarshaller; 051 import javax.xml.bind.util.JAXBResult; 052 import javax.xml.bind.util.JAXBSource; 053 import javax.xml.transform.Transformer; 054 import javax.xml.transform.TransformerException; 055 import org.apache.bcel.classfile.Attribute; 056 import org.apache.bcel.classfile.ClassParser; 057 import org.apache.bcel.classfile.Constant; 058 import org.apache.bcel.classfile.ConstantPool; 059 import org.apache.bcel.classfile.ConstantUtf8; 060 import org.apache.bcel.classfile.JavaClass; 061 import org.apache.bcel.classfile.Unknown; 062 import org.jomc.model.Dependencies; 063 import org.jomc.model.Dependency; 064 import org.jomc.model.Implementation; 065 import org.jomc.model.Message; 066 import org.jomc.model.Messages; 067 import org.jomc.model.ModelObject; 068 import org.jomc.model.ModelObjectValidationReport; 069 import org.jomc.model.Module; 070 import org.jomc.model.ObjectFactory; 071 import org.jomc.model.Properties; 072 import org.jomc.model.Property; 073 import org.jomc.model.Specification; 074 import org.jomc.model.SpecificationReference; 075 import org.jomc.model.Specifications; 076 import org.jomc.util.ParseException; 077 import org.jomc.util.TokenMgrError; 078 import org.jomc.util.VersionParser; 079 080 /** 081 * Manages Java classes. 082 * 083 * <p><b>Use cases</b><br/><ul> 084 * <li>{@link #commitClasses(javax.xml.bind.Marshaller, java.io.File) }</li> 085 * <li>{@link #commitClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, java.io.File) }</li> 086 * <li>{@link #commitClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, java.io.File) }</li> 087 * <li>{@link #commitClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, java.io.File) }</li> 088 * <li>{@link #validateClasses(javax.xml.bind.Unmarshaller, java.io.File) }</li> 089 * <li>{@link #validateClasses(javax.xml.bind.Unmarshaller, java.lang.ClassLoader) }</li> 090 * <li>{@link #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.io.File) }</li> 091 * <li>{@link #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.lang.ClassLoader) }</li> 092 * <li>{@link #validateClasses(org.jomc.model.Specification, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) }</li> 093 * <li>{@link #validateClasses(org.jomc.model.Implementation, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) }</li> 094 * <li>{@link #transformClasses(javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, java.io.File, java.util.List) }</li> 095 * <li>{@link #transformClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, java.io.File, java.util.List) }</li> 096 * <li>{@link #transformClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List) }</li> 097 * <li>{@link #transformClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List) }</li> 098 * </ul></p> 099 * 100 * @author <a href="mailto:cs@jomc.org">Christian Schulte</a> 101 * @version $Id: JavaClasses.java 992 2009-11-19 00:11:06Z schulte2005 $ 102 * 103 * @see #getModules() 104 * @see org.jomc.model.ModelManager#getContext(java.lang.ClassLoader) 105 * @see org.jomc.model.ModelManager#getMarshaller(java.lang.ClassLoader) 106 * @see org.jomc.model.ModelManager#getUnmarshaller(java.lang.ClassLoader) 107 * @see ModelObjectValidationReport#isModelObjectValid() 108 */ 109 public class JavaClasses extends JomcTool 110 { 111 112 /** Creates a new {@code JavaClasses} instance. */ 113 public JavaClasses() 114 { 115 super(); 116 } 117 118 /** 119 * Creates a new {@code JavaClasses} instance taking a {@code JavaClasses} instance to initialize the instance with. 120 * 121 * @param tool The instance to initialize the new instance with, 122 */ 123 public JavaClasses( final JavaClasses tool ) 124 { 125 super( tool ); 126 } 127 128 /** 129 * Commits meta-data of the modules of the instance to compiled Java classes. 130 * 131 * @param marshaller The marshaller to use for committing the classes. 132 * @param classesDirectory The directory holding the compiled class files. 133 * 134 * @throws NullPointerException if {@code marshaller} or {@code classesDirectory} is {@code null}. 135 * @throws IOException if committing meta-data fails. 136 * 137 * @see #commitClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, java.io.File) 138 */ 139 public void commitClasses( final Marshaller marshaller, final File classesDirectory ) throws IOException 140 { 141 if ( marshaller == null ) 142 { 143 throw new NullPointerException( "marshaller" ); 144 } 145 if ( classesDirectory == null ) 146 { 147 throw new NullPointerException( "classesDirectory" ); 148 } 149 150 for ( Module m : this.getModules().getModule() ) 151 { 152 this.commitClasses( m, marshaller, classesDirectory ); 153 } 154 } 155 156 /** 157 * Commits meta-data of a given module of the modules of the instance to compiled Java classes. 158 * 159 * @param module The module to process. 160 * @param marshaller The marshaller to use for committing the classes. 161 * @param classesDirectory The directory holding the compiled class files. 162 * 163 * @throws NullPointerException if {@code module}, {@code marshaller} or {@code classesDirectory} is {@code null}. 164 * @throws IOException if committing meta-data fails. 165 * 166 * @see #commitClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, java.io.File) 167 * @see #commitClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, java.io.File) 168 */ 169 public void commitClasses( final Module module, final Marshaller marshaller, final File classesDirectory ) 170 throws IOException 171 { 172 if ( module == null ) 173 { 174 throw new NullPointerException( "module" ); 175 } 176 if ( marshaller == null ) 177 { 178 throw new NullPointerException( "marshaller" ); 179 } 180 if ( classesDirectory == null ) 181 { 182 throw new NullPointerException( "classesDirectory" ); 183 } 184 185 if ( module.getSpecifications() != null ) 186 { 187 for ( Specification s : module.getSpecifications().getSpecification() ) 188 { 189 this.commitClasses( s, marshaller, classesDirectory ); 190 } 191 } 192 if ( module.getImplementations() != null ) 193 { 194 for ( Implementation i : module.getImplementations().getImplementation() ) 195 { 196 this.commitClasses( i, marshaller, classesDirectory ); 197 } 198 } 199 } 200 201 /** 202 * Commits meta-data of a given specification of the modules of the instance to compiled Java classes. 203 * 204 * @param specification The specification to process. 205 * @param marshaller The marshaller to use for committing the classes. 206 * @param classesDirectory The directory holding the compiled class files. 207 * 208 * @throws NullPointerException if {@code specification}, {@code marshaller} or {@code classesDirectory} is 209 * {@code null}. 210 * @throws IOException if committing meta-data fails. 211 */ 212 public void commitClasses( final Specification specification, final Marshaller marshaller, 213 final File classesDirectory ) throws IOException 214 { 215 if ( specification == null ) 216 { 217 throw new NullPointerException( "specification" ); 218 } 219 if ( marshaller == null ) 220 { 221 throw new NullPointerException( "marshaller" ); 222 } 223 if ( classesDirectory == null ) 224 { 225 throw new NullPointerException( "classesDirectory" ); 226 } 227 228 if ( this.isJavaClassDeclaration( specification ) ) 229 { 230 final String classLocation = specification.getClazz().replace( '.', File.separatorChar ) + ".class"; 231 final File classFile = new File( classesDirectory, classLocation ); 232 if ( this.isLoggable( Level.INFO ) ) 233 { 234 this.log( Level.INFO, this.getMessage( "committing", new Object[] 235 { 236 classFile.getAbsolutePath() 237 } ), null ); 238 239 } 240 241 final JavaClass javaClass = this.getJavaClass( classFile ); 242 this.setClassfileAttribute( javaClass, Specification.class.getName(), this.encodeModelObject( 243 marshaller, new ObjectFactory().createSpecification( specification ) ) ); 244 245 javaClass.dump( classFile ); 246 } 247 } 248 249 /** 250 * Commits meta-data of a given implementation of the modules of the instance to compiled Java classes. 251 * 252 * @param implementation The implementation to process. 253 * @param marshaller The marshaller to use for committing the classes. 254 * @param classesDirectory The directory holding the compiled class files. 255 * 256 * @throws NullPointerException if {@code implementation}, {@code marshaller} or {@code classesDirectory} is 257 * {@code null}. 258 * @throws IOException if committing meta-data fails. 259 */ 260 public void commitClasses( final Implementation implementation, final Marshaller marshaller, 261 final File classesDirectory ) throws IOException 262 { 263 if ( implementation == null ) 264 { 265 throw new NullPointerException( "implementation" ); 266 } 267 if ( marshaller == null ) 268 { 269 throw new NullPointerException( "marshaller" ); 270 } 271 if ( classesDirectory == null ) 272 { 273 throw new NullPointerException( "classesDirectory" ); 274 } 275 276 if ( this.isJavaClassDeclaration( implementation ) ) 277 { 278 Dependencies dependencies = this.getModules().getDependencies( implementation.getIdentifier() ); 279 if ( dependencies == null ) 280 { 281 dependencies = new Dependencies(); 282 } 283 284 Properties properties = this.getModules().getProperties( implementation.getIdentifier() ); 285 if ( properties == null ) 286 { 287 properties = new Properties(); 288 } 289 290 Messages messages = this.getModules().getMessages( implementation.getIdentifier() ); 291 if ( messages == null ) 292 { 293 messages = new Messages(); 294 } 295 296 Specifications specifications = this.getModules().getSpecifications( implementation.getIdentifier() ); 297 if ( specifications == null ) 298 { 299 specifications = new Specifications(); 300 } 301 302 for ( SpecificationReference r : specifications.getReference() ) 303 { 304 if ( specifications.getSpecification( r.getIdentifier() ) == null && this.isLoggable( Level.WARNING ) ) 305 { 306 this.log( Level.WARNING, this.getMessage( "unresolvedSpecification", new Object[] 307 { 308 r.getIdentifier(), implementation.getIdentifier() 309 } ), null ); 310 311 } 312 } 313 314 for ( Dependency d : dependencies.getDependency() ) 315 { 316 final Specification s = this.getModules().getSpecification( d.getIdentifier() ); 317 318 if ( s != null ) 319 { 320 if ( specifications.getSpecification( s.getIdentifier() ) == null ) 321 { 322 specifications.getSpecification().add( s ); 323 } 324 } 325 else if ( this.isLoggable( Level.WARNING ) ) 326 { 327 this.log( Level.WARNING, this.getMessage( "unresolvedDependencySpecification", new Object[] 328 { 329 d.getIdentifier(), d.getName(), implementation.getIdentifier() 330 } ), null ); 331 332 } 333 } 334 335 final String classLocation = implementation.getClazz().replace( '.', File.separatorChar ) + ".class"; 336 final File classFile = new File( classesDirectory, classLocation ); 337 338 if ( this.isLoggable( Level.INFO ) ) 339 { 340 this.log( Level.INFO, this.getMessage( "committing", new Object[] 341 { 342 classFile.getAbsolutePath() 343 } ), null ); 344 345 } 346 347 final JavaClass javaClass = this.getJavaClass( classFile ); 348 final ObjectFactory of = new ObjectFactory(); 349 350 this.setClassfileAttribute( javaClass, Dependencies.class.getName(), this.encodeModelObject( 351 marshaller, of.createDependencies( dependencies ) ) ); 352 353 this.setClassfileAttribute( javaClass, Properties.class.getName(), this.encodeModelObject( 354 marshaller, of.createProperties( properties ) ) ); 355 356 this.setClassfileAttribute( javaClass, Messages.class.getName(), this.encodeModelObject( 357 marshaller, of.createMessages( messages ) ) ); 358 359 this.setClassfileAttribute( javaClass, Specifications.class.getName(), this.encodeModelObject( 360 marshaller, of.createSpecifications( specifications ) ) ); 361 362 javaClass.dump( classFile ); 363 } 364 } 365 366 /** 367 * Validates compiled Java classes against the modules of the instance. 368 * 369 * @param unmarshaller The unmarshaller to use for validating classes. 370 * @param classesDirectory The directory holding the compiled class files. 371 * 372 * @return The report of the validation. 373 * 374 * @throws NullPointerException if {@code unmarshaller} or {@code classesDirectory} is {@code null}. 375 * @throws IOException if reading class files fails. 376 * 377 * @see #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.io.File) 378 */ 379 public ModelObjectValidationReport validateClasses( final Unmarshaller unmarshaller, final File classesDirectory ) 380 throws IOException 381 { 382 if ( unmarshaller == null ) 383 { 384 throw new NullPointerException( "unmarshaller" ); 385 } 386 if ( classesDirectory == null ) 387 { 388 throw new NullPointerException( "classesDirectory" ); 389 } 390 391 final ModelObjectValidationReport report = new ModelObjectValidationReport( 392 new ObjectFactory().createModules( this.getModules() ) ); 393 394 for ( Module m : this.getModules().getModule() ) 395 { 396 final ModelObjectValidationReport current = this.validateClasses( m, unmarshaller, classesDirectory ); 397 report.getDetails().addAll( current.getDetails() ); 398 } 399 400 return report; 401 } 402 403 /** 404 * Validates compiled Java classes against the modules of the instance. 405 * 406 * @param unmarshaller The unmarshaller to use for validating classes. 407 * @param classLoader The class loader to search for classes. 408 * 409 * @return The report of the validation. 410 * 411 * @throws NullPointerException if {@code unmarshaller} or {@code classLoader} is {@code null}. 412 * @throws IOException if reading class files fails. 413 * 414 * @see #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.lang.ClassLoader) 415 */ 416 public ModelObjectValidationReport validateClasses( final Unmarshaller unmarshaller, final ClassLoader classLoader ) 417 throws IOException 418 { 419 if ( unmarshaller == null ) 420 { 421 throw new NullPointerException( "unmarshaller" ); 422 } 423 if ( classLoader == null ) 424 { 425 throw new NullPointerException( "classLoader" ); 426 } 427 428 final ModelObjectValidationReport report = new ModelObjectValidationReport( 429 new ObjectFactory().createModules( this.getModules() ) ); 430 431 for ( Module m : this.getModules().getModule() ) 432 { 433 final ModelObjectValidationReport current = this.validateClasses( m, unmarshaller, classLoader ); 434 report.getDetails().addAll( current.getDetails() ); 435 } 436 437 return report; 438 } 439 440 /** 441 * Validates compiled Java classes against a given module of the modules of the instance. 442 * 443 * @param module The module to process. 444 * @param unmarshaller The unmarshaller to use for validating classes. 445 * @param classesDirectory The directory holding the compiled class files. 446 * 447 * @return The report of the validation. 448 * 449 * @throws NullPointerException if {@code module}, {@code unmarshaller} or {@code classesDirectory} is {@code null}. 450 * @throws IOException if reading class files fails. 451 * 452 * @see #validateClasses(org.jomc.model.Specification, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) 453 * @see #validateClasses(org.jomc.model.Implementation, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) 454 */ 455 public ModelObjectValidationReport validateClasses( final Module module, final Unmarshaller unmarshaller, 456 final File classesDirectory ) throws IOException 457 { 458 if ( module == null ) 459 { 460 throw new NullPointerException( "module" ); 461 } 462 if ( unmarshaller == null ) 463 { 464 throw new NullPointerException( "unmarshaller" ); 465 } 466 if ( classesDirectory == null ) 467 { 468 throw new NullPointerException( "classesDirectory" ); 469 } 470 471 final ModelObjectValidationReport report = new ModelObjectValidationReport( 472 new ObjectFactory().createModule( module ) ); 473 474 if ( module.getSpecifications() != null ) 475 { 476 for ( Specification s : module.getSpecifications().getSpecification() ) 477 { 478 if ( this.isJavaClassDeclaration( s ) ) 479 { 480 final String classLocation = s.getClazz().replace( '.', File.separatorChar ) + ".class"; 481 final File classFile = new File( classesDirectory, classLocation ); 482 final ModelObjectValidationReport current = 483 this.validateClasses( s, unmarshaller, this.getJavaClass( classFile ) ); 484 485 report.getDetails().addAll( current.getDetails() ); 486 } 487 } 488 } 489 490 if ( module.getImplementations() != null ) 491 { 492 for ( Implementation i : module.getImplementations().getImplementation() ) 493 { 494 if ( this.isJavaClassDeclaration( i ) ) 495 { 496 final String classLocation = i.getClazz().replace( '.', File.separatorChar ) + ".class"; 497 final File classFile = new File( classesDirectory, classLocation ); 498 final JavaClass javaClass = this.getJavaClass( classFile ); 499 final ModelObjectValidationReport current = 500 this.validateClasses( i, unmarshaller, javaClass ); 501 502 report.getDetails().addAll( current.getDetails() ); 503 } 504 } 505 } 506 507 return report; 508 } 509 510 /** 511 * Validates compiled Java classes against a given module of the modules of the instance. 512 * 513 * @param module The module to process. 514 * @param unmarshaller The unmarshaller to use for validating classes. 515 * @param classLoader The class loader to search for classes. 516 * 517 * @return The report of the validation. 518 * 519 * @throws NullPointerException if {@code module}, {@code unmarshaller} or {@code classLoader} is {@code null}. 520 * @throws IOException if reading class files fails. 521 * 522 * @see #validateClasses(org.jomc.model.Specification, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) 523 * @see #validateClasses(org.jomc.model.Implementation, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) 524 */ 525 public ModelObjectValidationReport validateClasses( final Module module, final Unmarshaller unmarshaller, 526 final ClassLoader classLoader ) throws IOException 527 { 528 if ( module == null ) 529 { 530 throw new NullPointerException( "module" ); 531 } 532 if ( unmarshaller == null ) 533 { 534 throw new NullPointerException( "unmarshaller" ); 535 } 536 if ( classLoader == null ) 537 { 538 throw new NullPointerException( "classLoader" ); 539 } 540 541 final ModelObjectValidationReport report = new ModelObjectValidationReport( 542 new ObjectFactory().createModule( module ) ); 543 544 if ( module.getSpecifications() != null ) 545 { 546 for ( Specification s : module.getSpecifications().getSpecification() ) 547 { 548 if ( this.isJavaClassDeclaration( s ) ) 549 { 550 final String classLocation = s.getClazz().replace( '.', '/' ) + ".class"; 551 final URL classUrl = classLoader.getResource( classLocation ); 552 553 if ( classUrl == null ) 554 { 555 throw new IOException( this.getMessage( "resourceNotFound", new Object[] 556 { 557 classLocation 558 } ) ); 559 560 } 561 562 final JavaClass javaClass = this.getJavaClass( classUrl, classLocation ); 563 final ModelObjectValidationReport current = 564 this.validateClasses( s, unmarshaller, javaClass ); 565 566 report.getDetails().addAll( current.getDetails() ); 567 } 568 } 569 } 570 571 if ( module.getImplementations() != null ) 572 { 573 for ( Implementation i : module.getImplementations().getImplementation() ) 574 { 575 if ( this.isJavaClassDeclaration( i ) ) 576 { 577 final String classLocation = i.getClazz().replace( '.', '/' ) + ".class"; 578 final URL classUrl = classLoader.getResource( classLocation ); 579 580 if ( classUrl == null ) 581 { 582 throw new IOException( this.getMessage( "resourceNotFound", new Object[] 583 { 584 classLocation 585 } ) ); 586 587 } 588 589 final JavaClass javaClass = this.getJavaClass( classUrl, classLocation ); 590 final ModelObjectValidationReport current = 591 this.validateClasses( i, unmarshaller, javaClass ); 592 593 report.getDetails().addAll( current.getDetails() ); 594 } 595 } 596 } 597 598 return report; 599 } 600 601 /** 602 * Validates compiled Java classes against a given specification of the modules of the instance. 603 * 604 * @param specification The specification to process. 605 * @param unmarshaller The unmarshaller to use for validating classes. 606 * @param javaClass The class to validate. 607 * 608 * @return The report of the validation. 609 * 610 * @throws NullPointerException if {@code specification}, {@code unmarshaller} or {@code javaClass} is {@code null}. 611 * @throws IOException if reading class files fails. 612 */ 613 public ModelObjectValidationReport validateClasses( final Specification specification, 614 final Unmarshaller unmarshaller, final JavaClass javaClass ) 615 throws IOException 616 { 617 if ( specification == null ) 618 { 619 throw new NullPointerException( "specification" ); 620 } 621 if ( unmarshaller == null ) 622 { 623 throw new NullPointerException( "unmarshaller" ); 624 } 625 if ( javaClass == null ) 626 { 627 throw new NullPointerException( "javaClass" ); 628 } 629 630 if ( this.isLoggable( Level.INFO ) ) 631 { 632 this.log( Level.INFO, this.getMessage( "validatingSpecification", new Object[] 633 { 634 specification.getIdentifier() 635 } ), null ); 636 637 } 638 639 final ModelObjectValidationReport report = new ModelObjectValidationReport( 640 new ObjectFactory().createSpecification( specification ) ); 641 642 Specification decoded = null; 643 final byte[] bytes = this.getClassfileAttribute( javaClass, Specification.class.getName() ); 644 if ( bytes != null ) 645 { 646 decoded = this.decodeModelObject( unmarshaller, bytes, Specification.class ); 647 } 648 649 if ( decoded != null ) 650 { 651 if ( decoded.getMultiplicity() != specification.getMultiplicity() ) 652 { 653 report.getDetails().add( new ModelObjectValidationReport.Detail( 654 "CLASS_ILLEGAL_SPECIFICATION_MULTIPLICITY", Level.SEVERE, 655 this.getMessage( "illegalMultiplicity", new Object[] 656 { 657 specification.getIdentifier(), specification.getMultiplicity().value(), 658 decoded.getMultiplicity().value() 659 } ) ) ); 660 661 } 662 663 if ( decoded.getScope() == null 664 ? specification.getScope() != null 665 : !decoded.getScope().equals( specification.getScope() ) ) 666 { 667 report.getDetails().add( new ModelObjectValidationReport.Detail( 668 "CLASS_ILLEGAL_SPECIFICATION_SCOPE", Level.SEVERE, 669 this.getMessage( "illegalScope", new Object[] 670 { 671 specification.getIdentifier(), 672 specification.getScope() == null ? "Multiton" : specification.getScope(), 673 decoded.getScope() == null ? "Multiton" : decoded.getScope() 674 } ) ) ); 675 676 } 677 678 if ( decoded.getClazz() == null 679 ? specification.getClazz() != null 680 : !decoded.getClazz().equals( specification.getClazz() ) ) 681 { 682 report.getDetails().add( new ModelObjectValidationReport.Detail( 683 "CLASS_ILLEGAL_SPECIFICATION_CLASS", Level.SEVERE, 684 this.getMessage( "illegalSpecificationClass", new Object[] 685 { 686 decoded.getIdentifier(), specification.getClazz(), decoded.getClazz() 687 } ) ) ); 688 689 } 690 } 691 else if ( this.isLoggable( Level.WARNING ) ) 692 { 693 this.log( Level.WARNING, this.getMessage( "cannotValidateSpecification", new Object[] 694 { 695 specification.getIdentifier(), Specification.class.getName() 696 } ), null ); 697 698 } 699 700 return report; 701 } 702 703 /** 704 * Validates compiled Java classes against a given implementation of the modules of the instance. 705 * 706 * @param implementation The implementation to process. 707 * @param unmarshaller The unmarshaller to use for validating classes. 708 * @param javaClass The class to validate. 709 * 710 * @return The report of the validation. 711 * 712 * @throws NullPointerException if {@code implementation}, {@code unmarshaller} or {@code javaClass} is 713 * {@code null}. 714 * @throws IOException if reading class files fails. 715 */ 716 public ModelObjectValidationReport validateClasses( final Implementation implementation, 717 final Unmarshaller unmarshaller, final JavaClass javaClass ) 718 throws IOException 719 { 720 if ( implementation == null ) 721 { 722 throw new NullPointerException( "implementation" ); 723 } 724 if ( unmarshaller == null ) 725 { 726 throw new NullPointerException( "unmarshaller" ); 727 } 728 if ( javaClass == null ) 729 { 730 throw new NullPointerException( "javaClass" ); 731 } 732 733 if ( this.isLoggable( Level.INFO ) ) 734 { 735 this.log( Level.INFO, this.getMessage( "validatingImplementation", new Object[] 736 { 737 implementation.getIdentifier() 738 } ), null ); 739 740 } 741 742 final ModelObjectValidationReport report = new ModelObjectValidationReport( 743 new ObjectFactory().createImplementation( implementation ) ); 744 745 try 746 { 747 Dependencies dependencies = this.getModules().getDependencies( implementation.getIdentifier() ); 748 if ( dependencies == null ) 749 { 750 dependencies = new Dependencies(); 751 } 752 753 Properties properties = this.getModules().getProperties( implementation.getIdentifier() ); 754 if ( properties == null ) 755 { 756 properties = new Properties(); 757 } 758 759 Messages messages = this.getModules().getMessages( implementation.getIdentifier() ); 760 if ( messages == null ) 761 { 762 messages = new Messages(); 763 } 764 765 Specifications specifications = this.getModules().getSpecifications( implementation.getIdentifier() ); 766 if ( specifications == null ) 767 { 768 specifications = new Specifications(); 769 } 770 771 Dependencies decodedDependencies = null; 772 byte[] bytes = this.getClassfileAttribute( javaClass, Dependencies.class.getName() ); 773 if ( bytes != null ) 774 { 775 decodedDependencies = this.decodeModelObject( unmarshaller, bytes, Dependencies.class ); 776 } 777 778 Properties decodedProperties = null; 779 bytes = this.getClassfileAttribute( javaClass, Properties.class.getName() ); 780 if ( bytes != null ) 781 { 782 decodedProperties = this.decodeModelObject( unmarshaller, bytes, Properties.class ); 783 } 784 785 Messages decodedMessages = null; 786 bytes = this.getClassfileAttribute( javaClass, Messages.class.getName() ); 787 if ( bytes != null ) 788 { 789 decodedMessages = this.decodeModelObject( unmarshaller, bytes, Messages.class ); 790 } 791 792 Specifications decodedSpecifications = null; 793 bytes = this.getClassfileAttribute( javaClass, Specifications.class.getName() ); 794 if ( bytes != null ) 795 { 796 decodedSpecifications = this.decodeModelObject( unmarshaller, bytes, Specifications.class ); 797 } 798 799 if ( decodedDependencies != null ) 800 { 801 for ( Dependency decodedDependency : decodedDependencies.getDependency() ) 802 { 803 final Dependency dependency = dependencies.getDependency( decodedDependency.getName() ); 804 805 if ( dependency == null ) 806 { 807 report.getDetails().add( new ModelObjectValidationReport.Detail( 808 "CLASS_MISSING_IMPLEMENTATION_DEPENDENCY", Level.SEVERE, 809 this.getMessage( "missingDependency", new Object[] 810 { 811 implementation.getIdentifier(), decodedDependency.getName() 812 } ) ) ); 813 814 } 815 816 final Specification s = this.getModules().getSpecification( decodedDependency.getIdentifier() ); 817 818 if ( s != null && s.getVersion() != null && decodedDependency.getVersion() != null && 819 VersionParser.compare( decodedDependency.getVersion(), s.getVersion() ) > 0 ) 820 { 821 final Module moduleOfSpecification = 822 this.getModules().getModuleOfSpecification( s.getIdentifier() ); 823 824 final Module moduleOfImplementation = 825 this.getModules().getModuleOfImplementation( implementation.getIdentifier() ); 826 827 report.getDetails().add( new ModelObjectValidationReport.Detail( 828 "CLASS_INCOMPATIBLE_IMPLEMENTATION_DEPENDENCY", Level.SEVERE, 829 this.getMessage( "incompatibleDependency", new Object[] 830 { 831 javaClass.getClassName(), moduleOfImplementation == null 832 ? "<>" : moduleOfImplementation.getName(), 833 s.getIdentifier(), moduleOfSpecification == null 834 ? "<>" : moduleOfSpecification.getName(), 835 decodedDependency.getVersion(), s.getVersion() 836 } ) ) ); 837 838 } 839 } 840 } 841 else if ( this.isLoggable( Level.WARNING ) ) 842 { 843 this.log( Level.WARNING, this.getMessage( "cannotValidateImplementation", new Object[] 844 { 845 implementation.getIdentifier(), Dependencies.class.getName() 846 } ), null ); 847 848 } 849 850 if ( decodedProperties != null ) 851 { 852 for ( Property decodedProperty : decodedProperties.getProperty() ) 853 { 854 final Property property = properties.getProperty( decodedProperty.getName() ); 855 856 if ( property == null ) 857 { 858 report.getDetails().add( new ModelObjectValidationReport.Detail( 859 "CLASS_MISSING_IMPLEMENTATION_PROPERTY", Level.SEVERE, 860 this.getMessage( "missingProperty", new Object[] 861 { 862 implementation.getIdentifier(), decodedProperty.getName() 863 } ) ) ); 864 865 } 866 else 867 { 868 if ( decodedProperty.getType() == null 869 ? property.getType() != null 870 : !decodedProperty.getType().equals( property.getType() ) ) 871 { 872 report.getDetails().add( new ModelObjectValidationReport.Detail( 873 "CLASS_ILLEGAL_IMPLEMENTATION_PROPERTY", Level.SEVERE, 874 this.getMessage( "illegalPropertyType", new Object[] 875 { 876 implementation.getIdentifier(), decodedProperty.getName(), 877 property.getType() == null ? "default" : property.getType(), 878 decodedProperty.getType() == null ? "default" : decodedProperty.getType() 879 } ) ) ); 880 881 } 882 } 883 } 884 } 885 else if ( this.isLoggable( Level.WARNING ) ) 886 { 887 this.log( Level.WARNING, this.getMessage( "cannotValidateImplementation", new Object[] 888 { 889 implementation.getIdentifier(), Properties.class.getName() 890 } ), null ); 891 892 } 893 894 if ( decodedMessages != null ) 895 { 896 for ( Message decodedMessage : decodedMessages.getMessage() ) 897 { 898 final Message message = messages.getMessage( decodedMessage.getName() ); 899 900 if ( message == null ) 901 { 902 report.getDetails().add( new ModelObjectValidationReport.Detail( 903 "CLASS_MISSING_IMPLEMENTATION_MESSAGE", Level.SEVERE, 904 this.getMessage( "missingMessage", new Object[] 905 { 906 implementation.getIdentifier(), decodedMessage.getName() 907 } ) ) ); 908 909 } 910 } 911 } 912 else if ( this.isLoggable( Level.WARNING ) ) 913 { 914 this.log( Level.WARNING, this.getMessage( "cannotValidateImplementation", new Object[] 915 { 916 implementation.getIdentifier(), Messages.class.getName() 917 } ), null ); 918 919 } 920 921 if ( decodedSpecifications != null ) 922 { 923 for ( Specification decodedSpecification : decodedSpecifications.getSpecification() ) 924 { 925 final Specification specification = 926 this.getModules().getSpecification( decodedSpecification.getIdentifier() ); 927 928 if ( specification == null ) 929 { 930 report.getDetails().add( new ModelObjectValidationReport.Detail( 931 "CLASS_MISSING_SPECIFICATION", Level.SEVERE, 932 this.getMessage( "missingSpecification", new Object[] 933 { 934 implementation.getIdentifier(), decodedSpecification.getIdentifier() 935 } ) ) ); 936 937 } 938 else 939 { 940 if ( decodedSpecification.getMultiplicity() != specification.getMultiplicity() ) 941 { 942 report.getDetails().add( new ModelObjectValidationReport.Detail( 943 "CLASS_ILLEGAL_SPECIFICATION_MULTIPLICITY", Level.SEVERE, 944 this.getMessage( "illegalMultiplicity", new Object[] 945 { 946 specification.getIdentifier(), specification.getMultiplicity().value(), 947 decodedSpecification.getMultiplicity().value() 948 } ) ) ); 949 950 } 951 952 if ( decodedSpecification.getScope() == null 953 ? specification.getScope() != null 954 : !decodedSpecification.getScope().equals( specification.getScope() ) ) 955 { 956 report.getDetails().add( new ModelObjectValidationReport.Detail( 957 "CLASS_ILLEGAL_SPECIFICATION_SCOPE", Level.SEVERE, 958 this.getMessage( "illegalScope", new Object[] 959 { 960 decodedSpecification.getIdentifier(), 961 specification.getScope() == null 962 ? "Multiton" : specification.getScope(), 963 decodedSpecification.getScope() == null 964 ? "Multiton" : decodedSpecification.getScope() 965 } ) ) ); 966 967 } 968 969 if ( decodedSpecification.getClazz() == null 970 ? specification.getClazz() != null 971 : !decodedSpecification.getClazz().equals( specification.getClazz() ) ) 972 { 973 report.getDetails().add( new ModelObjectValidationReport.Detail( 974 "CLASS_ILLEGAL_SPECIFICATION_CLASS", Level.SEVERE, 975 this.getMessage( "illegalSpecificationClass", new Object[] 976 { 977 decodedSpecification.getIdentifier(), specification.getClazz(), 978 decodedSpecification.getClazz() 979 } ) ) ); 980 981 } 982 } 983 } 984 985 for ( SpecificationReference decodedReference : decodedSpecifications.getReference() ) 986 { 987 final Specification specification = 988 specifications.getSpecification( decodedReference.getIdentifier() ); 989 990 if ( specification == null ) 991 { 992 report.getDetails().add( new ModelObjectValidationReport.Detail( 993 "CLASS_MISSING_SPECIFICATION", Level.SEVERE, 994 this.getMessage( "missingSpecification", new Object[] 995 { 996 implementation.getIdentifier(), decodedReference.getIdentifier() 997 } ) ) ); 998 999 } 1000 else if ( decodedReference.getVersion() != null && specification.getVersion() != null && 1001 VersionParser.compare( decodedReference.getVersion(), specification.getVersion() ) != 0 ) 1002 { 1003 final Module moduleOfSpecification = 1004 this.getModules().getModuleOfSpecification( decodedReference.getIdentifier() ); 1005 1006 final Module moduleOfImplementation = 1007 this.getModules().getModuleOfImplementation( implementation.getIdentifier() ); 1008 1009 report.getDetails().add( new ModelObjectValidationReport.Detail( 1010 "CLASS_INCOMPATIBLE_IMPLEMENTATION", Level.SEVERE, 1011 this.getMessage( "incompatibleImplementation", new Object[] 1012 { 1013 javaClass.getClassName(), moduleOfImplementation == null 1014 ? "<>" : moduleOfImplementation.getName(), 1015 specification.getIdentifier(), moduleOfSpecification == null 1016 ? "<>" : moduleOfSpecification.getName(), 1017 decodedReference.getVersion(), specification.getVersion() 1018 } ) ) ); 1019 1020 } 1021 } 1022 } 1023 else if ( this.isLoggable( Level.WARNING ) ) 1024 { 1025 this.log( Level.WARNING, this.getMessage( "cannotValidateImplementation", new Object[] 1026 { 1027 implementation.getIdentifier(), Specifications.class.getName() 1028 } ), null ); 1029 1030 } 1031 1032 return report; 1033 } 1034 catch ( final ParseException e ) 1035 { 1036 throw (IOException) new IOException( e.getMessage() ).initCause( e ); 1037 } 1038 catch ( final TokenMgrError e ) 1039 { 1040 throw (IOException) new IOException( e.getMessage() ).initCause( e ); 1041 } 1042 } 1043 1044 /** 1045 * Transforms committed meta-data of compiled Java classes of the modules of the instance. 1046 * 1047 * @param marshaller The marshaller to use for transforming classes. 1048 * @param unmarshaller The unmarshaller to use for transforming classes. 1049 * @param classesDirectory The directory holding the compiled class files. 1050 * @param transformers The transformers to use for transforming the classes. 1051 * 1052 * @throws NullPointerException if {@code marshaller}, {@code unmarshaller}, {@code classesDirectory} or 1053 * {@code transformers} is {@code null}. 1054 * @throws IOException if accessing class files fails. 1055 * @throws TransformerException if transforming class files fails. 1056 * 1057 * @see #transformClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, java.io.File, java.util.List) 1058 */ 1059 public void transformClasses( final Marshaller marshaller, final Unmarshaller unmarshaller, 1060 final File classesDirectory, final List<Transformer> transformers ) 1061 throws IOException, TransformerException 1062 { 1063 if ( marshaller == null ) 1064 { 1065 throw new NullPointerException( "marshaller" ); 1066 } 1067 if ( unmarshaller == null ) 1068 { 1069 throw new NullPointerException( "unmarshaller" ); 1070 } 1071 if ( transformers == null ) 1072 { 1073 throw new NullPointerException( "transformers" ); 1074 } 1075 if ( classesDirectory == null ) 1076 { 1077 throw new NullPointerException( "classesDirectory" ); 1078 } 1079 1080 for ( Module m : this.getModules().getModule() ) 1081 { 1082 this.transformClasses( m, marshaller, unmarshaller, classesDirectory, transformers ); 1083 } 1084 } 1085 1086 /** 1087 * Transforms committed meta-data of compiled Java classes of a given module of the modules of the instance. 1088 * 1089 * @param module The module to process. 1090 * @param marshaller The marshaller to use for transforming classes. 1091 * @param unmarshaller The unmarshaller to use for transforming classes. 1092 * @param classesDirectory The directory holding the compiled class files. 1093 * @param transformers The transformers to use for transforming the classes. 1094 * 1095 * @throws NullPointerException if {@code module}, {@code marshaller}, {@code unmarshaller}, 1096 * {@code classesDirectory} or {@code transformers} is {@code null}. 1097 * @throws IOException if accessing class files fails. 1098 * @throws TransformerException if transforming class files fails. 1099 * 1100 * @see #transformClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List) 1101 * @see #transformClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List) 1102 */ 1103 public void transformClasses( final Module module, final Marshaller marshaller, final Unmarshaller unmarshaller, 1104 final File classesDirectory, final List<Transformer> transformers ) 1105 throws IOException, TransformerException 1106 { 1107 if ( module == null ) 1108 { 1109 throw new NullPointerException( "module" ); 1110 } 1111 if ( marshaller == null ) 1112 { 1113 throw new NullPointerException( "marshaller" ); 1114 } 1115 if ( unmarshaller == null ) 1116 { 1117 throw new NullPointerException( "unmarshaller" ); 1118 } 1119 if ( transformers == null ) 1120 { 1121 throw new NullPointerException( "transformers" ); 1122 } 1123 if ( classesDirectory == null ) 1124 { 1125 throw new NullPointerException( "classesDirectory" ); 1126 } 1127 1128 if ( module.getSpecifications() != null ) 1129 { 1130 for ( Specification s : module.getSpecifications().getSpecification() ) 1131 { 1132 if ( this.isJavaClassDeclaration( s ) ) 1133 { 1134 final String classLocation = s.getIdentifier().replace( '.', File.separatorChar ) + ".class"; 1135 final File classFile = new File( classesDirectory, classLocation ); 1136 1137 if ( this.isLoggable( Level.INFO ) ) 1138 { 1139 this.log( Level.INFO, this.getMessage( "transforming", new Object[] 1140 { 1141 classFile.getAbsolutePath() 1142 } ), null ); 1143 1144 } 1145 1146 final JavaClass javaClass = this.getJavaClass( classFile ); 1147 this.transformClasses( s, marshaller, unmarshaller, javaClass, transformers ); 1148 javaClass.dump( classFile ); 1149 } 1150 } 1151 } 1152 1153 if ( module.getImplementations() != null ) 1154 { 1155 for ( Implementation i : module.getImplementations().getImplementation() ) 1156 { 1157 if ( this.isJavaClassDeclaration( i ) ) 1158 { 1159 final String classLocation = i.getClazz().replace( '.', File.separatorChar ) + ".class"; 1160 final File classFile = new File( classesDirectory, classLocation ); 1161 1162 if ( this.isLoggable( Level.INFO ) ) 1163 { 1164 this.log( Level.INFO, this.getMessage( "transforming", new Object[] 1165 { 1166 classFile.getAbsolutePath() 1167 } ), null ); 1168 1169 } 1170 1171 final JavaClass javaClass = this.getJavaClass( classFile ); 1172 this.transformClasses( i, marshaller, unmarshaller, javaClass, transformers ); 1173 javaClass.dump( classFile ); 1174 } 1175 } 1176 } 1177 } 1178 1179 /** 1180 * Transforms committed meta-data of compiled Java classes of a given specification of the modules of the instance. 1181 * 1182 * @param specification The specification to process. 1183 * @param marshaller The marshaller to use for transforming classes. 1184 * @param unmarshaller The unmarshaller to use for transforming classes. 1185 * @param javaClass The java class to process. 1186 * @param transformers The transformers to use for transforming the classes. 1187 * 1188 * @throws NullPointerException if {@code specification}, {@code marshaller}, {@code unmarshaller}, 1189 * {@code javaClass} or {@code transformers} is {@code null}. 1190 * @throws IOException if accessing class files fails. 1191 * @throws TransformerException if transforming class files fails. 1192 */ 1193 public void transformClasses( final Specification specification, final Marshaller marshaller, 1194 final Unmarshaller unmarshaller, final JavaClass javaClass, 1195 final List<Transformer> transformers ) throws IOException, TransformerException 1196 { 1197 if ( specification == null ) 1198 { 1199 throw new NullPointerException( "specification" ); 1200 } 1201 if ( marshaller == null ) 1202 { 1203 throw new NullPointerException( "marshaller" ); 1204 } 1205 if ( unmarshaller == null ) 1206 { 1207 throw new NullPointerException( "unmarshaller" ); 1208 } 1209 if ( javaClass == null ) 1210 { 1211 throw new NullPointerException( "javaClass" ); 1212 } 1213 if ( transformers == null ) 1214 { 1215 throw new NullPointerException( "transformers" ); 1216 } 1217 1218 try 1219 { 1220 Specification decodedSpecification = null; 1221 final ObjectFactory objectFactory = new ObjectFactory(); 1222 final byte[] bytes = this.getClassfileAttribute( javaClass, Specification.class.getName() ); 1223 if ( bytes != null ) 1224 { 1225 decodedSpecification = this.decodeModelObject( unmarshaller, bytes, Specification.class ); 1226 } 1227 1228 if ( decodedSpecification != null ) 1229 { 1230 for ( Transformer transformer : transformers ) 1231 { 1232 final JAXBSource source = 1233 new JAXBSource( marshaller, objectFactory.createSpecification( decodedSpecification ) ); 1234 1235 final JAXBResult result = new JAXBResult( unmarshaller ); 1236 transformer.transform( source, result ); 1237 decodedSpecification = ( (JAXBElement<Specification>) result.getResult() ).getValue(); 1238 } 1239 1240 this.setClassfileAttribute( javaClass, Specification.class.getName(), this.encodeModelObject( 1241 marshaller, objectFactory.createSpecification( decodedSpecification ) ) ); 1242 1243 } 1244 } 1245 catch ( final JAXBException e ) 1246 { 1247 throw (IOException) new IOException( e.getMessage() ).initCause( e ); 1248 } 1249 } 1250 1251 /** 1252 * Transforms committed meta-data of compiled Java classes of a given implementation of the modules of the instance. 1253 * 1254 * @param implementation The implementation to process. 1255 * @param marshaller The marshaller to use for transforming classes. 1256 * @param unmarshaller The unmarshaller to use for transforming classes. 1257 * @param javaClass The java class to process. 1258 * @param transformers The transformers to use for transforming the classes. 1259 * 1260 * @throws NullPointerException if {@code implementation}, {@code marshaller}, {@code unmarshaller}, 1261 * {@code javaClass} or {@code transformers} is {@code null}. 1262 * @throws IOException if accessing class files fails. 1263 * @throws TransformerException if transforming class files fails. 1264 */ 1265 public void transformClasses( final Implementation implementation, final Marshaller marshaller, 1266 final Unmarshaller unmarshaller, final JavaClass javaClass, 1267 final List<Transformer> transformers ) throws TransformerException, IOException 1268 { 1269 if ( implementation == null ) 1270 { 1271 throw new NullPointerException( "implementation" ); 1272 } 1273 if ( marshaller == null ) 1274 { 1275 throw new NullPointerException( "marshaller" ); 1276 } 1277 if ( unmarshaller == null ) 1278 { 1279 throw new NullPointerException( "unmarshaller" ); 1280 } 1281 if ( javaClass == null ) 1282 { 1283 throw new NullPointerException( "javaClass" ); 1284 } 1285 if ( transformers == null ) 1286 { 1287 throw new NullPointerException( "transformers" ); 1288 } 1289 1290 try 1291 { 1292 Dependencies decodedDependencies = null; 1293 byte[] bytes = this.getClassfileAttribute( javaClass, Dependencies.class.getName() ); 1294 if ( bytes != null ) 1295 { 1296 decodedDependencies = this.decodeModelObject( unmarshaller, bytes, Dependencies.class ); 1297 } 1298 1299 Messages decodedMessages = null; 1300 bytes = this.getClassfileAttribute( javaClass, Messages.class.getName() ); 1301 if ( bytes != null ) 1302 { 1303 decodedMessages = this.decodeModelObject( unmarshaller, bytes, Messages.class ); 1304 } 1305 1306 Properties decodedProperties = null; 1307 bytes = this.getClassfileAttribute( javaClass, Properties.class.getName() ); 1308 if ( bytes != null ) 1309 { 1310 decodedProperties = this.decodeModelObject( unmarshaller, bytes, Properties.class ); 1311 } 1312 1313 Specifications decodedSpecifications = null; 1314 bytes = this.getClassfileAttribute( javaClass, Specifications.class.getName() ); 1315 if ( bytes != null ) 1316 { 1317 decodedSpecifications = this.decodeModelObject( unmarshaller, bytes, Specifications.class ); 1318 } 1319 1320 final ObjectFactory of = new ObjectFactory(); 1321 for ( Transformer transformer : transformers ) 1322 { 1323 if ( decodedDependencies != null ) 1324 { 1325 final JAXBSource source = new JAXBSource( marshaller, of.createDependencies( decodedDependencies ) ); 1326 final JAXBResult result = new JAXBResult( unmarshaller ); 1327 transformer.transform( source, result ); 1328 decodedDependencies = ( (JAXBElement<Dependencies>) result.getResult() ).getValue(); 1329 } 1330 1331 if ( decodedMessages != null ) 1332 { 1333 final JAXBSource source = new JAXBSource( marshaller, of.createMessages( decodedMessages ) ); 1334 final JAXBResult result = new JAXBResult( unmarshaller ); 1335 transformer.transform( source, result ); 1336 decodedMessages = ( (JAXBElement<Messages>) result.getResult() ).getValue(); 1337 } 1338 1339 if ( decodedProperties != null ) 1340 { 1341 final JAXBSource source = new JAXBSource( marshaller, of.createProperties( decodedProperties ) ); 1342 final JAXBResult result = new JAXBResult( unmarshaller ); 1343 transformer.transform( source, result ); 1344 decodedProperties = ( (JAXBElement<Properties>) result.getResult() ).getValue(); 1345 } 1346 1347 if ( decodedSpecifications != null ) 1348 { 1349 final JAXBSource source = 1350 new JAXBSource( marshaller, of.createSpecifications( decodedSpecifications ) ); 1351 1352 final JAXBResult result = new JAXBResult( unmarshaller ); 1353 transformer.transform( source, result ); 1354 decodedSpecifications = ( (JAXBElement<Specifications>) result.getResult() ).getValue(); 1355 } 1356 } 1357 1358 if ( decodedDependencies != null ) 1359 { 1360 this.setClassfileAttribute( javaClass, Dependencies.class.getName(), this.encodeModelObject( 1361 marshaller, of.createDependencies( decodedDependencies ) ) ); 1362 1363 } 1364 1365 if ( decodedMessages != null ) 1366 { 1367 this.setClassfileAttribute( javaClass, Messages.class.getName(), this.encodeModelObject( 1368 marshaller, of.createMessages( decodedMessages ) ) ); 1369 1370 } 1371 1372 if ( decodedProperties != null ) 1373 { 1374 this.setClassfileAttribute( javaClass, Properties.class.getName(), this.encodeModelObject( 1375 marshaller, of.createProperties( decodedProperties ) ) ); 1376 1377 } 1378 1379 if ( decodedSpecifications != null ) 1380 { 1381 this.setClassfileAttribute( javaClass, Specifications.class.getName(), this.encodeModelObject( 1382 marshaller, of.createSpecifications( decodedSpecifications ) ) ); 1383 1384 } 1385 } 1386 catch ( final JAXBException e ) 1387 { 1388 throw (IOException) new IOException( e.getMessage() ).initCause( e ); 1389 } 1390 } 1391 1392 /** 1393 * Parses a class file. 1394 * 1395 * @param classFile The class file to parse. 1396 * 1397 * @return The parsed class file. 1398 * 1399 * @throws NullPointerException if {@code classFile} is {@code null}. 1400 * @throws IOException if parsing {@code classFile} fails. 1401 * 1402 * @see JavaClass 1403 */ 1404 public JavaClass getJavaClass( final File classFile ) throws IOException 1405 { 1406 if ( classFile == null ) 1407 { 1408 throw new NullPointerException( "classFile" ); 1409 } 1410 1411 return this.getJavaClass( classFile.toURI().toURL(), classFile.getName() ); 1412 } 1413 1414 /** 1415 * Parses a class file. 1416 * 1417 * @param url The URL of the class file to parse. 1418 * @param className The name of the class at {@code url}. 1419 * 1420 * @return The parsed class file. 1421 * 1422 * @throws NullPointerException if {@code url} or {@code className} is {@code null}. 1423 * @throws IOException if parsing fails. 1424 * 1425 * @see JavaClass 1426 */ 1427 public JavaClass getJavaClass( final URL url, final String className ) throws IOException 1428 { 1429 if ( url == null ) 1430 { 1431 throw new NullPointerException( "url" ); 1432 } 1433 if ( className == null ) 1434 { 1435 throw new NullPointerException( "className" ); 1436 } 1437 1438 return this.getJavaClass( url.openStream(), className ); 1439 } 1440 1441 /** 1442 * Parses a class file. 1443 * 1444 * @param stream The stream to read the class file from. 1445 * @param className The name of the class to read from {@code stream}. 1446 * 1447 * @return The parsed class file. 1448 * 1449 * @throws NullPointerException if {@code stream} or {@code className} is {@code null}. 1450 * @throws IOException if parsing fails. 1451 * 1452 * @see JavaClass 1453 */ 1454 public JavaClass getJavaClass( final InputStream stream, final String className ) throws IOException 1455 { 1456 if ( stream == null ) 1457 { 1458 throw new NullPointerException( "stream" ); 1459 } 1460 if ( className == null ) 1461 { 1462 throw new NullPointerException( "className" ); 1463 } 1464 1465 final ClassParser parser = new ClassParser( stream, className ); 1466 final JavaClass clazz = parser.parse(); 1467 stream.close(); 1468 return clazz; 1469 } 1470 1471 /** 1472 * Gets an attribute from a java class. 1473 * 1474 * @param clazz The java class to get an attribute from. 1475 * @param attributeName The name of the attribute to get. 1476 * 1477 * @return The value of attribute {@code attributeName} of {@code clazz} or {@code null} if no such attribute 1478 * exists. 1479 * 1480 * @throws NullPointerException if {@code clazz} or {@code attributeName} is {@code null}. 1481 * @throws IOException if getting the attribute fails. 1482 * 1483 * @see JavaClass#getAttributes() 1484 */ 1485 public byte[] getClassfileAttribute( final JavaClass clazz, final String attributeName ) throws IOException 1486 { 1487 if ( clazz == null ) 1488 { 1489 throw new NullPointerException( "clazz" ); 1490 } 1491 if ( attributeName == null ) 1492 { 1493 throw new NullPointerException( "attributeName" ); 1494 } 1495 1496 final Attribute[] attributes = clazz.getAttributes(); 1497 1498 for ( int i = attributes.length - 1; i >= 0; i-- ) 1499 { 1500 final Constant constant = clazz.getConstantPool().getConstant( attributes[i].getNameIndex() ); 1501 1502 if ( constant instanceof ConstantUtf8 && attributeName.equals( ( (ConstantUtf8) constant ).getBytes() ) ) 1503 { 1504 final Unknown unknown = (Unknown) attributes[i]; 1505 return unknown.getBytes(); 1506 } 1507 } 1508 1509 return null; 1510 } 1511 1512 /** 1513 * Adds or updates an attribute in a java class. 1514 * 1515 * @param clazz The class to update. 1516 * @param attributeName The name of the attribute to update. 1517 * @param data The new data of the attribute to update the {@code classFile} with. 1518 * 1519 * @throws NullPointerException if {@code clazz} or {@code attributeName} is {@code null}. 1520 * @throws IOException if updating the class file fails. 1521 * 1522 * @see JavaClass#getAttributes() 1523 */ 1524 public void setClassfileAttribute( final JavaClass clazz, final String attributeName, final byte[] data ) 1525 throws IOException 1526 { 1527 if ( clazz == null ) 1528 { 1529 throw new NullPointerException( "clazz" ); 1530 } 1531 if ( attributeName == null ) 1532 { 1533 throw new NullPointerException( "attributeName" ); 1534 } 1535 1536 /* 1537 The JavaTM Virtual Machine Specification - Second Edition - Chapter 4.1 1538 1539 A Java virtual machine implementation is required to silently ignore any 1540 or all attributes in the attributes table of a ClassFile structure that 1541 it does not recognize. Attributes not defined in this specification are 1542 not allowed to affect the semantics of the class file, but only to 1543 provide additional descriptive information (ยง4.7.1). 1544 */ 1545 Attribute[] attributes = clazz.getAttributes(); 1546 1547 int attributeIndex = -1; 1548 int nameIndex = -1; 1549 1550 for ( int i = attributes.length - 1; i >= 0; i-- ) 1551 { 1552 final Constant constant = clazz.getConstantPool().getConstant( attributes[i].getNameIndex() ); 1553 1554 if ( constant instanceof ConstantUtf8 && attributeName.equals( ( (ConstantUtf8) constant ).getBytes() ) ) 1555 { 1556 attributeIndex = i; 1557 nameIndex = attributes[i].getNameIndex(); 1558 } 1559 } 1560 1561 if ( nameIndex == -1 ) 1562 { 1563 final Constant[] pool = clazz.getConstantPool().getConstantPool(); 1564 final Constant[] tmp = new Constant[ pool.length + 1 ]; 1565 System.arraycopy( pool, 0, tmp, 0, pool.length ); 1566 tmp[pool.length] = new ConstantUtf8( attributeName ); 1567 nameIndex = pool.length; 1568 clazz.setConstantPool( new ConstantPool( tmp ) ); 1569 } 1570 1571 final Unknown unknown = new Unknown( nameIndex, data.length, data, clazz.getConstantPool() ); 1572 1573 if ( attributeIndex == -1 ) 1574 { 1575 final Attribute[] tmp = new Attribute[ attributes.length + 1 ]; 1576 System.arraycopy( attributes, 0, tmp, 0, attributes.length ); 1577 tmp[attributes.length] = unknown; 1578 attributes = tmp; 1579 } 1580 else 1581 { 1582 attributes[attributeIndex] = unknown; 1583 } 1584 1585 clazz.setAttributes( attributes ); 1586 } 1587 1588 /** 1589 * Encodes a model object to a byte array. 1590 * 1591 * @param marshaller The marshaller to use for encoding the object. 1592 * @param modelObject The model object to encode. 1593 * 1594 * @return GZIP compressed XML document for {@code modelObject}. 1595 * 1596 * @throws NullPointerException if {@code marshaller} or {@code modelObject} is {@code null}. 1597 * @throws IOException if encoding {@code modelObject} fails. 1598 */ 1599 public byte[] encodeModelObject( final Marshaller marshaller, 1600 final JAXBElement<? extends ModelObject> modelObject ) throws IOException 1601 { 1602 if ( marshaller == null ) 1603 { 1604 throw new NullPointerException( "marshaller" ); 1605 } 1606 if ( modelObject == null ) 1607 { 1608 throw new NullPointerException( "modelObject" ); 1609 } 1610 1611 try 1612 { 1613 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 1614 final GZIPOutputStream out = new GZIPOutputStream( baos ); 1615 marshaller.marshal( modelObject, out ); 1616 out.close(); 1617 return baos.toByteArray(); 1618 } 1619 catch ( final JAXBException e ) 1620 { 1621 throw (IOException) new IOException( e.getMessage() ).initCause( e ); 1622 } 1623 } 1624 1625 /** 1626 * Decodes a model object from a byte array. 1627 * 1628 * @param unmarshaller The unmarshaller to use for decoding the object. 1629 * @param bytes The encoded model object to decode. 1630 * @param type The type of the encoded model object. 1631 * @param <T> The type of the decoded model object. 1632 * 1633 * @return Model object decoded from {@code bytes}. 1634 * 1635 * @throws NullPointerException if {@code unmarshaller}, {@code bytes} or {@code type} is {@code null}. 1636 * @throws IOException if decoding {@code bytes} fails. 1637 */ 1638 public <T extends ModelObject> T decodeModelObject( final Unmarshaller unmarshaller, final byte[] bytes, 1639 final Class<T> type ) throws IOException 1640 { 1641 if ( unmarshaller == null ) 1642 { 1643 throw new NullPointerException( "unmarshaller" ); 1644 } 1645 if ( bytes == null ) 1646 { 1647 throw new NullPointerException( "bytes" ); 1648 } 1649 if ( type == null ) 1650 { 1651 throw new NullPointerException( "type" ); 1652 } 1653 1654 try 1655 { 1656 final ByteArrayInputStream bais = new ByteArrayInputStream( bytes ); 1657 final GZIPInputStream in = new GZIPInputStream( bais ); 1658 final JAXBElement<T> element = (JAXBElement<T>) unmarshaller.unmarshal( in ); 1659 in.close(); 1660 return element.getValue(); 1661 } 1662 catch ( final JAXBException e ) 1663 { 1664 throw (IOException) new IOException( e.getMessage() ).initCause( e ); 1665 } 1666 } 1667 1668 private String getMessage( final String key, final Object args ) 1669 { 1670 final ResourceBundle b = ResourceBundle.getBundle( JavaClasses.class.getName().replace( '.', '/' ) ); 1671 final MessageFormat f = new MessageFormat( b.getString( key ) ); 1672 return f.format( args ); 1673 } 1674 1675 }