001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.scxml.invoke;
018    
019    import java.io.IOException;
020    import java.io.Serializable;
021    import java.net.URL;
022    import java.util.Iterator;
023    import java.util.Map;
024    
025    import org.apache.commons.scxml.Context;
026    import org.apache.commons.scxml.Evaluator;
027    import org.apache.commons.scxml.SCInstance;
028    import org.apache.commons.scxml.SCXMLExecutor;
029    import org.apache.commons.scxml.TriggerEvent;
030    import org.apache.commons.scxml.env.SimpleDispatcher;
031    import org.apache.commons.scxml.env.SimpleErrorHandler;
032    import org.apache.commons.scxml.env.SimpleErrorReporter;
033    import org.apache.commons.scxml.env.SimpleSCXMLListener;
034    import org.apache.commons.scxml.io.SCXMLParser;
035    import org.apache.commons.scxml.model.ModelException;
036    import org.apache.commons.scxml.model.SCXML;
037    import org.xml.sax.SAXException;
038    
039    /**
040     * A simple {@link Invoker} for SCXML documents. Invoked SCXML document
041     * may not contain external namespace elements, further invokes etc.
042     */
043    public class SimpleSCXMLInvoker implements Invoker, Serializable {
044    
045        /** Serial version UID. */
046        private static final long serialVersionUID = 1L;
047        /** Parent state ID. */
048        private String parentStateId;
049        /** Event prefix, all events sent to the parent executor must begin
050         *  with this prefix. */
051        private String eventPrefix;
052        /** Invoking document's SCInstance. */
053        private SCInstance parentSCInstance;
054        /** The invoked state machine executor. */
055        private SCXMLExecutor executor;
056        /** Cancellation status. */
057        private boolean cancelled;
058    
059        //// Constants
060        /** Prefix for all events sent to the parent state machine. */
061        private static String invokePrefix = ".invoke.";
062        /** Suffix for invoke done event. */
063        private static String invokeDone = "done";
064        /** Suffix for invoke cancel response event. */
065        private static String invokeCancelResponse = "cancel.response";
066    
067        /**
068         * {@inheritDoc}.
069         */
070        public void setParentStateId(final String parentStateId) {
071            this.parentStateId = parentStateId;
072            this.eventPrefix = this.parentStateId + invokePrefix;
073            this.cancelled = false;
074        }
075    
076        /**
077         * {@inheritDoc}.
078         */
079        public void setSCInstance(final SCInstance scInstance) {
080            this.parentSCInstance = scInstance;
081        }
082    
083        /**
084         * {@inheritDoc}.
085         */
086        public void invoke(final String source, final Map params)
087        throws InvokerException {
088            SCXML scxml = null;
089            try {
090                scxml = SCXMLParser.parse(new URL(source),
091                    new SimpleErrorHandler());
092            } catch (ModelException me) {
093                throw new InvokerException(me.getMessage(), me.getCause());
094            } catch (IOException ioe) {
095                throw new InvokerException(ioe.getMessage(), ioe.getCause());
096            } catch (SAXException se) {
097                throw new InvokerException(se.getMessage(), se.getCause());
098            }
099            Evaluator eval = parentSCInstance.getEvaluator();
100            executor = new SCXMLExecutor(eval,
101                new SimpleDispatcher(), new SimpleErrorReporter());
102            Context rootCtx = eval.newContext(null);
103            for (Iterator iter = params.entrySet().iterator(); iter.hasNext();) {
104                Map.Entry entry = (Map.Entry) iter.next();
105                rootCtx.setLocal((String) entry.getKey(), entry.getValue());
106            }
107            executor.setRootContext(rootCtx);
108            executor.setStateMachine(scxml);
109            executor.addListener(scxml, new SimpleSCXMLListener());
110            executor.registerInvokerClass("scxml", this.getClass());
111            try {
112                executor.go();
113            } catch (ModelException me) {
114                throw new InvokerException(me.getMessage(), me.getCause());
115            }
116            if (executor.getCurrentStatus().isFinal()) {
117                TriggerEvent te = new TriggerEvent(eventPrefix + invokeDone,
118                    TriggerEvent.SIGNAL_EVENT);
119                new AsyncTrigger(parentSCInstance.getExecutor(), te).start();
120            }
121        }
122    
123        /**
124         * {@inheritDoc}.
125         */
126        public void parentEvents(final TriggerEvent[] evts)
127        throws InvokerException {
128            if (cancelled) {
129                return; // no further processing should take place
130            }
131            boolean doneBefore = executor.getCurrentStatus().isFinal();
132            try {
133                executor.triggerEvents(evts);
134            } catch (ModelException me) {
135                throw new InvokerException(me.getMessage(), me.getCause());
136            }
137            if (!doneBefore && executor.getCurrentStatus().isFinal()) {
138                TriggerEvent te = new TriggerEvent(eventPrefix + invokeDone,
139                    TriggerEvent.SIGNAL_EVENT);
140                new AsyncTrigger(parentSCInstance.getExecutor(), te).start();
141            }
142        }
143    
144        /**
145         * {@inheritDoc}.
146         */
147        public void cancel()
148        throws InvokerException {
149            cancelled = true;
150            TriggerEvent te = new TriggerEvent(eventPrefix
151                + invokeCancelResponse, TriggerEvent.SIGNAL_EVENT);
152            new AsyncTrigger(parentSCInstance.getExecutor(), te).start();
153        }
154    
155    }
156