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    }