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 */ 017package org.apache.commons.scxml2.test; 018 019import java.io.BufferedReader; 020import java.io.File; 021import java.io.IOException; 022import java.io.InputStreamReader; 023import java.net.URL; 024import java.util.StringTokenizer; 025 026import javax.xml.stream.XMLStreamException; 027 028import org.apache.commons.scxml2.Context; 029import org.apache.commons.scxml2.Evaluator; 030import org.apache.commons.scxml2.SCXMLExecutor; 031import org.apache.commons.scxml2.TriggerEvent; 032import org.apache.commons.scxml2.env.Tracer; 033import org.apache.commons.scxml2.invoke.SimpleSCXMLInvoker; 034import org.apache.commons.scxml2.io.SCXMLReader; 035import org.apache.commons.scxml2.io.SCXMLWriter; 036import org.apache.commons.scxml2.model.ModelException; 037import org.apache.commons.scxml2.model.SCXML; 038 039/** 040 * Utility methods used by command line SCXML execution, useful for 041 * debugging. 042 * 043 * The following expression languages are supported in SCXML documents: 044 * <ol> 045 * <li>JEXL - Using Commons JEXL</li> 046 * </ol> 047 * 048 * @see org.apache.commons.scxml2.env.jexl 049 */ 050public final class StandaloneUtils { 051 052 /** 053 * Command line utility method for executing the state machine defined 054 * using the SCXML document described by the specified URI and using 055 * the specified expression evaluator. 056 * 057 * @param uri The URI or filename of the SCXML document 058 * @param evaluator The expression evaluator for the expression language 059 * used in the specified SCXML document 060 * 061 * <p>RUNNING:</p> 062 * <ul> 063 * <li>Enter a space-separated list of "events"</li> 064 * <li>To quit, enter "quit"</li> 065 * <li>To populate a variable in the current context, 066 * type "name=value"</li> 067 * <li>To reset state machine, enter "reset"</li> 068 * </ul> 069 */ 070 public static void execute(final String uri, final Evaluator evaluator) { 071 try { 072 String documentURI = getCanonicalURI(uri); 073 Context rootCtx = evaluator.newContext(null); 074 Tracer trc = new Tracer(); 075 SCXML doc = SCXMLReader.read(new URL(documentURI)); 076 if (doc == null) { 077 System.err.println("The SCXML document " + uri 078 + " can not be parsed!"); 079 System.exit(-1); 080 } 081 System.out.println(SCXMLWriter.write(doc)); 082 SCXMLExecutor exec = new SCXMLExecutor(evaluator, null, trc); 083 exec.setStateMachine(doc); 084 exec.addListener(doc, trc); 085 exec.registerInvokerClass("scxml", SimpleSCXMLInvoker.class); 086 exec.setRootContext(rootCtx); 087 exec.go(); 088 BufferedReader br = new BufferedReader(new 089 InputStreamReader(System.in)); 090 String event; 091 while ((event = br.readLine()) != null) { 092 event = event.trim(); 093 if (event.equalsIgnoreCase("help") || event.equals("?")) { 094 System.out.println("Enter a space-separated list of " 095 + "events"); 096 System.out.println("To populate a variable in the " 097 + "current context, type \"name=value\""); 098 System.out.println("To quit, enter \"quit\""); 099 System.out.println("To reset state machine, enter " 100 + "\"reset\""); 101 } else if (event.equalsIgnoreCase("quit")) { 102 break; 103 } else if (event.equalsIgnoreCase("reset")) { 104 exec.reset(); 105 } else if (event.indexOf('=') != -1) { 106 int marker = event.indexOf('='); 107 String name = event.substring(0, marker); 108 String value = event.substring(marker + 1); 109 rootCtx.setLocal(name, value); 110 System.out.println("Set variable " + name + " to " 111 + value); 112 } else if (event.trim().length() == 0 113 || event.equalsIgnoreCase("null")) { 114 TriggerEvent[] evts = {new TriggerEvent(null, 115 TriggerEvent.SIGNAL_EVENT, null)}; 116 exec.triggerEvents(evts); 117 if (exec.getStatus().isFinal()) { 118 System.out.println("A final configuration reached."); 119 } 120 } else { 121 StringTokenizer st = new StringTokenizer(event); 122 int tkns = st.countTokens(); 123 TriggerEvent[] evts = new TriggerEvent[tkns]; 124 for (int i = 0; i < tkns; i++) { 125 evts[i] = new TriggerEvent(st.nextToken(), 126 TriggerEvent.SIGNAL_EVENT, null); 127 } 128 exec.triggerEvents(evts); 129 if (exec.getStatus().isFinal()) { 130 System.out.println("A final configuration reached."); 131 } 132 } 133 } 134 } catch (IOException e) { 135 e.printStackTrace(); 136 } catch (ModelException e) { 137 e.printStackTrace(); 138 } catch (XMLStreamException e) { 139 e.printStackTrace(); 140 } 141 } 142 143 /** 144 * @param uri an absolute or relative URL 145 * @return java.lang.String canonical URL (absolute) 146 * @throws java.io.IOException if a relative URL can not be resolved 147 * to a local file 148 */ 149 private static String getCanonicalURI(final String uri) 150 throws IOException { 151 if (uri.toLowerCase().startsWith("http://") 152 || uri.toLowerCase().startsWith("file://")) { 153 return uri; 154 } 155 File in = new File(uri); 156 return "file:///" + in.getCanonicalPath(); 157 } 158 159 /** 160 * Discourage instantiation since this is a utility class. 161 */ 162 private StandaloneUtils() { 163 super(); 164 } 165 166} 167