001 /* 002 * Copyright 2004-2007 Stephen J. McConnell. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.runtime; 020 021 import dpml.appliance.StandardAppliance; 022 import dpml.lang.Disposable; 023 import dpml.state.StateDecoder; 024 import dpml.util.DefaultLogger; 025 import dpml.util.ElementHelper; 026 027 import java.io.IOException; 028 import java.io.File; 029 import java.lang.ref.Reference; 030 import java.net.URI; 031 import java.net.URL; 032 import java.util.WeakHashMap; 033 import java.util.Hashtable; 034 import java.util.Map; 035 import java.util.Set; 036 import java.util.concurrent.Executors; 037 import java.util.concurrent.ExecutorService; 038 import java.util.concurrent.CopyOnWriteArraySet; 039 import java.util.concurrent.TimeUnit; 040 041 import net.dpml.annotation.CollectionPolicy; 042 import net.dpml.annotation.LifestylePolicy; 043 import net.dpml.annotation.ActivationPolicy; 044 045 import net.dpml.appliance.Appliance; 046 047 import net.dpml.lang.Buffer; 048 import net.dpml.lang.DecodingException; 049 import net.dpml.lang.ServiceRegistry; 050 import net.dpml.lang.StandardServiceRegistry; 051 import net.dpml.lang.Strategy; 052 053 import net.dpml.state.State; 054 055 import net.dpml.transit.Artifact; 056 057 import net.dpml.util.Logger; 058 059 import org.w3c.dom.Element; 060 061 import static dpml.state.DefaultState.NULL_STATE; 062 063 /** 064 * Component strategy. 065 * 066 * @author <a href="http://www.dpml.net">Digital Product Management Library</a> 067 * @version 2.1.1 068 */ 069 public class ComponentStrategy extends Strategy implements Component, ServiceRegistry 070 { 071 private static final Logger LOGGER = new DefaultLogger( "dpml.lang.component" ); 072 private static final ComponentStrategyHandler HANDLER = new ComponentStrategyHandler(); 073 074 private final String m_name; 075 private final int m_priority; 076 private final Class<?> m_class; 077 private final String m_path; 078 private final LifestylePolicy m_lifestyle; 079 private final CollectionPolicy m_collection; 080 private final State m_graph; 081 private final LifestyleHandler m_handler; 082 private final ContextModel m_context; 083 private final ActivationPolicy m_activation; 084 private final PartsDirective m_parts; 085 private final Logger m_logger; 086 private final Map<String,Object> m_map = new Hashtable<String,Object>(); 087 088 private final Set<ComponentListener> m_listeners = new CopyOnWriteArraySet<ComponentListener>(); 089 private final ExecutorService m_queue = Executors.newSingleThreadExecutor(); 090 091 private ServiceRegistry m_registry; 092 private Element m_element; 093 094 /** 095 * Creation of a new component strategy. 096 * @param partition the enclosing partition 097 * @param name the component name relative to the enclosing partition 098 * @param priority the component priority 099 * @param type the component class 100 * @param activation the activation policy 101 * @param lifestyle the lifestyle policy 102 * @param collection the collection policy 103 * @param context the context model 104 * @param parts the internal part structure 105 * @exception IOException if an IO error occurs 106 */ 107 ComponentStrategy( 108 final String partition, final String name, int priority, final Class type, 109 ActivationPolicy activation, LifestylePolicy lifestyle, CollectionPolicy collection, 110 ContextModel context, PartsDirective parts ) 111 throws IOException 112 { 113 super( type.getClassLoader() ); 114 115 m_class = type; 116 m_priority = priority; 117 m_activation = activation; 118 m_lifestyle = lifestyle; 119 m_collection = collection; 120 m_context = context; 121 122 m_name = getComponentName( name, m_class ); 123 m_path = getComponentPath( partition, m_name, m_class ); 124 m_logger = getComponentLogger( m_path ); 125 m_graph = getLifecycleGraph( m_class ); 126 m_parts = getPartsDirective( parts ); 127 128 m_parts.initialize( this ); 129 130 m_map.put( "name", m_name ); 131 m_map.put( "path", m_path ); 132 m_map.put( "work", new File( System.getProperty( "user.dir" ) ).getCanonicalFile() ); 133 m_map.put( "temp", new File( System.getProperty( "java.io.tmpdir" ) ).getCanonicalFile() ); 134 m_map.put( "uri", URI.create( "component:" + m_path ) ); 135 136 if( m_logger.isTraceEnabled() ) 137 { 138 final String message = 139 "new " 140 + m_collection.toString().toLowerCase() 141 + " " 142 + m_lifestyle.toString().toLowerCase() 143 + " [" 144 + m_class.getName() 145 + "]"; 146 m_logger.trace( message ); 147 } 148 149 m_handler = getLifestyleHandler( m_lifestyle ); 150 } 151 152 void setElement( Element element ) 153 { 154 m_element = element; 155 } 156 157 /** 158 * Get the component name. 159 * @return the name 160 */ 161 public String getName() 162 { 163 return m_name; 164 } 165 166 /** 167 * Get the component priority. 168 * @return the priority 169 */ 170 public int getPriority() 171 { 172 return m_priority; 173 } 174 175 /** 176 * Add a listener to the component. 177 * @param listener the component listener 178 */ 179 public void addComponentListener( ComponentListener listener ) 180 { 181 m_listeners.add( listener ); 182 } 183 184 /** 185 * Remove a listener from the component. 186 * @param listener the component listener 187 */ 188 public void removeComponentListener( ComponentListener listener ) 189 { 190 m_listeners.remove( listener ); 191 } 192 193 void processEvent( ComponentEvent event ) 194 { 195 Logger logger= getLogger(); 196 for( ComponentListener listener : m_listeners ) 197 { 198 m_queue.execute( new ComponentEventDistatcher( logger, listener, event ) ); 199 } 200 } 201 202 Map<String,Object> getContextMap() 203 { 204 return m_map; 205 } 206 207 /** 208 * Get a runtime provider for this component. 209 * @return the provider 210 */ 211 public Provider getProvider() 212 { 213 synchronized( m_handler ) 214 { 215 return m_handler.getProvider(); 216 } 217 } 218 219 /** 220 * Release a provider back to the component. 221 * @param provider the provider to release 222 */ 223 public void release( Provider provider ) 224 { 225 synchronized( m_handler ) 226 { 227 m_handler.release( provider ); 228 } 229 } 230 231 /** 232 * Initialize the component with a supplied service registry. 233 * @param registry the service registry 234 */ 235 public void initialize( ServiceRegistry registry ) // TODO: parts initialization should occur here? 236 { 237 if( m_logger.isTraceEnabled() ) 238 { 239 m_logger.trace( "initialization" ); 240 } 241 m_registry = registry; 242 } 243 244 /** 245 * Test if this component model can handle the supplied service type. 246 * @param type the service type 247 * @return true if the component is type compatible 248 */ 249 public boolean isaCandidate( Class<?> type ) 250 { 251 return type.isAssignableFrom( m_class ); 252 } 253 254 /** 255 * Get a service reference for the supplied type. 256 * @param service the service type 257 * @return a service instance or null if unresolvable 258 */ 259 public <T>T lookup( Class<T> service ) 260 { 261 if( m_logger.isTraceEnabled() ) 262 { 263 m_logger.trace( "lookup: " + service.getName() ); 264 } 265 266 for( String key : m_parts.getKeys() ) 267 { 268 Strategy strategy = m_parts.getStrategy( key ); 269 if( strategy.isaCandidate( service ) ) 270 { 271 try 272 { 273 return strategy.getInstance( service ); 274 } 275 catch( Exception e ) 276 { 277 if( strategy instanceof ComponentStrategy ) 278 { 279 ComponentStrategy s = (ComponentStrategy) strategy; 280 String path = s.getComponentPath(); 281 282 final String error = 283 "Lookup aquisition in [" 284 + getComponentPath() 285 + "] failed while aquiring the service [" 286 + service.getName() 287 + "] from the provider [" 288 + path 289 + "]."; 290 throw new ComponentError( error, e ); 291 } 292 else 293 { 294 final String error = 295 "Lookup aquisition in [" 296 + getComponentPath() 297 + "] failed while aquiring the service [" 298 + service.getName() 299 + "]."; 300 throw new ComponentError( error, e ); 301 } 302 } 303 } 304 } 305 306 if( null != m_registry ) 307 { 308 return m_registry.lookup( service ); 309 } 310 else 311 { 312 ServiceRegistry registry = new StandardServiceRegistry(); 313 return registry.lookup( service ); 314 } 315 } 316 317 /** 318 * Terminate the component model. 319 */ 320 public void terminate() 321 { 322 terminate( 10, TimeUnit.SECONDS ); 323 } 324 325 /** 326 * Terminate the component model using a supplied timeout criteria. 327 * @param timeout the timeout duration 328 * @param units the measurement units 329 */ 330 void terminate( long timeout, TimeUnit units ) 331 { 332 synchronized( this ) 333 { 334 if( getLogger().isTraceEnabled() ) 335 { 336 getLogger().trace( "termination" ); 337 } 338 m_handler.terminate(); 339 m_queue.shutdown(); 340 try 341 { 342 boolean ok = m_queue.awaitTermination( timeout, units ); 343 if( !ok ) 344 { 345 final String message = 346 "Component termination timeout in [" 347 + getName() 348 + "] (some events may not have been processed)."; 349 getLogger().warn( message ); 350 } 351 } 352 catch( Exception e ) 353 { 354 e.printStackTrace(); 355 } 356 } 357 } 358 359 /** 360 * Internal support for the resolution of a context service lookup request. 361 * The service classname comes from a context entry in this component and 362 * is resolved by the parent component. The parent evaluates off of its 363 * internal parts for a component implementing the service and if found, 364 * the instance is returned. 365 * @param class the requested class 366 * @param type the return type 367 * @exception Exception if an error occurs 368 */ 369 <T>T getService( Class<?> clazz, Class<T> type ) throws Exception // TODO: ensure we don't evaluate the requestor 370 { 371 if( getLogger().isTraceEnabled() ) 372 { 373 getLogger().trace( "invoking lookup in " + getComponentPath() + " for " + clazz.getName() ); 374 } 375 if( null != m_registry ) 376 { 377 try 378 { 379 Object value = m_registry.lookup( clazz ); 380 return type.cast( value ); 381 } 382 catch( Exception e ) 383 { 384 final String error = 385 "Service lookup in component [" 386 + getComponentPath() 387 + "] failed."; 388 throw new ComponentException( error, e ); 389 } 390 } 391 else 392 { 393 return null; 394 } 395 } 396 397 Class getComponentClass() 398 { 399 return m_class; 400 } 401 402 String getComponentName() 403 { 404 return m_name; 405 } 406 407 String getComponentPath() 408 { 409 return m_path; 410 } 411 412 ContextModel getContextModel() 413 { 414 return m_context; 415 } 416 417 PartsDirective getPartsDirective() 418 { 419 return m_parts; 420 } 421 422 State getStateGraph() 423 { 424 return m_graph; 425 } 426 427 Logger getComponentLogger() 428 { 429 return m_logger; 430 } 431 432 Logger getLogger() 433 { 434 return m_logger; 435 } 436 437 /** 438 * Return the assigned collection policy. 439 * @return the collection policy 440 */ 441 public CollectionPolicy getCollectionPolicy() 442 { 443 return m_collection; 444 } 445 446 /** 447 * Return the assigned lifestyle policy. 448 * @return the lifestyle policy 449 */ 450 public LifestylePolicy getLifestylePolicy() 451 { 452 return m_lifestyle; 453 } 454 455 /** 456 * Return the assigned activation policy. 457 * @return the activation policy 458 */ 459 public ActivationPolicy getActivationPolicy() 460 { 461 return m_activation; 462 } 463 464 private Logger getComponentLogger( String path ) 465 { 466 return new DefaultLogger( path ); 467 } 468 469 /** 470 * Return an instance of the requested class. 471 * @param clazz the class 472 * @return the instance 473 */ 474 public <T>T getInstance( Class<T> clazz ) 475 { 476 if( clazz.equals( Component.class ) ) 477 { 478 return clazz.cast( this ); 479 } 480 synchronized( m_handler ) 481 { 482 Provider provider = getProvider(); 483 if( clazz.equals( Provider.class ) ) 484 { 485 return clazz.cast( provider ); 486 } 487 Object instance = provider.getInstance( clazz ); 488 return clazz.cast( instance ); 489 } 490 } 491 492 /** 493 * Return an instance of the requested class. 494 * @param c the class 495 * @return the instance 496 * @exception IOException if an error occurs 497 */ 498 public <T>T getContentForClass( Class<T> c ) throws IOException 499 { 500 if( c.isAssignableFrom( m_class ) ) 501 { 502 return getInstance( c ); 503 } 504 else if( c == Provider.class ) 505 { 506 synchronized( m_handler ) 507 { 508 Provider provider = m_handler.getProvider(); 509 return c.cast( provider ); 510 } 511 } 512 else if( c.isAssignableFrom( getClass() ) ) 513 { 514 return c.cast( this ); 515 } 516 else if( c == Appliance.class ) 517 { 518 Logger logger = getComponentLogger(); 519 Appliance appliance = new StandardAppliance( logger, this ); 520 return c.cast( appliance ); 521 } 522 else 523 { 524 return null; 525 } 526 } 527 528 /** 529 * Encode the component model to the supplied buffer. 530 * @param buffer the endoding buffer 531 * @param key the component key 532 * @exception IOException if an error occurs 533 */ 534 public void encode( Buffer buffer, String key ) throws IOException 535 { 536 boolean flag = buffer.isNamespace( NAMESPACE ); 537 if( null != m_element ) 538 { 539 String k = ElementHelper.getAttribute( m_element, "key" ); 540 if( m_element.getLocalName().equals( "part" ) ) 541 { 542 buffer.nl( "<part" ); 543 if( !flag ) 544 { 545 buffer.write( " xmlns=\"" + NAMESPACE + "\"" ); 546 } 547 String uri = ElementHelper.getAttribute( m_element, "uri" ); 548 buffer.write( " key=\"" + k + "\"" ); 549 buffer.write( " uri=\"" + uri + "\"" ); 550 Element[] elements = ElementHelper.getChildren( m_element ); 551 if( elements.length == 0 ) 552 { 553 buffer.write( "/>" ); 554 } 555 else 556 { 557 buffer.write( ">" ); 558 for( Element elem : elements ) 559 { 560 String local = elem.getLocalName(); 561 if( "param".equals( local ) ) 562 { 563 Buffer b = buffer.indent(); 564 b.nl( "<param key=\"" ); 565 String name = ElementHelper.getAttribute( elem, "key" ); 566 b.write( name ); 567 b.write( "\" value=\"" ); 568 String value = ElementHelper.getAttribute( elem, "value" ); 569 b.write( value ); 570 b.write( "\"/>" ); 571 } 572 else 573 { 574 String spec = DecodingException.list( elem ); 575 String warning = 576 "Ignoring unrecognized element: " 577 + local 578 + "\n" 579 + spec; 580 getLogger().warn( warning ); 581 } 582 } 583 buffer.nl( "</part>" ); 584 } 585 return; 586 } 587 else 588 { 589 String classname = getComponentClass().getName(); 590 String name = ElementHelper.getAttribute( m_element, "name" ); 591 String priority = ElementHelper.getAttribute( m_element, "priority" ); 592 String lifestyle = ElementHelper.getAttribute( m_element, "lifestyle" ); 593 String lifecycle = ElementHelper.getAttribute( m_element, "lifecycle" ); 594 String collection = ElementHelper.getAttribute( m_element, "collection" ); 595 String activation = ElementHelper.getAttribute( m_element, "activation" ); 596 ContextDirective context = getContextModel().getDirective(); 597 PartsDirective parts = getPartsDirective(); 598 buffer.nl( "<component" ); 599 if( !flag ) 600 { 601 buffer.write( " xmlns=\"" + NAMESPACE + "\"" ); 602 } 603 buffer.write( " class=\"" + classname + "\"" ); 604 if( null != key ) 605 { 606 buffer.write( " key=\"" + k + "\"" ); 607 } 608 if( null != name ) 609 { 610 buffer.write( " name=\"" + name + "\"" ); 611 } 612 if( ( null != priority ) && ( !"0".equals( priority ) ) ) 613 { 614 buffer.write( " priority=\"" + priority + "\"" ); 615 } 616 if( null != lifestyle ) 617 { 618 buffer.write( " lifestyle=\"" + lifestyle + "\"" ); 619 } 620 if( null != lifecycle ) 621 { 622 buffer.write( " lifecycle=\"" + lifestyle + "\"" ); 623 } 624 if( null != collection ) 625 { 626 buffer.write( " collection=\"" + collection + "\"" ); 627 } 628 if( null != activation ) 629 { 630 buffer.write( " activation=\"" + activation + "\"" ); 631 } 632 if( ( context.size() == 0 ) && ( parts.size() == 0 ) ) 633 { 634 buffer.write( "/>" ); 635 } 636 else 637 { 638 buffer.write( ">" ); 639 Buffer b = buffer.namespace( NAMESPACE ); 640 context.encode( b.indent(), null ); 641 parts.encode( b.indent() ); 642 buffer.nl( "</component>" ); 643 } 644 } 645 } 646 else 647 { 648 throw new IllegalStateException( "Undefined element" ); 649 } 650 } 651 652 private PartsDirective getPartsDirective( PartsDirective directive ) 653 { 654 if( null == directive ) 655 { 656 return new PartsDirective(); 657 } 658 else 659 { 660 return directive; 661 } 662 } 663 664 private String getComponentPath( String partition, String name, Class c ) 665 { 666 if( null == partition ) 667 { 668 return "/" + name; 669 } 670 else 671 { 672 return "/" + partition.replace( ".", "/" ) + "/" + name; 673 } 674 } 675 676 private String getComponentName( String name, Class<?> c ) 677 { 678 if( null != name ) 679 { 680 return name; 681 } 682 else 683 { 684 return getComponentName( c ); 685 } 686 } 687 688 //----------------------------------------------------------------------- 689 // Lifestyle handlers 690 //----------------------------------------------------------------------- 691 692 private LifestyleHandler getLifestyleHandler( LifestylePolicy policy ) 693 { 694 if( policy.equals( LifestylePolicy.SINGLETON ) ) 695 { 696 return new SingletonLifestyleHandler( this ); 697 } 698 else if( policy.equals( LifestylePolicy.THREAD ) ) 699 { 700 return new ThreadLifestyleHandler( this ); 701 } 702 else 703 { 704 return new TransientLifestyleHandler( this ); 705 } 706 } 707 708 /** 709 * Singleton holder class. The singleton holder mains a single 710 * <tt>LifestyleHandler</tt> of a component relative to the component model 711 * identity within the scope of the controller. References to the 712 * singleton instance will be shared across mutliple threads. 713 */ 714 private class SingletonLifestyleHandler extends LifestyleHandler 715 { 716 private Reference<Provider> m_reference; 717 718 SingletonLifestyleHandler( ComponentStrategy strategy ) 719 { 720 super( strategy ); 721 Provider provider = new StandardProvider( strategy ); 722 m_reference = createReference( null ); 723 } 724 725 Provider getProvider() 726 { 727 Provider provider = m_reference.get(); 728 if( null == provider ) 729 { 730 ComponentStrategy strategy = getComponentStrategy(); 731 provider = new StandardProvider( strategy ); 732 m_reference = createReference( provider ); 733 } 734 return provider; 735 } 736 737 void release( Provider provider ) 738 { 739 } 740 741 void terminate() 742 { 743 synchronized( this ) 744 { 745 Provider provider = m_reference.get(); 746 if( null != provider ) 747 { 748 if( provider instanceof Disposable ) 749 { 750 Disposable disposable = (Disposable) provider; 751 disposable.dispose(); 752 } 753 m_reference = createReference( null ); 754 } 755 } 756 } 757 } 758 759 /** 760 * Transient holder class. The transient holder provides support for 761 * the transient lifestyle ensuing the creation of a new <tt>LifestyleHandler</tt> 762 * per request. 763 */ 764 private class TransientLifestyleHandler extends LifestyleHandler 765 { 766 private final WeakHashMap<Provider,Void> m_providers = new WeakHashMap<Provider,Void>(); // transients 767 768 TransientLifestyleHandler( ComponentStrategy strategy ) 769 { 770 super( strategy ); 771 } 772 773 Provider getProvider() 774 { 775 ComponentStrategy strategy = getComponentStrategy(); 776 Provider provider = new StandardProvider( strategy ); 777 m_providers.put( provider, null ); 778 return provider; 779 } 780 781 void release( Provider provider ) 782 { 783 if( null == provider ) 784 { 785 return; 786 } 787 if( m_providers.containsKey( provider ) ) 788 { 789 m_providers.remove( provider ); 790 if( provider instanceof Disposable ) 791 { 792 Disposable disposable = (Disposable) provider; 793 disposable.dispose(); 794 } 795 } 796 } 797 798 void terminate() 799 { 800 Provider[] providers = m_providers.keySet().toArray( new Provider[0] ); 801 for( Provider provider : providers ) 802 { 803 release( provider ); 804 } 805 } 806 } 807 808 /** 809 * The ThreadHolder class provides support for the per-thread lifestyle 810 * policy within which new <tt>LifestyleHandler</tt> creation is based on a single 811 * <tt>LifestyleHandler</tt> per thread. 812 */ 813 private class ThreadLifestyleHandler extends LifestyleHandler 814 { 815 private final ThreadLocalHolder m_threadLocalHolder = new ThreadLocalHolder(); 816 817 ThreadLifestyleHandler( ComponentStrategy strategy ) 818 { 819 super( strategy ); 820 } 821 822 Provider getProvider() 823 { 824 return (Provider) m_threadLocalHolder.get(); 825 } 826 827 void release( Provider provider ) 828 { 829 m_threadLocalHolder.release( provider ); 830 } 831 832 void terminate() 833 { 834 m_threadLocalHolder.terminate(); 835 } 836 837 /** 838 * Internal thread local holder for the per-thread lifestyle holder. 839 */ 840 private class ThreadLocalHolder extends ThreadLocal 841 { 842 private final WeakHashMap<Provider,Void> m_providers = 843 new WeakHashMap<Provider,Void>(); // per thread instances 844 845 protected synchronized Provider initialValue() 846 { 847 ComponentStrategy strategy = getComponentStrategy(); 848 Provider provider = new StandardProvider( strategy ); 849 m_providers.put( provider, null ); 850 return provider; 851 } 852 853 synchronized void release( Provider provider ) 854 { 855 if( m_providers.containsKey( provider ) ) 856 { 857 m_providers.remove( provider ); 858 if( provider instanceof Disposable ) 859 { 860 Disposable disposable = (Disposable) provider; 861 disposable.dispose(); 862 } 863 } 864 } 865 866 synchronized void terminate() 867 { 868 Provider[] providers = m_providers.keySet().toArray( new Provider[0] ); 869 for( Provider provider : providers ) 870 { 871 release( provider ); 872 } 873 } 874 } 875 } 876 877 //----------------------------------------------------------------------- 878 // utilities 879 //----------------------------------------------------------------------- 880 881 private static String getComponentName( Class<?> c ) 882 { 883 if( c.isAnnotationPresent( net.dpml.annotation.Component.class ) ) 884 { 885 net.dpml.annotation.Component annotation = 886 c.getAnnotation( net.dpml.annotation.Component.class ); 887 String name = annotation.name(); 888 if( !"".equals( name ) ) 889 { 890 return name; 891 } 892 } 893 return c.getName(); 894 } 895 896 private static String getComponentNameFromClass( Class<?> c ) 897 { 898 String classname = c.getName(); 899 int n = classname.lastIndexOf( "." ); 900 if( n > -1 ) 901 { 902 return classname.substring( n+1 ); 903 } 904 else 905 { 906 return classname; 907 } 908 } 909 910 private static CollectionPolicy getCollectionPolicy( Class<?> c ) 911 { 912 if( c.isAnnotationPresent( net.dpml.annotation.Component.class ) ) 913 { 914 net.dpml.annotation.Component annotation = 915 c.getAnnotation( net.dpml.annotation.Component.class ); 916 return annotation.collection(); 917 } 918 return CollectionPolicy.HARD; 919 } 920 921 private static State getLifecycleGraph( Class<?> c ) throws IOException 922 { 923 String name = getComponentNameFromClass( c ); 924 String spec = name + ".xgraph"; 925 URL url = getLifecycleURL( c, spec ); 926 if( null != url ) 927 { 928 StateDecoder decoder = new StateDecoder(); 929 return decoder.loadState( url ); 930 } 931 if( c.isAnnotationPresent( net.dpml.annotation.Component.class ) ) 932 { 933 net.dpml.annotation.Component annotation = 934 c.getAnnotation( net.dpml.annotation.Component.class ); 935 String path = annotation.lifecycle(); 936 return getLifecycleGraph( c, path ); 937 } 938 else 939 { 940 return NULL_STATE; 941 } 942 } 943 944 private static State getLifecycleGraph( Class<?> c, String path ) throws IOException 945 { 946 if( ( null == path ) || "".equals( path ) ) 947 { 948 return NULL_STATE; 949 } 950 else 951 { 952 URL url = getLifecycleURL( c, path ); 953 StateDecoder decoder = new StateDecoder(); 954 return decoder.loadState( url ); 955 } 956 } 957 958 private static URL getLifecycleURL( Class<?> c, String path ) throws IOException 959 { 960 int n = path.indexOf( ":" ); 961 if( n > -1 ) 962 { 963 try 964 { 965 return Artifact.toURL( new URI( path ) ); 966 } 967 catch( Exception e ) 968 { 969 final String error = 970 "Bad url: " + path; 971 IOException ioe = new IOException( error ); 972 ioe.initCause( e ); 973 throw ioe; 974 } 975 } 976 else 977 { 978 return c.getResource( path ); 979 } 980 } 981 982 private static final String NAMESPACE = ComponentStrategyHandler.NAMESPACE; 983 984 /** 985 * Returns the string representing of the component. 986 * @return the component as a string 987 */ 988 public String toString() 989 { 990 return getComponentPath(); 991 } 992 993 /** 994 * Component event dispatcher. 995 */ 996 private static class ComponentEventDistatcher implements Runnable 997 { 998 private Logger m_logger; 999 private ComponentListener m_listener; 1000 private ComponentEvent m_event; 1001 1002 ComponentEventDistatcher( Logger logger, ComponentListener listener, ComponentEvent event ) 1003 { 1004 m_logger = logger; 1005 m_listener = listener; 1006 m_event = event; 1007 } 1008 1009 /** 1010 * Run the dispatch thread. 1011 */ 1012 public void run() 1013 { 1014 try 1015 { 1016 m_listener.componentChanged( m_event ); 1017 } 1018 catch( Throwable e ) 1019 { 1020 final String error = 1021 "Event distatch error."; 1022 m_logger.error( error, e ); 1023 } 1024 } 1025 } 1026 }