The Shale Dialog2 Framework is designed to allow configuration of a "dialog" or "conversation" with a user, which may span multiple HTTP requests. While such a dialog is in progress, the framework will also maintain a "data" object representing the state of the current computation, and throw that data object away when the conversation is completed, without waiting for the user to log off so that the user's session will be cleaned out.
Design goals for the framework include the following:
The remaining sections below provide more details about the generic framework APIs and functionality exposed to application developers. However, many details of the actual functionality (including configuration resource formats) will be specific to the particular implementation of the Dialog2 Framework that you select for your application, so be sure to consult the JavaDocs for that particular package as well.
The Shale Dialog2 framework operates on the following primary abstractions:
Constants.CONTEXT_BEAN
(literal value is dialog2
). At most one DialogContext
may be associated with a JSF view at any time.DialogContext
instances,
or retrieve existing ones, associated with a particular user session.
During runtime execution of the application, the manager for a particular
user is stored as a session scope attribute specified by
Constants.MANAGER_BEAN
(literal value is
org.apache.shale.dialog2.MANAGER
).While a DialogContext
instance is active for a particular JavaServer Faces view, it takes over the usual
implementation of the JavaServer Faces NavigationHandler
. After the
Invoke Application phase completes, a custom navigation handler will call the
advance()
on the active DialogContext instance. This method is
presumed to advance the state of the underlying dialog until a JavaServer Faces
view needs to be displayed to the user. At that point, the advanced()
method returns the required view identifier (or null
to redisplay
the current view, consistent with standard JavaServer Faces navigation), and
the requested view is rendered to the user.
If there is no active DialogContext
for the current view, the
custom NavigationHandler
delegates to the standard JavaServer Faces
NavigationHandler
. Because of this, you may freely intermix views
managed by the dialog framework and views managed by standard JavaServer Faces
navigation rules, in the same application.
It is quite often useful to maintain a set of information about the state of
the current dialog's computation, across multiple requests to the server. When
a particular dialog instance is completed, the corresponding information can be
thrown away. The Dialog2 Framework supports these requirements by supporting a
general purpose data
property (of type Object) on the
DialogContext instance.
The data type of this object can be anything useful for the dialog being executed,
but will typically be either a JavaBean containing properties for each individual
information item comprising the state of the computation for this dialog, or a
Map
where key/value pairs can easily be stored. When the dialog instance has
completed its computation, the data object will have all references to it removed,
so that it can be garbage collected.
Of particular note is the fact that JavaServer Faces value binding expressions can be used to bind visual components to values in the data object. For example, assume that the dialog is managing a wizard dialog for updating a user profile, and one of the properties that can be edited is the user's full name. One can bind an input text component directly to the corresponding property like this, with no need for intermediate copying as the various pages of the wizard dialog are navigated:
<h:inputText id="fullName" value="#{dialog2.data.fullName}"/>
If the current view does not have an active DialogContext instance associated with it, there are several approaches available to creating a new instance and associating it with the current view:
Constants.DIALOG_PREFIX
(literal value is dialog2:
), a new DialogContext instance
for a dialog named by the remainder of the logical outcome string
will be created and associated with the current view. To start a
dialog named "wizard", the action method could execute:
return "dialog2:wizard";
// Create a new DialogContext instance for a dialog named "wizard" FacesContext context = FacesContext.getCurrentInstance(); DialogContextManager manager = (DialogContextManager) context.getApplication().getVariableResolver(). resolveVariable(context, Constants.MANAGER_BEAN); DialogContext dcontext = manager.create(context, "wizard"); // Advance the state of this dialog until it needs to display a view String viewId = dcontext.advance(context, null); // Navigate to the specified view and return ViewHandler vh = context.getApplication().getViewHandler(); UIViewRoot view = vh.createView(context, viewId); view.setViewId(viewId); context.setViewRoot(view); context.renderResponse(); return null;
Constants.DIALOG_NAME
(literal value is
org.apache.shale.dialog2.DIALOG_NAME
), a new
DialogContext
instance for this dialog name will
be created and associated with the current view. For example,
a request for view editCustomer.faces
could also
request starting the EditCustomerInfo
dialog by
submitting a request URL like this:
http://localhost:8080/myapp/editCustomer.faces?org.apache.shale.dialog2.DIALOG_NAME=EditCustomerInfo
In the latter case, there is an additional option to cause the newly
created dialog instance to be associated with a parent dialog
instance for the same user. This is accomplished by specifying an additional
request parameter specified by Constants.PARENT_ID
(literal value is org.apache.shale.dialog2.PARENT_ID
), whose
value is the dialog id of an active dialog for this user.
The most common use case for this special parent/child relationship is
where the main page that a user is executing is under control of a named
dialog, and the application wishes to display a popup window (under the control
of its own dialog, and therefore its own DialogContext intsance) that can
access the data of the parent dialog instance, via a value binding expression
like #{dialog2.parent.data.fullName}
from within the popup dialog's
execution. In this way, you can easily create popup dialogs that can pull
initial state information from the main window, perform arbitrary operations
on it, and push results back to the main window ... with the only coupling being
agreement on the names of properties in the parent dialog's data
object to be used for communication.
The precise mechanism by which a DialogContext is removed is dependent upon
the implementation selected, but typically an implementation will allow the
application developer to specify an "end state" that, when advance()
reaches that state, will cause the remove()
method on the
DialogContextManager instance for the
current user to be called. This will trigger any necessary cleanup, including
releasing all references to the data
object for the removed
DialogContext
instance so that it can be garbage collected.
Any implementation of the Shale Dialog2 APIs should conform to the following requirements:
META-INF/faces-config.xml
resource in the JAR
file, so that the JavaServer Faces implementation will process the
included configuration metadata at application startup time.org.apache.shale.dialog2.DialogContextManager
. The
managed bean definition must:
Constants.MANAGER_BEAN
(literal value is
org.apache.shale.dialog2.MANAGER
).DialogContextManager
implementation will
configure itself on demand, upon the first call to its processing
methods.If these requirements are met, an application developer may select a particular
implementation simply by dropping its corresponding JAR file (and any external
dependencies that the implementation also requires) into the
/WEB-INF/lib
directory of a web application, and the implementation
will be self configured.
The Shale Sandbox currently contains two implementations of the Dialog2 Framework: