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 }