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