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