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