001    /*
002     * Copyright 2005 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.metro.runtime;
020    
021    import java.beans.Expression;
022    import java.beans.Statement;
023    import java.lang.reflect.InvocationTargetException;
024    import java.net.URI;
025    import java.util.ArrayList;
026    import java.util.Hashtable;
027    import java.util.List;
028    import java.util.WeakHashMap;
029    import java.util.EventObject;
030    import java.util.EventListener;
031    import java.rmi.RemoteException;
032    
033    import net.dpml.state.State;
034    import net.dpml.state.Transition;
035    import net.dpml.state.Operation;
036    import net.dpml.state.Interface;
037    import net.dpml.state.Trigger;
038    import net.dpml.state.Trigger.TriggerEvent;
039    import net.dpml.state.Action;
040    import net.dpml.state.StateMachine;
041    import net.dpml.state.UnknownOperationException;
042    import net.dpml.state.UnknownTransitionException;
043    import net.dpml.state.IntegrityRuntimeException;
044    import net.dpml.state.StateListener;
045    import net.dpml.state.StateEvent;
046    import net.dpml.state.ExecAction;
047    import net.dpml.state.ApplyAction;
048    
049    import net.dpml.util.Logger;
050    import net.dpml.util.EventQueue;
051    import net.dpml.util.EventHandler;
052    
053    /**
054     * Default state-machine implementation.
055     * 
056     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
057     * @version 1.0.4
058     */
059    public class DefaultStateMachine implements StateMachine, EventHandler
060    {
061       /**
062        * Constant name used to reference a state change in a property event.
063        */
064        public static final String PROPERTY_NAME = "state";
065        
066       /**
067        * Validate the state integrity.
068        * @param state the state to validate
069        */
070        public static void validate( State state )
071        {
072            validateState( state );
073        }
074    
075        //-------------------------------------------------------------------------------
076        // state
077        //-------------------------------------------------------------------------------
078        
079        private State m_state;
080        private boolean m_active = false;
081        private boolean m_disposed = false;
082        
083        private final Object m_lock = new Object();
084    
085        private final EventQueue m_queue;
086        
087        private final WeakHashMap m_listeners = new WeakHashMap();
088    
089        private final Logger m_logger;
090        
091        //-------------------------------------------------------------------------------
092        // constructor
093        //-------------------------------------------------------------------------------
094    
095       /**
096        * Creation of a new state machine using a state graph.
097        * @param queue the event queue
098        * @param logger the logging channel
099        * @param state the state graph
100        * @exception RemoteException if RMI exception occurs
101        */
102        public DefaultStateMachine( EventQueue queue, Logger logger, State state ) throws RemoteException
103        {
104            m_queue = queue;
105            m_logger = logger;
106            m_state = state;
107        }
108    
109        //-------------------------------------------------------------------------------
110        // StateMachine
111        //-------------------------------------------------------------------------------
112    
113       /**
114        * Add a state change listener to the state machine.
115        * @param listener the state listener
116        */
117        public void addStateListener( final StateListener listener )
118        {
119            if( null == listener )
120            {
121                throw new NullPointerException( "listener" );
122            }
123            checkDisposed();
124            synchronized( m_lock ) 
125            {
126                m_listeners.put( listener, null );
127            }
128        }
129        
130       /**
131        * Remove a state listener from the state machine.
132        * @param listener the state listener
133        */
134        public void removeStateListener( final StateListener listener )
135        {
136            if( null == listener )
137            {
138                throw new NullPointerException( "listener" );
139            }
140            checkDisposed();
141            synchronized( m_lock )
142            {
143                m_listeners.remove( listener );
144            }
145        }
146        
147       /**
148        * Return the assigned state event listeners.
149        * @return the event listeners
150        */
151        public EventListener[] getEventListeners()
152        {
153            return getStateListeners();
154        }
155        
156        private StateListener[] getStateListeners()
157        {
158            synchronized( m_lock )
159            {
160                return (StateListener[]) m_listeners.keySet().toArray( new StateListener[0] );
161            }
162        }
163        
164       /**
165        * Process the supplied event.
166        * @param event the event to process
167        */
168        public void processEvent( EventObject event )
169        {
170            StateListener[] listeners = getStateListeners();
171            for( int i=0; i < listeners.length; i++ )
172            {
173                StateListener listener = listeners[i];
174                if( event instanceof StateEvent )
175                {
176                    try
177                    {
178                        StateEvent e = (StateEvent) event;
179                        listener.stateChanged( e );
180                    }
181                    catch( Throwable e )
182                    {
183                        final String error =
184                          "StateListener notification error.";
185                        getLogger().error( error, e );
186                    }
187                }
188            }
189        }
190    
191       /**
192        * Return the current state.
193        * @return the current state
194        */
195        public State getState()
196        {
197            checkDisposed();
198            synchronized( m_state )
199            {
200                return m_state;
201            }
202        }
203        
204       /**
205        * Locate and return the most immediate initialization action defined relative to 
206        * the current state.  If an action is located within the current state it will be 
207        * return otherwise the search will continue up the state graph until either an 
208        * action is located or no further parent state is available in which case a null 
209        * value is returned.
210        * 
211        * @return the initialization action or null if no action declared
212        */
213        public Action getInitializationAction()
214        {
215            checkDisposed();
216            try
217            {
218                return getAction( m_state, Trigger.INITIALIZATION );
219            }
220            catch( Throwable e )
221            {
222                final String error = 
223                  "Unexpected error during resolution of initialization actions.";
224                throw new IntegrityRuntimeException( error, e );
225            }
226        }
227        
228       /**
229        * Locate and return the most immediate termination action defined relative to 
230        * the current state.  If an action is located within the current state it will be 
231        * return otherwise the search will continue up the state graph until either an 
232        * action is located or no further parent state is available in which case a null 
233        * value is returned.
234        * 
235        * @return the termination action or null if no action declared
236        */
237        public Action getTerminationAction()
238        {
239            checkDisposed();
240            try
241            {
242                return getAction( m_state, Trigger.TERMINATION );
243            }
244            catch( Throwable e )
245            {
246                final String error = 
247                  "Unexpected error during resolution of termination actions.";
248                throw new IntegrityRuntimeException( error, e );
249            }
250        }
251        
252       /**
253        * Invoke initialization of the supplied object using the initialization action
254        * declared under the current state path.
255        * 
256        * @param object the object to initialize
257        * @return the state established as a sidee effect of the initialization
258        * @exception InvocationTargetException if an invocation error occurs as a 
259        *   result of initialization
260        */
261        public State initialize( Object object ) throws InvocationTargetException
262        {
263            checkDisposed();
264            ArrayList visited = new ArrayList();
265            try
266            {
267                State result = initialize( visited, object );
268                m_active = true;
269                return result;
270            }
271            catch( UnknownTransitionException e )
272            {
273                final String error = 
274                  "Internal state machine error raised due to an unresolved transition.";
275                throw new IntegrityRuntimeException( error, e );
276            }
277            catch( UnknownOperationException e )
278            {
279                final String error = 
280                  "Internal state machine error raised due to an unresolved operation.";
281                throw new IntegrityRuntimeException( error, e );
282            }
283            catch( InvocationTargetException e )
284            {
285                throw e;
286            }
287            catch( Throwable e )
288            {
289                final String error = 
290                  "Unexpected error during state-machine initialization.";
291                throw new IntegrityRuntimeException( error, e );
292            }
293        }
294        
295       /**
296        * Execute a named operation on the supplied object.
297        * @param name an operation name
298        * @param object the target object
299        * @param args operation arguments
300        * @return the result of operation invocation
301        * @exception UnknownOperationException if the operation is unknown
302        * @exception InvocationTargetException if an invocation error occurs as a 
303        *   result of operation execution
304        */
305        public Object execute( String name, Object object, Object[] args ) 
306          throws UnknownOperationException, InvocationTargetException
307        {
308            checkDisposed();
309            Operation operation = getOperation( getState(), name );
310            return execute( operation, object, args );
311        }
312        
313       /**
314        * Invoke a management method on the supplied object.
315        * @param object the target object
316        * @param method the method name
317        * @param args method parameter arguments
318        * @return the return value
319        * @exception IllegalStateException if the method is recognized but not available
320        * @exception UnknownOperationException if the operation is unknown
321        * @exception InvocationTargetException if an invocation error occurs as a 
322        *   result of operation execution
323        */
324        public Object invoke( Object object, String method, Object[] args ) 
325          throws UnknownOperationException, InvocationTargetException, IllegalStateException
326        {
327            checkDisposed();
328            
329            // TODO: validate exposure of declaring interface
330            
331            try
332            {
333                Expression expression = new Expression( object, method, args );
334                return expression.getValue();
335            }
336            catch( InvocationTargetException e )
337            {
338                throw e;
339            }
340            catch( Exception e )
341            {
342                throw new InvocationTargetException( e );
343            }
344        }
345        
346       /**
347        * Apply a named transition to the target object.
348        * @param name the transition name
349        * @param object the object against which any transition handler action are to be applied
350        * @return the state established by the application of the transition
351        * @exception UnknownTransitionException if the transition is unknown
352        * @exception InvocationTargetException if an invocation error occurs as a 
353        *   result of transition invocation
354        */
355        public State apply( String name, Object object ) 
356          throws UnknownTransitionException, InvocationTargetException
357        {
358            checkDisposed();
359            synchronized( m_state )
360            {
361                Transition transition = getTransition( m_state, name );
362                return apply( transition, object );
363            }
364        }
365        
366       /**
367        * Return all of the available transitions relative to the current state.
368        * @return the available transitions
369        */
370        public Transition[] getTransitions()
371        {
372            checkDisposed();
373            Hashtable table = new Hashtable();
374            State[] states = m_state.getStatePath();
375            for( int i=( states.length-1 ); i>-1; i-- )
376            {
377                State state = states[i];
378                Transition[] transitions = state.getTransitions();
379                for( int j=0; j<transitions.length; j++ )
380                {
381                    Transition transition = transitions[j];
382                    String name = transition.getName();
383                    if( null == table.get( name ) )
384                    {
385                        table.put( name, transition );
386                    }
387                }
388            }
389            return (Transition[]) table.values().toArray( new Transition[0] );
390        }
391        
392       /**
393        * Return all of the available operations relative to the current state.
394        * @return the available operations
395        */
396        public Operation[] getOperations() 
397        {
398            checkDisposed();
399            Hashtable table = new Hashtable();
400            State[] states = m_state.getStatePath();
401            for( int i=( states.length-1 ); i>-1; i-- )
402            {
403                State state = states[i];
404                Operation[] operations = state.getOperations();
405                for( int j=0; j<operations.length; j++ )
406                {
407                    Operation operation = operations[j];
408                    String name = operation.getName();
409                    if( null == table.get( name ) )
410                    {
411                        table.put( name, operation );
412                    }
413                }
414            }
415            return (Operation[]) table.values().toArray( new Operation[0] );
416        }
417        
418       /**
419        * Return all of the available interfaces relative to the current state.
420        * @return the available interface declarations
421        */
422        public Interface[] getInterfaces()
423        {
424            checkDisposed();
425            Hashtable table = new Hashtable();
426            State[] states = m_state.getStatePath();
427            for( int i=( states.length-1 ); i>-1; i-- )
428            {
429                State state = states[i];
430                Interface[] interfaces = state.getInterfaces();
431                for( int j=0; j<interfaces.length; j++ )
432                {
433                    Interface data = interfaces[j];
434                    String name = data.getName();
435                    if( null == table.get( name ) )
436                    {
437                        table.put( name, data );
438                    }
439                }
440            }
441            return (Interface[]) table.values().toArray( new Interface[0] );
442        }
443        
444       /**
445        * Invoke termination of the supplied object using the termination action
446        * declared under the current state path.
447        * 
448        * @param object the object to terminate
449        * @return the state established as a side-effect of the termination
450        */
451        public State terminate( Object object )
452        {
453            checkDisposed();
454            ArrayList visited = new ArrayList();
455            try
456            {
457                State result = terminate( visited, object );
458                m_active = false;
459                return result;
460            }
461            catch( Throwable e )
462            {
463                e.printStackTrace();
464                return getState();
465            }
466        }
467        
468       /**
469        * Returns the active status of the state machine.
470        * @return TRUE if the state machine has invoked initialization and 
471        * termination has not been performed otherwise FALSE
472        */
473        public boolean isActive()
474        {
475            return m_active;
476        }
477        
478       /**
479        * Dispose of the state machine.
480        */
481        public void dispose()
482        {
483            synchronized( m_lock )
484            {
485                StateListener[] listeners = getStateListeners();
486                for( int i=0; i<listeners.length; i++ )
487                {
488                    StateListener listener = listeners[i];
489                    removeStateListener( listener );
490                }
491            }
492            m_disposed = true;
493            m_state = null;
494        }
495        
496        //-------------------------------------------------------------------------------
497        // implementation
498        //-------------------------------------------------------------------------------
499        
500        private String getTag( final Object object )
501        {
502            return DefaultProvider.createTag( object );
503        }
504        
505        private State initialize( List list, Object object ) throws Exception
506        {
507            Action action = getInitializationAction();
508            if( null == action )
509            {
510                return m_state;
511            }
512            else if( list.contains( action ) )
513            {
514                return m_state;
515            }
516            else
517            {
518                list.add( action );
519                if( action instanceof Operation )
520                {
521                    Operation operation = (Operation) action;
522                    execute( operation, object, new Object[0] );
523                    return m_state;
524                }
525                else if( action instanceof Transition )
526                {
527                    Transition transition = (Transition) action;
528                    State current = getState();
529                    State result = apply( transition, object );
530                    if( !current.equals( result ) )
531                    {
532                        return initialize( list, object );
533                    }
534                    else
535                    {
536                        return getState();
537                    }
538                }
539                else
540                {
541                    final String error = "Unrecognized action: " + action;
542                    throw new IllegalStateException( error );
543                }
544            }
545        }
546        
547        private Object execute( Operation operation, Object object, Object[] args ) throws InvocationTargetException
548        {
549            if( null == object )
550            {
551                return null;
552            }
553            
554            String method = getMethodName( operation );
555            
556            try
557            {
558                Expression expression = new Expression( object, method, args );
559                return expression.getValue();
560            }
561            catch( InvocationTargetException e )
562            {
563                throw e;
564            }
565            catch( Exception e )
566            {
567                throw new InvocationTargetException( e );
568            }
569        }
570        
571        private State terminate( List list, Object object ) throws Exception
572        {
573            Action action = getTerminationAction();
574            if( null == action )
575            {
576                return m_state;
577            }
578            else if( list.contains( action ) )
579            {
580                return m_state;
581            }
582            else
583            {
584                if( action instanceof Operation )
585                {
586                    Operation operation = (Operation) action;
587                    execute( operation, object, new Object[0] );
588                    return m_state;
589                }
590                else if( action instanceof Transition )
591                {
592                    Transition transition = (Transition) action;
593                    State current = getState();
594                    State result = apply( transition, object );
595                    if( !current.equals( result ) )
596                    {
597                        return terminate( list, object );
598                    }
599                    else
600                    {
601                        return getState();
602                    }
603                }
604                else
605                {
606                    final String error = "Unrecognized action: " + action;
607                    throw new IllegalStateException( error );
608                }
609            }
610        }
611        
612        private State apply( Transition transition, Object object ) throws InvocationTargetException
613        {
614            synchronized( m_state )
615            {
616                State context = transition.getState();
617                String target = transition.getTargetName();
618                State state = getState( context, target );
619                Operation operation = transition.getOperation();
620                if( getLogger().isTraceEnabled() )
621                {
622                    String name = transition.getName();
623                    String tag = getTag( object );
624                    getLogger().trace( 
625                      "applying transition [" 
626                      + name 
627                      + "] to " 
628                      + tag );
629                }
630                if( null != operation )
631                {
632                    execute( operation, object, new Object[0] ); // TODO: add resolved values as args
633                }
634                setState( object, state );
635                return state;
636            }
637        }
638        
639        private void execute( URI handler, Object object ) throws InvocationTargetException
640        {
641            if( null == object )
642            {
643                return;
644            }
645            String scheme = handler.getScheme();
646            if( "method".equals( scheme ) )
647            {
648                String methodName = handler.getSchemeSpecificPart();
649                if( getLogger().isTraceEnabled() )
650                {
651                    String tag = getTag( object );
652                    getLogger().trace( 
653                      "executing operation [" 
654                      + methodName 
655                      + "] on " 
656                      + tag );
657                }
658                Statement statement = new Statement( object, methodName, new Object[0] );
659                try
660                {
661                    statement.execute();
662                }
663                catch( InvocationTargetException e )
664                {
665                    throw e;
666                }
667                catch( Exception e )
668                {
669                    throw new InvocationTargetException( e );
670                }
671            }
672            else
673            {
674                final String error = 
675                  "Operation scheme not recognized."
676                  + "\nScheme: " + scheme
677                  + "\nURI: " + handler;
678                throw new IllegalArgumentException( error );
679            }
680        }
681        
682        private void setState( final Object object, final State state )
683        {
684            synchronized( m_state )
685            {
686                if( m_state != state )
687                {
688                    final State oldState = m_state;
689                    m_state = state;
690                    
691                    if( getLogger().isTraceEnabled() )
692                    {
693                        String tag = getTag( object );
694                        getLogger().trace( 
695                          "transitioning from [" 
696                          + oldState 
697                          + "] to [" 
698                          + state
699                          + "] in " 
700                          + tag );
701                    }
702                    
703                    final StateEvent event = new StateEvent( this, oldState, state );
704                    m_queue.enqueueEvent( event );
705                }
706            }
707        }
708        
709        private Action getAction( State state, TriggerEvent category )
710          throws UnknownTransitionException, UnknownOperationException
711        {
712            Trigger[] triggers = state.getTriggers();
713            for( int i=0; i<triggers.length; i++ )
714            {
715                Trigger trigger = triggers[i];
716                if( trigger.getEvent().equals( category ) )
717                {
718                    Action action = trigger.getAction();
719                    if( action instanceof ApplyAction )
720                    {
721                        ApplyAction apply = (ApplyAction) action;
722                        String id = apply.getID();
723                        return getTransition( state, id );
724                    }
725                    else if( action instanceof ExecAction )
726                    {
727                        ExecAction exec = (ExecAction) action;
728                        String id = exec.getID();
729                        return getOperation( state, id );
730                    }
731                    else
732                    {
733                        return action;
734                    }
735                }
736            }
737            State parent = state.getParent();
738            if( null != parent )
739            {
740                return getAction( parent, category );
741            }
742            else
743            {
744                return null;
745            }
746        }
747        
748        private Transition getTransition( State state, String name ) throws UnknownTransitionException
749        {
750            Transition[] transitions = state.getTransitions();
751            for( int i=0; i<transitions.length; i++ )
752            {
753                Transition transition = transitions[i];
754                if( name.equals( transition.getName() ) )
755                {
756                    return transition;
757                }
758            }
759            State parent = state.getParent();
760            if( null == parent )
761            {
762                final String error = 
763                  "Unable to resolve a transition named [" 
764                  + name
765                  + "] relative to the state [" 
766                  + state.getName()
767                  + "].";
768                throw new UnknownTransitionException( error );
769            }
770            else
771            {
772                try
773                {
774                    return getTransition( parent, name );
775                }
776                catch( UnknownTransitionException e )
777                {
778                    final String error = 
779                      "Unable to resolve a transition named [" 
780                      + name
781                      + "] relative to the current state [" 
782                      + state
783                      + "].";
784                    throw new UnknownTransitionException( error );
785                }
786            }
787        }
788    
789        private Operation getOperation( State state, String name ) throws UnknownOperationException
790        {
791            Operation[] operations = state.getOperations();
792            for( int i=0; i<operations.length; i++ )
793            {
794                Operation operation = operations[i];
795                if( name.equals( operation.getName() ) )
796                {
797                    return operation;
798                }
799            }
800            State parent = state.getParent();
801            if( null == parent )
802            {
803                throw new UnknownOperationException( name );
804            }
805            else
806            {
807                return getOperation( parent, name );
808            }
809        }
810    
811        //-------------------------------------------------------------------------------
812        // static internals used to validate the integrity of a state graph
813        //-------------------------------------------------------------------------------
814        
815        private static void validateState( State state )
816        {
817            Trigger[] triggers = state.getTriggers();
818            validateTriggers( state, triggers );
819            Transition[] transitions = state.getTransitions();
820            validateTransitions( state, transitions );
821            Operation[] operations = state.getOperations();
822            validateOperations( state, operations );
823            State[] states = state.getStates();
824            validateStates( state, states );
825        }
826    
827        private static void validateTransitions( State state, Transition[] transitions )
828        {
829            for( int i=0; i<transitions.length; i++ )
830            {
831                Transition transition = transitions[i];
832                validateTransition( state, transition );
833            }
834        }
835    
836        private static void validateOperations( State state, Operation[] operations )
837        {
838            for( int i=0; i<operations.length; i++ )
839            {
840                Operation operation = operations[i];
841                validateOperation( state, operation );
842            }
843        }
844    
845        private static void validateTriggers( State state, Trigger[] triggers )
846        {
847            for( int i=0; i<triggers.length; i++ )
848            {
849                Trigger trigger = triggers[i];
850                validateTrigger( state, trigger );
851            }
852        }
853        
854        private static void validateStates( State state, State[] states )
855        {
856            for( int i=0; i<states.length; i++ )
857            {
858                State s = states[i];
859                validateState( s );
860            }
861        }
862        
863        private static void validateTrigger( State state, Trigger trigger )
864        {
865            TriggerEvent event = trigger.getEvent();
866            Action action = trigger.getAction();
867            if( action instanceof Transition )
868            {
869                Transition transition = (Transition) action;
870                validateTransition( state, transition );
871            }
872            else if( action instanceof Operation )
873            {
874                Operation operation = (Operation) action;
875                validateOperation( state, operation );
876            }
877            else if( action instanceof ApplyAction )
878            {
879                ApplyAction apply = (ApplyAction) action;
880                String id = apply.getID();
881                validateTransition( state, id );
882            }
883            else if( action instanceof ExecAction )
884            {
885                ExecAction exec = (ExecAction) action;
886                String id = exec.getID();
887                validateOperation( state, id );
888            }
889        }
890        
891        private static void validateTransition( State state, String id )
892        {
893            //System.out.println( "# v/transition: " + id );
894        }
895        
896        private static void validateTransition( State state, Transition transition )
897        {
898            String target = transition.getTargetName();
899            State s = getState( state, target );
900            if( null == state )
901            {
902                final String error = 
903                  "Transition target [" 
904                  + target 
905                  + "] does not exist relative to state ["
906                  + state;
907                throw new IllegalStateException( error );
908            }
909        }
910        
911        private static void validateOperation( State state, String id )
912        {
913            //System.out.println( "# v/operation: " + operation );
914        }
915        
916        private static void validateOperation( State state, Operation operation )
917        {
918            //System.out.println( "# v/operation: " + operation );
919        }
920    
921        private static State getState( State state, String target )
922        {
923            if( target.startsWith( "/" ) )
924            {
925                //
926                // its an absolute target 
927                //
928                
929                String spec = target.substring( 1 );
930                State root = state.getStatePath()[0];
931                return getState( root, spec );
932            }
933            else if( target.startsWith( "../" ) )
934            {
935                //
936                // its relative to the state's parent
937                //
938                
939                String spec = target.substring( 3 );
940                State parent = state.getParent();
941                if( null != parent )
942                {
943                    return getState( parent, spec );
944                }
945                else
946                {
947                    final String error = 
948                    "Invalid relative reference [" 
949                    + spec
950                    + "] passed to root state.";
951                    throw new IllegalArgumentException( error );
952                }
953            }
954            else if( target.indexOf( "/" ) > -1 )
955            {
956                //
957                // its a composition address
958                //
959                
960                int index = target.indexOf( "/" );
961                String base = target.substring( 0, index );
962                String remainder = target.substring( index + 1 );
963                State s = getState( state, base );
964                return getState( s, remainder );
965            }
966            else
967            {
968                //
969                // its a name relative to the supplied state
970                //
971                
972                State[] states = state.getStates();
973                for( int i=0; i<states.length; i++ )
974                {
975                    State s = states[i];
976                    if( target.equals( s.getName() ) )
977                    {
978                        return s;
979                    }
980                }
981                final String error = 
982                  "Requested target state [" 
983                  + target
984                  + "] does not exist within the state ["
985                  + state.getName()
986                  + "].";
987                throw new IllegalArgumentException( error );
988            }
989        }
990        
991        private void checkDisposed() throws IllegalStateException
992        {
993            if( m_disposed )
994            {
995                final String error = 
996                  "Instance has been disposed.";
997                throw new IllegalStateException( error );
998            }
999        }
1000    
1001        private String getMethodName( Operation operation )
1002        {
1003            if( null != operation.getMethodName() )
1004            {
1005                return operation.getMethodName();
1006            }
1007            
1008            //
1009            // otherwise resolve it using java beans style getXxxx
1010            //
1011            
1012            String name = operation.getName();
1013            int n = name.length();
1014            if( n == 0 )
1015            {
1016                throw new IllegalArgumentException( "Operation name is empty." );
1017            }
1018            else if( n == 1 )
1019            {
1020                return "get" + name.toUpperCase();
1021            }
1022            else
1023            {
1024                return "get" 
1025                  + name.substring( 0, 1 ).toUpperCase()
1026                  + name.substring( 1 );
1027            }
1028        }
1029        
1030        private static URI createURI( String path ) throws Exception
1031        {
1032            if( null == path )
1033            {
1034                return null;
1035            }
1036            else
1037            {
1038                return new URI( path );
1039            }
1040        }
1041    
1042        private Logger getLogger()
1043        {
1044            return m_logger;
1045        }
1046        
1047    }