📄 scxmldialognavigationhandler.java
字号:
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* You may build in a package on your choice. Dependency information:
*
* Commons SCXML dependencies -
* http://commons.apache.org/scxml/dependencies.html
*
* Apache Shale dependencies -
* http://shale.apache.org/dependencies.html
*/
package org.apache.commons.scxml.usecases;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.faces.application.NavigationHandler;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.Rule;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.scxml.SCXMLDigester;
import org.apache.commons.scxml.SCXMLExecutor;
import org.apache.commons.scxml.TriggerEvent;
import org.apache.commons.scxml.env.SimpleDispatcher;
import org.apache.commons.scxml.env.SimpleErrorHandler;
import org.apache.commons.scxml.env.SimpleErrorReporter;
import org.apache.commons.scxml.env.SimpleSCXMLListener;
import org.apache.commons.scxml.env.faces.SessionContext;
import org.apache.commons.scxml.env.faces.ShaleDialogELEvaluator;
import org.apache.commons.scxml.model.ModelException;
import org.apache.commons.scxml.model.SCXML;
import org.apache.commons.scxml.model.TransitionTarget;
import org.apache.shale.dialog.Globals;
import org.apache.shale.dialog.Status;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
/**
* <p>SCXML configuration file(s) driven Shale dialog navigation handler.</p>
*
* <p>Recipe for using SCXML documents to drive Shale dialogs:
* <ol>
* <li>Build the <code>SCXMLDialogNavigationHandler</code> (available
* below, use a Commons SCXML nightly build 10/09/05 or later) and make it
* available to your web application classpath (<code>WEB-INF/classes</code>).
* </li>
* <li>Update the "<code>WEB-INF/faces-config.xml</code>"
* for your web application such that the
* "<code>faces-config/application/navigation-handler</code>"
* entry points to
* "<code>org.apache.commons.scxml.usecases.SCXMLDialogNavigationHandler</code>"
* (with the appropriate package name, if you changed it).
* </li>
* <li>As an alternative to (1) and (2), you can place a <i>jar</i> in the
* <code>WEB-INF/lib</code> directory which contains the
* <code>SCXMLDialogNavigationHandler</code> and a
* <code>META-INF/faces-config.xml</code> with just the entry in (2).</li>
* <li>Use SCXML documents to describe Shale dialog flows (details below)
* in your application. You may have multiple mappings from transition
* targets to JSF views to support multi-channel applications.</li>
* <li>The SCXML-based dialog is entered when
* <code>handleNavigation()</code> is called with a logical outcome
* of the form "<code>dialog:xxx</code>" and there is no current
* dialog in progress, where "<code>xxx</code>" is the URL pointing
* to the SCXML document.</li>
* </ol>
* </p>
*
* <p>Using SCXML documents to define the Shale dialog "flows":
* <ul>
* <li>ActionState instances may be mapped to executable content
* in UML <onentry> (and may be chained similarly).</li>
* <li>ViewState instances may be mapped to UML transition
* targets.</li>
* <li>SubdialogState instances may be mapped to external SCXML
* documents.</li>
* <li>EndState instances may be mapped to SCXML final states.</li>
* <li>The {@link SCXMLDialogNavigationHandler} defines a
* "faces.outcome" event which the relevant SCXML
* transitions from a "view state" can wait for.</li>
* </ul>
* </p>
*
* <p>Towards pluggable dialog management in Shale - A "black box"
* dialog may consist of the following tuple:
* <ul>
* <li>Unique dialog identifier</li>
* <li>A generic NavigationHandler (i.e. dialog strategy)</li>
* <li>An dialog/flow configuration resource (Ex: SCXML document)</li>
* <li>Optionally, multiple other configuration resources,
* (Ex: one for each channel - web, voice, small device, etc.)</li>
* </ul>
* The Shale DialogNavigationHandler may then delegate appropriately.
* </p>
*/
public final class SCXMLDialogNavigationHandler extends NavigationHandler {
// ------------------------------------------------------------ Constructors
/**
* <p>Create a new {@link SCXMLDialogNavigationHandler}, wrapping the
* specified standard navigation handler implementation.</p>
*
* @param handler Standard <code>NavigationHandler</code> we are wrapping
*/
public SCXMLDialogNavigationHandler(NavigationHandler handler) {
this.handler = handler;
}
// -------------------------------------------------------- Static Variables
/**
* <p>The prefix on a logical outcome String that indicates the remainder
* of the string is the URL of a SCXML-based Shale dialog to be entered.</p>
*/
public static final String PREFIX = "dialog:";
// ------------------------------------------------------ Instance Variables
/**
* <p>The standard <code>NavigationHandler</code> implementation that
* we are wrapping.</p>
*/
private NavigationHandler handler = null;
/**
* <p>The <code>Log</code> instance for this class.</p>
*/
private final Log log = LogFactory.getLog(getClass());
/**
* <p>Key under which we will store the SCXMLExecutor (more generally,
* some session scoped state pertaining to the current dialog).</p>
*/
private String dialogKey = null; // Cached on first use
/**
* <p>Map storing SCXML state IDs as keys and JSF view IDs as values.</p>
*/
private Map target2viewMap = null;
// ----------------------------------------------- NavigationHandler Methods
/**
* <p>Handle the navigation request implied by the specified parameters.</p>
*
* @param context <code>FacesContext</code> for the current request
* @param fromAction The action binding expression that was evaluated
* to retrieve the specified outcome (if any)
* @param outcome The logical outcome returned by the specified action
*
* @exception IllegalArgumentException if the configuration information
* for a previously saved position cannot be found
* @exception IllegalArgumentException if an unknown State type is found
*/
public void handleNavigation(FacesContext context, String fromAction,
String outcome) {
if (log.isDebugEnabled()) {
log.debug("handleNavigation(viewId=" +
context.getViewRoot().getViewId() +
",fromAction=" + fromAction +
",outcome=" + outcome + ")");
}
SCXMLExecutor exec = getDialogExecutor(context);
String viewId = null;
if (exec == null && outcome != null && outcome.startsWith(PREFIX)) {
/**** DIALOG ENTRY ****/
// dialog is a state machine, parse & obtain an executor
exec = initDialogExecutor(context, outcome.substring(PREFIX.
length()));
if (exec != null) {
// cache executor in session scope
// TODO: Shale caches Dialog instances. SCXMLExecutor
// knows what state(s) the dialog is in, so Dialog#findState()
// is not needed.
setDialogExecutor(context, exec);
// obtain our initial view
viewId = getCurrentViewId(exec);
}
// else delegate
} else if (exec != null) {
/**** SUBSEQUENT TURNS OF DIALOG ****/
// pass a handle to the current ctx (for evaluating binding exprs)
updateEvaluator(context, outcome);
// fire a "faces.outcome" event on the dialog's state machine
TriggerEvent[] te = { new TriggerEvent("faces.outcome",
TriggerEvent.SIGNAL_EVENT) };
try {
exec.triggerEvents(te);
} catch (ModelException me) {
log.error(me.getMessage(), me);
}
// obtain next view
viewId = getCurrentViewId(exec);
}
if (viewId != null) {
// we understood this "outcome" and we have a new view to render
log.info("Rendering view: " + viewId);
updateDialogStatus(context, exec);
render(context, viewId);
} else {
/**** DELEGATE BY DEFAULT ****/
handler.handleNavigation(context, fromAction, outcome);
}
}
/**
* <p>Return the SCXMLExecutor for the specified SCXML document, if it
* exists; otherwise, return <code>null</code>.</p>
*
* @param context <code>FacesContext</code> for the current request
* @param dialogIdentifier URL of the SCXML document for the requested
* dialog
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -