1 8 package org.codehaus.dna.tools.verifier; 9 10 import java.lang.reflect.Method ; 11 import java.lang.reflect.Modifier ; 12 import java.net.URL ; 13 import java.text.MessageFormat ; 14 import java.util.ArrayList ; 15 import java.util.List ; 16 import java.util.ResourceBundle ; 17 18 import org.codehaus.dna.Active; 19 import org.codehaus.dna.Composable; 20 import org.codehaus.dna.Configurable; 21 import org.codehaus.dna.Configuration; 22 import org.codehaus.dna.LogEnabled; 23 import org.codehaus.dna.ResourceLocator; 24 import org.codehaus.metaclass.Attributes; 25 import org.codehaus.metaclass.model.Attribute; 26 27 34 public class ComponentVerifier 35 { 36 39 private static final String BASE_NAME = 40 ComponentVerifier.class.getName() + "Resources"; 41 42 45 private static final ResourceBundle BUNDLE = 46 ResourceBundle.getBundle( BASE_NAME ); 47 48 52 private static final Class [] EMPTY_TYPES = new Class [ 0 ]; 53 54 57 private static final Class [] FRAMEWORK_CLASSES = new Class [] 58 { 59 LogEnabled.class, 60 Composable.class, 61 Configurable.class, 62 Active.class 63 }; 64 65 68 private static final String CONFIGURE_METHOD_NAME = "configure"; 69 70 73 private static final Class [] CONFIGURE_PARAMETER_TYPES = new Class []{Configuration.class}; 74 75 78 private static final String COMPOSE_METHOD_NAME = "compose"; 79 80 83 private static final Class [] COMPOSE_PARAMETER_TYPES = new Class []{ResourceLocator.class}; 84 85 92 public VerifyIssue[] verifyType( final Class type ) 93 { 94 final List issues = new ArrayList (); 95 verifyMetaData( type, issues ); 96 verifyDependencyMetaData( type, issues ); 97 verifyConfigurationMetaData( type, issues ); 98 99 final Class [] interfaces = getServiceClasses( type, issues ); 100 101 verifyClass( type, issues ); 102 verifyImplementsServices( type, interfaces, issues ); 103 for( int i = 0; i < interfaces.length; i++ ) 104 { 105 verifyService( interfaces[ i ], issues ); 106 } 107 108 return (VerifyIssue[])issues. 109 toArray( new VerifyIssue[ issues.size() ] ); 110 } 111 112 119 void verifyMetaData( final Class type, final List issues ) 120 { 121 final Attribute attribute = 122 Attributes.getAttribute( type, "dna.component" ); 123 if( null == attribute ) 124 { 125 final String message = getMessage( "CV001" ); 126 final VerifyIssue issue = 127 new VerifyIssue( VerifyIssue.ERROR, message ); 128 issues.add( issue ); 129 } 130 } 131 132 138 void verifyConfigurationMetaData( final Class type, final List issues ) 139 { 140 Attribute attribute = getConfigurationMetaData( type ); 141 if( null != attribute ) 142 { 143 final String location = attribute.getParameter( "location" ); 144 if( null == location ) 145 { 146 final Object [] args = new Object []{"type"}; 147 final String message = getMessage( "CV019", args ); 148 final VerifyIssue issue = 149 new VerifyIssue( VerifyIssue.ERROR, message ); 150 issues.add( issue ); 151 } 152 else 153 { 154 verifyLocation( type, location, issues ); 155 } 156 } 157 } 158 159 166 protected Attribute getConfigurationMetaData( final Class type ) 167 { 168 try 169 { 170 final Method method = getConfigurationMethod( type ); 171 return Attributes.getAttribute( method, "dna.configuration" ); 172 } 173 catch( final NoSuchMethodException nsme ) 174 { 175 return null; 176 } 177 } 178 179 186 protected Method getConfigurationMethod( final Class type ) 187 throws NoSuchMethodException 188 { 189 if( !Configurable.class.isAssignableFrom( type ) ) 190 { 191 throw new NoSuchMethodException (); 192 } 193 return type.getMethod( CONFIGURE_METHOD_NAME, 194 CONFIGURE_PARAMETER_TYPES ); 195 } 196 197 204 void verifyLocation( final Class type, 205 final String location, 206 final List issues ) 207 { 208 final URL url = type.getResource( location ); 209 if( null == url ) 210 { 211 final Object [] args = new Object []{location}; 212 final String message = getMessage( "CV020", args ); 213 final VerifyIssue issue = 214 new VerifyIssue( VerifyIssue.ERROR, message ); 215 issues.add( issue ); 216 } 217 } 218 219 225 void verifyDependencyMetaData( final Class type, final List issues ) 226 { 227 final Attribute[] attributes = getDependencyAttributes( type ); 228 for( int i = 0; i < attributes.length; i++ ) 229 { 230 final Attribute attribute = attributes[ i ]; 231 verifyDependencyMetaData( type, attribute, issues ); 232 } 233 } 234 235 243 protected Attribute[] getDependencyAttributes( final Class type ) 244 { 245 try 246 { 247 final Method method = getComposeMethod( type ); 248 return Attributes.getAttributes( method, "dna.dependency" ); 249 } 250 catch( NoSuchMethodException e ) 251 { 252 return Attribute.EMPTY_SET; 253 } 254 } 255 256 265 protected Method getComposeMethod( final Class type ) 266 throws NoSuchMethodException 267 { 268 if( !Composable.class.isAssignableFrom( type ) ) 269 { 270 throw new NoSuchMethodException (); 271 } 272 return type.getMethod( COMPOSE_METHOD_NAME, COMPOSE_PARAMETER_TYPES ); 273 } 274 275 282 void verifyDependencyMetaData( final Class type, 283 final Attribute attribute, 284 final List issues ) 285 { 286 final String optional = attribute.getParameter( "optional" ); 287 verifyOptionalParameter( optional, issues ); 288 289 final String typeName = attribute.getParameter( "type" ); 290 if( null == typeName ) 291 { 292 final Object [] args = new Object []{"type"}; 293 final String message = getMessage( "CV015", args ); 294 final VerifyIssue issue = 295 new VerifyIssue( VerifyIssue.ERROR, message ); 296 issues.add( issue ); 297 } 298 else 299 { 300 verifyDependencyType( type, typeName, issues ); 301 final String key = attribute.getParameter( "key" ); 302 if( null == key ) 303 { 304 final Object [] args = new Object []{"key"}; 305 final String message = getMessage( "CV015", args ); 306 final VerifyIssue issue = 307 new VerifyIssue( VerifyIssue.ERROR, message ); 308 issues.add( issue ); 309 } 310 else 311 { 312 verifyDependencyKeyConforms( typeName, key, issues ); 313 } 314 } 315 } 316 317 323 void verifyOptionalParameter( final String optional, final List issues ) 324 { 325 if( null == optional ) 326 { 327 final Object [] args = new Object []{"optional"}; 328 final String message = getMessage( "CV015", args ); 329 final VerifyIssue issue = 330 new VerifyIssue( VerifyIssue.ERROR, message ); 331 issues.add( issue ); 332 } 333 else 334 { 335 verifyDependencyOptionalValid( optional, issues ); 336 } 337 } 338 339 345 void verifyDependencyOptionalValid( final String optional, 346 final List issues ) 347 { 348 if( !optional.equals( "true" ) && !optional.equals( "false" ) ) 349 { 350 final Object [] args = new Object []{optional}; 351 final String message = getMessage( "CV018", args ); 352 final VerifyIssue issue = 353 new VerifyIssue( VerifyIssue.ERROR, message ); 354 issues.add( issue ); 355 } 356 } 357 358 366 void verifyDependencyKeyConforms( final String typeName, 367 final String key, 368 final List issues ) 369 { 370 final int typeLength = typeName.length(); 371 final int keyLength = key.length(); 372 final String prefix; 373 if( typeLength == keyLength ) 374 { 375 prefix = typeName; 376 } 377 else 378 { 379 prefix = typeName + "/"; 380 } 381 if( !key.startsWith( prefix ) ) 382 { 383 final Object [] args = new Object []{key}; 384 final String message = getMessage( "CV017", args ); 385 final VerifyIssue issue = 386 new VerifyIssue( VerifyIssue.NOTICE, message ); 387 issues.add( issue ); 388 } 389 } 390 391 398 void verifyDependencyType( final Class type, 399 final String typeName, 400 final List issues ) 401 { 402 try 403 { 404 type.getClassLoader().loadClass( typeName ); 405 } 406 catch( final Throwable t ) 407 { 408 final Object [] args = new Object []{typeName, t}; 409 final String message = getMessage( "CV016", args ); 410 final VerifyIssue issue = 411 new VerifyIssue( VerifyIssue.ERROR, message ); 412 issues.add( issue ); 413 } 414 } 415 416 424 void verifyImplementsServices( final Class implementation, 425 final Class [] services, 426 final List issues ) 427 { 428 for( int i = 0; i < services.length; i++ ) 429 { 430 if( !services[ i ].isAssignableFrom( implementation ) ) 431 { 432 final Object [] args = new Object []{services[ i ].getName()}; 433 final String message = getMessage( "CV002", args ); 434 final VerifyIssue issue = 435 new VerifyIssue( VerifyIssue.ERROR, message ); 436 issues.add( issue ); 437 } 438 } 439 } 440 441 448 void verifyClass( final Class type, final List issues ) 449 { 450 verifyNoArgConstructor( type, issues ); 451 verifyNonAbstract( type, issues ); 452 verifyNonArray( type, issues ); 453 verifyNonInterface( type, issues ); 454 verifyNonPrimitive( type, issues ); 455 verifyPublic( type, issues ); 456 } 457 458 465 void verifyService( final Class clazz, final List issues ) 466 { 467 verifyServiceIsaInterface( clazz, issues ); 468 verifyServiceIsPublic( clazz, issues ); 469 verifyServiceNotALifecycle( clazz, issues ); 470 } 471 472 479 void verifyServiceIsaInterface( final Class clazz, final List issues ) 480 { 481 if( !clazz.isInterface() ) 482 { 483 final Object [] args = new Object []{clazz.getName()}; 484 final String message = getMessage( "CV004", args ); 485 final VerifyIssue issue = 486 new VerifyIssue( VerifyIssue.ERROR, message ); 487 issues.add( issue ); 488 } 489 } 490 491 498 void verifyServiceIsPublic( final Class clazz, final List issues ) 499 { 500 final boolean isPublic = 501 Modifier.isPublic( clazz.getModifiers() ); 502 if( !isPublic ) 503 { 504 final Object [] args = new Object []{clazz.getName()}; 505 final String message = getMessage( "CV005", args ); 506 final VerifyIssue issue = 507 new VerifyIssue( VerifyIssue.ERROR, message ); 508 issues.add( issue ); 509 } 510 } 511 512 519 void verifyServiceNotALifecycle( final Class clazz, final List issues ) 520 { 521 for( int i = 0; i < FRAMEWORK_CLASSES.length; i++ ) 522 { 523 final Class lifecycle = FRAMEWORK_CLASSES[ i ]; 524 if( lifecycle.isAssignableFrom( clazz ) ) 525 { 526 final Object [] args = 527 new Object []{clazz.getName(), lifecycle.getName()}; 528 final String message = getMessage( "CV006", args ); 529 final VerifyIssue issue = 530 new VerifyIssue( VerifyIssue.ERROR, message ); 531 issues.add( issue ); 532 } 533 } 534 } 535 536 543 void verifyNoArgConstructor( final Class clazz, final List issues ) 544 { 545 try 546 { 547 clazz.getConstructor( EMPTY_TYPES ); 548 } 549 catch( final NoSuchMethodException nsme ) 550 { 551 final String message = getMessage( "CV008" ); 552 final VerifyIssue issue = 553 new VerifyIssue( VerifyIssue.ERROR, message ); 554 issues.add( issue ); 555 } 556 } 557 558 565 void verifyNonAbstract( final Class clazz, final List issues ) 566 { 567 final boolean isAbstract = 568 Modifier.isAbstract( clazz.getModifiers() ); 569 if( isAbstract ) 570 { 571 final String message = getMessage( "CV009" ); 572 final VerifyIssue issue = 573 new VerifyIssue( VerifyIssue.ERROR, message ); 574 issues.add( issue ); 575 } 576 } 577 578 585 void verifyPublic( final Class clazz, final List issues ) 586 { 587 final boolean isPublic = 588 Modifier.isPublic( clazz.getModifiers() ); 589 if( !isPublic ) 590 { 591 final String message = getMessage( "CV010" ); 592 final VerifyIssue issue = 593 new VerifyIssue( VerifyIssue.ERROR, message ); 594 issues.add( issue ); 595 } 596 } 597 598 605 void verifyNonPrimitive( final Class clazz, final List issues ) 606 { 607 if( clazz.isPrimitive() ) 608 { 609 final String message = getMessage( "CV011" ); 610 final VerifyIssue issue = 611 new VerifyIssue( VerifyIssue.ERROR, message ); 612 issues.add( issue ); 613 } 614 } 615 616 623 void verifyNonInterface( final Class clazz, final List issues ) 624 { 625 if( clazz.isInterface() ) 626 { 627 final String message = getMessage( "CV012" ); 628 final VerifyIssue issue = 629 new VerifyIssue( VerifyIssue.ERROR, message ); 630 issues.add( issue ); 631 } 632 } 633 634 641 void verifyNonArray( final Class clazz, final List issues ) 642 { 643 if( clazz.isArray() ) 644 { 645 final String message = getMessage( "CV013" ); 646 final VerifyIssue issue = 647 new VerifyIssue( VerifyIssue.ERROR, message ); 648 issues.add( issue ); 649 } 650 } 651 652 661 Class [] getServiceClasses( final Class type, final List issues ) 662 { 663 final List services = new ArrayList (); 664 final ClassLoader classLoader = type.getClassLoader(); 665 final Attribute[] attributes = 666 Attributes.getAttributes( type, "dna.service" ); 667 for( int i = 0; i < attributes.length; i++ ) 668 { 669 final String classname = attributes[ i ].getParameter( "type" ); 670 try 671 { 672 final Class clazz = classLoader.loadClass( classname ); 673 services.add( clazz ); 674 } 675 catch( final Throwable t ) 676 { 677 final Object [] args = new Object []{classname, t}; 678 final String message = getMessage( "CV014", args ); 679 final VerifyIssue issue = 680 new VerifyIssue( VerifyIssue.ERROR, message ); 681 issues.add( issue ); 682 } 683 } 684 685 return (Class [])services.toArray( new Class [ services.size() ] ); 686 } 687 688 694 String getMessage( final String key ) 695 { 696 return BUNDLE.getString( key ); 697 } 698 699 707 String getMessage( final String key, final Object [] args ) 708 { 709 final String pattern = getMessage( key ); 710 return MessageFormat.format( pattern, args ); 711 } 712 } 713 | Popular Tags |