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