    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