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    }