[cvs] Expresso commit by mtraum: xml pretty print formattting (ant
target
JCorporate Ltd
jcorp at jcorp2.servlets.net
Mon Oct 4 19:51:36 PDT 2004
Log Message:
-----------
xml pretty print formattting (ant target xmlpp-edg)
Modified Files:
--------------
expresso/expresso-web/expresso/doc/edg:
utility.xml
components.xml
dbobjects.xml
WhyUseExpresso.xml
logging.xml
reporting.xml
contrib.xml
WhatIsMVC.xml
registration.xml
validation.xml
developing.xml
controllers.xml
taglib.xml
asyncprocess.xml
dbobject-types.xml
overview.xml
security.xml
caching.xml
event.xml
connectionpool.xml
unittest.xml
jsp.xml
dbmaint.xml
intro.xml
expressoxml.xml
deploy.xml
workflow.xml
configuration.xml
jobcontrol.xml
WhyUseFramework.xml
internationalization.xml
Revision Data
-------------
Index: workflow.xml
===================================================================
RCS file: /home/javacorp/.cvs/expresso/expresso/expresso-web/expresso/doc/edg/workflow.xml,v
retrieving revision 1.8
retrieving revision 1.9
diff -Lexpresso-web/expresso/doc/edg/workflow.xml -Lexpresso-web/expresso/doc/edg/workflow.xml -u -r1.8 -r1.9
--- expresso-web/expresso/doc/edg/workflow.xml
+++ expresso-web/expresso/doc/edg/workflow.xml
@@ -1,811 +1,653 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<chapter id="workflow" xreflabel="Workflow">
- <title>Expresso Workflow</title>
-
- <para><note> <para> If you find this EDG documentation helpful please
- consider <link linkend="donate_workflow">DONATING</link>! to keep the doc
- alive and current. </para> </note></para>
-
- <para><informaltable colsep="0" frame="none" pgwide="1" rowsep="0"> <tgroup
- cols="2" colsep="0" rowsep="0"> <colspec align="left" colsep="0"
- colwidth="50%" rowsep="0" /> <colspec align="right" colsep="0"
- colwidth="50%" rowsep="0" /> <tbody> <row> <entry><emphasis role="bold">Version:</emphasis>
- <emphasis role="version">Expresso 5.5</emphasis> </entry> <entry> <emphasis
- role="bold">Maintainer:</emphasis><ulink
- url="mailto:dlloyd at jgroup.net?Subject=EDG"><emphasis role="maintainer">Aime
- Bazin</emphasis></ulink> </entry> </row> </tbody> </tgroup> </informaltable></para>
-
- <remark><emphasis role="bold">Workflow was added to Expresso to both
- facilitate and encourage a certain approach to developing screen flow logic.
- This approach expects controller objects to only perform âcontrollerâ
- functions as described in the MVC design pattern. </emphasis></remark>
-
- <sidebar>
- <para> <emphasis role="bold">Warning:</emphasis> The workflow
- functionality in Expresso is still an alpha-quality feature. A few users
- have developed and use this feature, but it has not been tested well and
- may contain some bugs. </para>
- </sidebar>
-
- <sect1>
- <title>Introduction</title>
-
- <para>The following new features were added to Expresso to both facilitate
- and encourage a certain approach to developing screen flow logic. This
- approach expects controller objects to only perform âcontrollerâ functions
- as described in the MVC design pattern. This should limit the controller
- to âadaptâ and âmediateâ type of actions. Thanks to the Controller classâ
- support for the HTTP protocol, there should be little need for web
- applications to provide their own âadaptingâ code. This is because
- Expresso provides abstractions such as ControllerRequest and
- PersistentSession objects that have already done the adapting work between
- the HTTP and Java worlds.</para>
- </sect1>
-
- <sect1>
- <title>Theory</title>
-
-
-
- <para> In addition to being able to use transitions to transfer control
- between states and controllers, Expresso offers a basic form of workflow
- and allows for quite simple linear flows between states in a wizard like
- fashion. Each workflow state may also be re-useable in other controllers.
- This means that the workflow through the controller is defined at a higher
- level and not in the states themselves. </para>
-
-
-
- <para> This approach expects controller objects to only perform
- 'controller' functions as described in the MVC design pattern.
- This should limit the controller to 'adapt' and 'mediate'
- type of actions. Thanks to the Controller class' support for the HTTP
- protocol, there should be little need for web applications to provide
- their own 'adapting' code. This is because Expresso provides
- abstractions such as ControllerRequest and PersistentSession objects that
- have already done the adapting work between the HTTP and Java worlds.
- </para>
-
- <para>Most of the code required in Controller descendant classes should be
- solely for the purpose of providing âmediationâ logic. This is the logic
- that ties components of the application together. The new features that
- have been added to the Controller class encourage developers to keep this
- mediation logic inside controller objects by providing support for
- mediation between state objects. This should help to avoid mediation code
- finding its way into state classes.</para>
-
-
-
- <para> Keeping the mediation code away from state classes allows these
- objects to focus on 'model' functions as described in MVC. In
- particular the state classes are 'commands' as defined in the
- Command design pattern. The state classes accept any required input via a
- StateForm at the time they are invoked, perform their 'business'
- logic and return any results via the state's form and/or the
- ControllerResponse object. If the business logic that needs to be
- performed is fairly complex then the state should act as a type of
- adapter. This is adapting between a user interaction and the classes used
- to support that user request (note: this is NOT like the adapting role of
- the Controller from HTTP to Java). The state would be responsible to take
- the single user request and adapt it into many calls onto supporting
- custom business 'model' objects. The state class (and any
- supporting model objects) should rarely include any mediation or workflow
- logic otherwise this reduces the potential for reuse in different
- application configurations. Therefore a properly coded state will be
- ignorant about who called it as well as who it should call once complete.
- </para>
-
-
-
- <para> The workflow features were designed with the following intent: A
- controller should relate to a 'use-case'. This is a single
- business function such as 'Register to a website' or 'Add a
- new widget to the inventory'. These use cases could comprise either a
- single user interaction or many user interactions/screens. A state relates
- to a single user interaction such as 'Enter address information'
- where the address information fits on a single screen (Note: since we kept
- mediation logic out of the state, this same state could be used both
- within a wizard-style registration and as part of a stand-alone screen to
- revise address information). </para>
-
-
-
- <para> When a controller is coded, states are added to the controller in
- one of 3 ways: <itemizedlist> <listitem><para>As a 'prompt' state</para></listitem>
- <listitem><para>As a 'handle' state</para></listitem> <listitem><para>As a
- 'final' state</para></listitem> </itemizedlist> </para>
-
-
-
- <para> Prompt states are those which build the contents of a screen using
- Input, Output, Block and Transition objects. The Controller will add its
- own transition objects based on the 'position' of the prompt state
- within the controller these are the Next and Previous buttons common in
- wizards. When errors are generated in a prompt state's associated
- handle state, the prompt state will be invoked by default assuming the
- handle state has not overridden this at runtime. </para>
-
-
-
- <para> Handle states accept user input from a state form and usually
- perform validation on that data. These states are normally directly
- invoked as a result of a user action however they will also be invoked as
- a result of a 'final' state execution. Restricting these states to
- validation-type logic allows them to be reused more easily (eg Within a
- wizard or stand-alone). Even in a single-screen controller it is a good
- practice to separate the validation from the commit processing using a
- final state. <note><para>Keeping the handle state ignorant of its associated
- prompt state allows it to be reused more readily. This can be useful for
- example, when automating a number of user tasks in a batch or proxy mode.</para></note>
- </para>
-
-
-
- <para> Final states are the last state to be invoked in a controller. They
- usually contain the guts of the processing involved in a use-case. This
- could include database updates or any other processing that needs to occur
- once all validation has succeeded. Before a final state is executed, all
- handle states within that controller are rerun and any validation errors
- cause the appropriate prompt state to be redisplayed. This is useful for
- wizard-style screen flow. </para>
-
-
-
- <para> Structuring your states in this fashion allows a use-case to be
- fairly easily reconfigured so that it can be used via different user
- interfaces. In a WAP interface for example, a new controller would be
- created with states defined at a lower level of granularity. The final
- state could then be reused to complete the use-case operation. </para>
-
-
-
- <para> Data collected through the wizard process may also be conveniently
- held in a ControllerForm which may be given request or session level
- scope. See the section on session data persistence for a detailed
- explanation. </para>
-
- <para>Note: Use-cases with no user input form should be coded as separate
- controllers containing only a final state. </para>
- </sect1>
-
- <sect1>
- <title>Controller & State Forms</title>
-
- <para> The new features provide for the concept of both a state and
- controller form. The controller form holds all the data required by the
- final state to complete the use-case. The state form holds only the data
- required by the individual states to perform their duties. Controller
- forms should inherit from <classname>com.jcorporate.expresso.core.controller.ControllerForm</classname>
- and state forms should implement the <classname>com.jcorporate.expresso.core.controller.StateForm</classname>
- interface. </para>
-
- <itemizedlist>
- <listitem>
- <para><emphasis role="bold">Controller form</emphasis>: The ActionForm
- defined in struts-config.xml for the given controller </para>
- </listitem>
-
- <listitem>
- <para><emphasis role="bold">State form</emphasis>: A bean that
- implements the StateForm interface </para>
- </listitem>
- </itemizedlist>
-
- <para> The state form defines the contract between the controller and the
- state. A state's form is specified using the addStatePairing() method
- as described below. It is populated from the controller form just prior to
- invoking the state. A new method has been defined within the State class
- called 'perform()'. This method replaces the existing run() method
- and should therefore be overridden to define state logic. This method has
- an additional parameter of type StateForm. Once the perform() has
- completed, the updated state form data is put back into the controller
- form. In a wizard setup, the attributes of the state form would be made up
- of a subset of the attributes on the controller form - this helps isolate
- the state from the other states in the controller. In a single screen
- controller, the state form would be the actual controller form. </para>
-
- <para>The controller form holds all the data required to perform the
- use-case. For wizard controllers, the form would normally be kept in
- session scope as defined in the Struts config file. In these controllers,
- the controller form will accumulate more data as the user progresses
- through the screens. In single screen situations, the controller form is
- used as the state form. </para>
-
- <para>Struts calls an ActionForm's reset() method every time it is
- populated from a user request. This would cause our data to be wiped out
- so the reset() method should not be overridden. Instead the
- resetController() method should be overridden. This method is only called
- when the initial state of a controller is invoked. The initial state is
- automatically defined when the controller's states are added in the
- controller constructor. </para>
- </sect1>
-
- <sect1>
- <title>Workflow</title>
-
-
-
- <para>The new features to support workflow capabilities revolve around the
- ability to direct execution between states and controllers. A new method,
- enableReturnToSender(), has been added to the Transition class and special
- Transition parameters can also be added to direct traffic. </para>
-
- <para>Calling the enableReturnToSender() method will indicate that the
- destination controller/state as specified in the transition object should
- return control back to this source state once the destination
- controller/state has completed without errors. If the target state is in a
- different controller and is also a wizard controller then the flow does
- not return to the source state until the successful completion of all the
- wizard screens. If the target state is in the current controller then flow
- returns to the source state after the target state has completed without
- errors. In either case, when the source state is eventually re-invoked, it
- will have its parameter context reestablished. </para>
-
- <para>An alternative to return-to-sender allows a transition to be defined
- with one of the following special workflow parameter pairs (as defined in
- the Controller class):</para>
-
- <para>CTL_SUCC_CTL and CTL_SUCC_STATE STATE_SUCC_CTL and STATE_SUCC_STATE
- STATE_ERR_CTL and STATE_ERR_STATE</para>
-
- <para>CTL_SUCC_XXXX - indicate to the target controller where it should
- transition to once the final state has completed successfully. This should
- always be used for transitions between different controllers.</para>
-
- <para>STATE_SUCC_XXXX - indicate to the target state where it should
- transition to if it completes successfully. This should always be used for
- transitions within a controller.</para>
-
- <para>STATE_ERR_XXXX - indicate to the target state where it should
- transition to if it completes with errors. This should always be used for
- transitions within a controller.</para>
-
- <para>The flow of execution can be defined at any or all of the following
- times:</para>
-
- <itemizedlist>
- <listitem>
- <para>Controller Definition time </para>
- </listitem>
-
- <listitem>
- <para>Screen Build time </para>
- </listitem>
-
- <listitem>
- <para>State Run time </para>
- </listitem>
- </itemizedlist>
-
- <para>The preferred approach is to define the flow at Controller
- Definition time. This keeps the mediation logic in the controller wherever
- possible. Placing it in the Screen Build time allows for more flexibility
- while keeping the flow logic outside of the state handlers. The last
- option allows a state to determine where to route to based on run time
- considerations that are only known by the state itself (use one of the
- first two options whenever possible). If flow is defined in more than one
- of these locations then the following priority is followed: </para>
-
- <orderedlist>
- <listitem>
- <para>State Run </para>
- </listitem>
-
- <listitem>
- <para>Screen Build</para>
- </listitem>
-
- <listitem>
- <para>Controller Definition</para>
- </listitem>
- </orderedlist>
- </sect1>
-
- <sect1>
- <title>Controller Definition</title>
-
- <sect2>
- <title>State default flow</title>
-
-
-
- <para>When using the new features, a controllerâs constructor should no
- longer call the addState() method. The new addStatePairing(State, State,
- String) and addFinalState(State) methods should be called instead.</para>
-
- <para>The addStatePairing() pairs up a handle state with its associated
- prompt state. This will automatically cause a prompt state to be invoked
- if any errors were generated in the associated handle state.</para>
-
- <para>The addFinalState() will be used by the Controller to identify
- when it needs to rerun all its handle states. This will occur
- automatically just prior to running the final state. This is needed in
- wizard screens to ensure that all data is still valid. If any errors are
- generated by a handle state then the associated prompt state will be
- invoked in order to display the error message on the appropriate screen.</para>
-
- <para>The sequencing of calls to addStatePairing() is significant. This
- defines the order of the screens in the controller. The Controller will
- add 'Previous' and 'Next' transition object(s) to all
- prompt statesâ responses. These transitions refer to either the previous
- prompt state (for 'Previous' buttons) or to a prompt stateâs
- associated handle state (for 'Next' or 'Done' buttons).
- If no errors are generated by the handle state then the next prompt
- state in the sequence is then invoked. </para>
- </sect2>
-
- <sect2>
- <title>Controller Security & Chaining flow</title>
-
- <para>The following two new attributes have been added to the Controller
- class: controllerChainingTransition and controllerSecurityTransition.
- Controller Chaining allows an application to specify the controller to
- be invoked once the current one completes. A controller completes once
- the final state has run without errors. Controller Security allows an
- application to specify the controller to be invoked if a user encounters
- controller authorization failure on a state in the current controller.
- Both of these new attributes are of type Transition, and if needed would
- normally be setup in the controllerâs constructor.</para>
- </sect2>
- </sect1>
-
- <sect1>
- <title>Screen Build</title>
-
- <para>A state normally adds transition objects to its response in order to
- be displayed as links or buttons on an HTML page. These transition objects
- can have either the special parameters set or can have the
- enableReturnToSender() method called before doing this. Enabling the
- return-to-sender would cause the target controller/state to re-invoke this
- source state so that it can redisplay the screen to the user.
- Alternatively the special parameters can be used to route to some other
- destination.</para>
- </sect1>
-
- <sect1>
- <title>State Run</title>
-
- <para>The State class now has two new attributes of type Transition:</para>
-
- <itemizedlist>
- <listitem>
- <para>successTransition </para>
- </listitem>
-
- <listitem>
- <para>errorTransition </para>
- </listitem>
- </itemizedlist>
-
- <para>These attributes can be set in the stateâs perform() method. Doing
- so would override any routing that was defined at the Controller
- Definition or Screen Build times. When a state completes its execution,
- these two attributes will be used to determine which transition to execute
- (if any). If the ErrorCollection is not empty then the errorTransition
- will be used otherwise the successTransition will be executed. This
- approach is similar to returning an ActionForward in Struts and so state
- logic should now create transition objects and set the required attributes
- but not call the transition() method. Rather, the newly created transition
- objects should be assigned to one of the two new attributes of the State
- class.</para>
- </sect1>
-
- <sect1>
- <title>Practice</title>
-
-
-
- <para> Basically, the workflow allows you to build a sequence of screens
- with Previous, Next and Finish buttons. Validation and error handling
- management to the correct states also occurs. A special transition called
- <function>enableReturnToSender()</function> allows for a wizard to
- essentially call another wizard and continue where it left off. </para>
-
-
-
- <para>An example of a state flow diagram:</para>
-
-
-
- <graphic fileref="../images/edg/WorkflowStateDiagram.gif" width="5in" />
-
-
-
- <para> There are three different types of states used in Expresso
- workflow: </para>
-
-
-
- <orderedlist>
-
-
- <listitem>
-
-
- <para> <emphasis role="bold">Prompt states</emphasis>: These are
- typically used to input user information. </para>
-
-
- </listitem>
-
-
-
- <listitem>
-
-
- <para> <emphasis role="bold">Handle (validate) states</emphasis>:
- These are used to validate the input entered in the prompt state.
- </para>
-
-
- </listitem>
-
-
-
- <listitem>
-
-
- <para> <emphasis role="bold">Final states</emphasis>: This is always
- the last state called in the wizard and usually is responsible for
- performing the final actions on the data collected in previous states.
- </para>
-
-
- </listitem>
-
-
- </orderedlist>
-
-
-
- <para> The sequence of transitions between workflow states can be defined
- in three different places, namely: </para>
-
-
-
- <orderedlist>
-
-
- <listitem>
-
-
- <para> <emphasis role="bold">In the Controller (preferred)</emphasis>:
- The state sequence is defined in the Controller object, externally to
- the states. </para>
-
-
- </listitem>
-
-
-
- <listitem>
-
-
- <para> <emphasis role="bold">At screen build time</emphasis>: The
- state sequence is defined at screen build time. </para>
-
-
- </listitem>
-
-
-
- <listitem>
-
-
- <para> <emphasis role="bold">At runtime</emphasis>: The state itself
- can decide the sequence. This is not the preferred approach as
- remember we are trying to build re-usable states here. </para>
-
-
- </listitem>
-
-
- </orderedlist>
-
-
-
- <sect2>
-
-
- <title>Defining the workflow in the Controller</title>
-
-
-
- <para> Prompt states are paired off with handle (validate) states using
- the addStatePairing() function. The sequence in which the
- addStatePairing() function is called essentially defines the state
- transition sequence or routing. </para>
-
-
-
- <para> For example the following code in the Controller constructor will
- create three wizard screens/states that can be navigated backwards and
- forwards from state 1 through 3. </para>
-
-
-
- <informalexample>
-
-
- <programlisting>addStatePairing(promptState1, handleState1, null); addStatePairing(promptState2, handleState2, null); addStatePairing(promptState3, handleState3, null); addFinalState(handleFinalState);</programlisting>
-
-
- </informalexample>
-
-
-
- <para> It is not possible to achieve sequences other than a simple
- linear sequence using a controller definition. If your wizard requires
- multiple paths, then you need to use either "screen build time"
- or "runtime" transitions. </para>
-
-
-
- <para> Errors defined in the handle states will cause a return to the
- prompt state with appropriate error. </para>
-
-
-
- <para> A special transition can be used to route all security
- authorization failures to a new state (for example a login state).
- </para>
-
-
-
- <programlisting> <function>setControllerSecurityTransition(oneTransition);</function> </programlisting>
-
-
-
- <para>or</para>
-
-
-
- <programlisting> <function>setControllerChainingTransition(oneTransition);</function> </programlisting>
-
-
-
- <para></para>
-
-
-
- <para> By setting the return-to-sender it is possible to return to the
- original place in the first wizard once the called wizard completes.
- </para>
-
-
-
- <programlisting> <function>oneTransition.enableReturnToSender(null);</function> </programlisting>
-
-
- </sect2>
-
-
-
- <sect2>
-
-
- <title>Defining the workflow at screen build time</title>
-
-
-
- <para> Divergent transitions can be coded into the prompt states to show
- additional buttons for state paths. These transitions may also be
- defined using the <function>enableReturnToSender()</function> .
- Obviously these transitions are built into the State code and will
- appear in all wizards that choose to reuse the state code, and hence are
- not as favorable as Controller definitions. </para>
-
-
- </sect2>
-
-
-
- <sect2>
-
-
- <title>Defining workflow at runtime</title>
-
-
-
- <para> In this case the transition is coded into the handle states and
- usually depends on testing of some user input. For example: </para>
-
-
-
- <informalexample>
-
-
- <programlisting>if (addDeviceForm.getSMSCarrier().equals("MTS")) { oneTransition = new Transition(); oneTransition.setState("prompt1"); oneTransition.setControllerObject("com.xyz.controller.ActivateMTS"); oneTransition.enableReturnToSender(response); setErrorTransition(oneTransition); response.addError("This message will be displayed on ActivateMTS"); }</programlisting>
-
-
- </informalexample>
-
-
-
- <para> This piece of code will cause the wizard to branch off to the
- com.xyz.controller.ActivateMTS controller if the form field SMScarrier
- in the StateForm is equal to "MTS". </para>
-
-
- </sect2>
-
-
-
- <sect2>
-
-
- <title>Workflow sample code</title>
-
-
-
- <para> This sample code uses a ControllerForm to store user input data
- for the session. The ControllerForm bean is defined as follows: </para>
-
-
-
- <informalexample>
-
-
- <programlisting>// // Standard form bean with getter and setter methods // This form is listed in struts-config.xml as a Struts ActionForm // and associated with the AddDevice.do action // public class AddDeviceForm extends ControllerForm { private String smsCarrier; public void setSMSCarrier(String val) { smsCarrier = val; } // This method is only called upon entry to a controller so this is // where the wizard values can all be reset public void resetController() { setSMSCarrier(null); } }</programlisting>
-
-
- </informalexample>
-
-
-
- <para> This bean has to be defined in the struts-config.xml file as
- follows: </para>
-
-
-
- <informalexample>
-
-
- <programlisting><form-beans> <form-bean name="addDeviceForm" type="com.xyz.controller.AddDeviceForm"/> </form-beans></programlisting>
-
-
- </informalexample>
-
-
-
- <para>Here's the Controller code:</para>
-
-
-
- <informalexample>
-
-
- <programlisting>// // Here the 'use-case' is AddDevice // public class AddDevice extends Controller { public AddDevice() throws NonHandleableException { super(); State promptState = null; State handleState = null; promptState = new PromptAddDevice("prompt1", "Add Device"); handleState = new ValidateAddDevice("handle1", "Add Device"); // Null in addStatePairing() indicates that a controller form // should be used addStatePairing(promptState, handleState, null); // More addStatePairing calls could go here if ever the AddDevice // function needs to split into multiple screens (ie wizard). handleState = new CompleteAddDevice("handleFinal", "Save"); addFinalState(handleState); // The next few lines will cause controller authorization failures // to route to the SignIn controller. // The return-to-sender is activated to return back to // the State that caused the authorization failure. Transition oneTransition = new Transition(); oneTransition.setState("prompt1"); oneTransition.setControllerObject("com.xyz.controller.SignIn"); try { // Will cause return to AddDevice on SignIn completed oneTransition.enableReturnToSender(null); } catch (ControllerException ce) { throw new NonHandleableException(ce); } setControllerSecurityTransition(oneTransition); // To use controller chaining call: // setControllerChainingTransition(oneTransition) } }</programlisting>
-
-
- </informalexample>
-
-
-
- <para> The controller also has to be registered in struts-config.xml as
- follows: </para>
-
-
-
- <informalexample>
-
-
- <programlisting><action path="/AddDevice" type="com.xyz.controller.AddDevice" name="addDeviceForm" scope="session" validate="false"> </action></programlisting>
-
-
- </informalexample>
-
-
-
- <para> Note that the action name refers to the form bean
- (ControllerForm) we defined above, and that the scope is set to
- 'session'. The session scope will keep the values stored in the
- ControllerForm intact for the duration of the session across all states.
- Setting scope='request' will only keep the form bean values
- valid across a single prompt/handle state transition. </para>
-
-
-
- <para> Note also that there are no action forwards defined in our
- example so all views will be rendered by the default view handler.
- </para>
-
-
-
- <para> [Authors note: It would be nice to have and example of a JSP that
- implements the action forwards] </para>
-
-
-
- <para>Code for the sample prompt state:</para>
-
-
-
- <informalexample>
-
-
- <programlisting>public class PromptAddDevice extends State { // // The 'perform' method is overridden to provide State logic. // // The StateForm class is a new interface for the new features. // public void perform(StateForm stateForm, ControllerRequest request, ControllerResponse response) throws ControllerException, NonHandleableException { super.perform(stateForm, request, response); AddDeviceForm addDeviceForm = (AddDeviceForm) stateForm; // Add Blocks, Inputs, Outputs, Transitions here...standard stuff. // Use stateForm to populate controller elements. Input I = new Input("SMSCarrier"); Reponse.addInput(I); // This will add a link/button on the AddDevice page that will take // user to AuthorizeDevice page and when that controller completes // normally, flow returns to this prompt state again. Transition oneTransition = new Transition(); oneTransition.setState("prompt1"); oneTransition.setControllerObject("com.xyz.controller.AuthorizeDevice"); oneTransition.setName("promptAuthorizeDevice"); oneTransition.setLabel("Authorize Device"); oneTransition.enableReturnToSender(response); // To flow to another state after AuthorizeDevice is completed replace // the enableReturnToSender(response) with: // oneTransition.addParam(Controller.CTL_SUCC_CTL, // "com.xyz.controller.AnotherState"); // oneTransition.addParam(Controller.CTL_SUCC_STATE, "prompt1"); } }</programlisting>
-
-
- </informalexample>
-
-
-
- <para> Note that when populating a response with input fields that will
- store values in the ControllerForm. The exact name of the bean property
- must be used, in our case SMSCarrier . This way the default renderer
- will automatically fill in the ControllerForm from the Input data
- entered by the user. </para>
-
-
-
- <para> This code also shows how to use screen time build transitions.
- </para>
-
-
-
- <para>Code for the sample handle state:</para>
-
-
-
- <informalexample>
-
-
- <programlisting>public class ValidateAddDevice extends State { // // Validate the form. // If any errors are generated here then by default the // associated promptState will be called. This can be overridden for // certain runtime conditions/errors. // public void perform(StateForm stateForm, ControllerRequest request, ControllerResponse response) throws ControllerException, NonHandleableException { super.perform(stateForm, request, response); AddDeviceForm addDeviceForm = (AddDeviceForm) stateForm; //Example of 'State Run' routing if (addDeviceForm.getSMSCarrier().equals("MTS")) { oneTransition = new Transition(); oneTransition.setState("prompt1"); oneTransition.setControllerObject("com.xyz.controller.ActivateMTS"); oneTransition.enableReturnToSender(response); //If no msg/error is added then use setSuccessTransition(oneTransition) setErrorTransition(oneTransition); response.addError("This message will be displayed on ActivateMTS"); } else { //This will route back to prompt state so user can correct input response.addError("Invalid Carrier."); } } }</programlisting>
-
-
- </informalexample>
-
-
-
- <para>And lastly the final state</para>
-
-
-
- <informalexample>
-
-
- <programlisting>public class CompleteAddDevice extends State { public void perform(StateForm stateForm, ControllerRequest request, ControllerResponse response) throws ControllerException, NonHandleableException { super.perform(stateForm, request, response); // This is the final state so use the controller form AddDeviceForm controllerForm = (AddDeviceForm) stateForm; // Perform any database updates here } }</programlisting>
-
-
- </informalexample>
-
-
- </sect2>
- </sect1>
-
- <sect1>
- <title>Conclusion</title>
-
-
-
- <sect2> <title>Contributors</title> <para> The following persons have
- contributed their time to this chapter: <itemizedlist mark="bullet">
- <listitem> <para>Aime Bazin </para> </listitem><listitem><para>Anonymous
- Contributor</para></listitem> <listitem> <para> David Lloyd <link
- linkend="jgroup">(JGroup Expert)</link> </para> </listitem><listitem><para>Sandra
- Cann</para></listitem> </itemizedlist> </para>
-
-
-
- <para> <note> <para id="donate_workflow"> Was this EDG documentation
- helpful? Do you wish to express your appreciation for the time expended
- over years developing the EDG doc? We now accept and appreciate monetary
- donations. Your support will keep the EDG doc alive and current. Please
- click the Donate button and enter ANY amount you think the EDG doc is
- worth. In appreciation of a $35+ donation, we'll give you a
- subscription service by emailing you notifications of doc updates; and
- donations $75+ will also receive an Expresso T-shirt. All online donation
- forms are SSL secured and payment can be made via Credit Card or your
- Paypal account. Thank you in advance. </para> <para> <ulink
- url="http://www.jcorporate.com/edgdoc.html">
- <inlinemediaobject> <imageobject> <imagedata
- fileref="../images/edg/paypal.bmp" format="BMP"></imagedata>
- </imageobject> </inlinemediaobject> </ulink> </para> </note> </para>
-
-
-
- <para> Copyright © 2001-2004 Jcorporate Ltd. All rights reserved.</para>
- </sect2>
- </sect1>
-</chapter>
\ No newline at end of file
+<chapter id='workflow' xreflabel='Workflow'>
+ <title>Expresso Workflow</title>
+ <para>
+ <note>
+ <para>
+If you find this EDG documentation helpful please consider <link linkend='donate_workflow'>DONATING</link>!
+to keep the doc alive and current.
+ </para>
+ </note>
+ </para>
+ <para>
+ <informaltable colsep='0' frame='none' pgwide='1' rowsep='0'>
+ <tgroup cols='2' colsep='0' rowsep='0'>
+ <colspec align='left' colsep='0' colwidth='50%' rowsep='0' />
+ <colspec align='right' colsep='0' colwidth='50%' rowsep='0' />
+ <tbody>
+ <row>
+ <entry><emphasis role='bold'>Version:</emphasis>
+
+<emphasis role='version'>Expresso 5.5</emphasis>
+ </entry>
+ <entry>
+<emphasis role='bold'>Maintainer:</emphasis><ulink url='mailto:dlloyd at jgroup.net?Subject=EDG'><emphasis
+role='maintainer'>Aime Bazin</emphasis></ulink>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ <remark>
+<emphasis role='bold'> Workflow was added to Expresso to both facilitate
+and encourage a certain approach to developing screen flow logic. This
+approach expects controller objects to only perform ‘controller’ functions
+as described in the MVC design pattern. </emphasis>
+ </remark>
+ <sidebar>
+ <para>
+<emphasis role='bold'>Warning:</emphasis> The workflow functionality in
+Expresso is still an alpha-quality feature. A few users have developed
+and use this feature, but it has not been tested well and may contain some
+bugs.
+ </para>
+ </sidebar>
+ <sect1>
+ <title>Introduction</title>
+ <para>
+The following new features were added to Expresso to both facilitate and
+encourage a certain approach to developing screen flow logic. This approach
+expects controller objects to only perform ‘controller’ functions as described
+in the MVC design pattern. This should limit the controller to ‘adapt’
+and ‘mediate’ type of actions. Thanks to the Controller class’ support
+for the HTTP protocol, there should be little need for web applications
+to provide their own ‘adapting’ code. This is because Expresso provides
+abstractions such as ControllerRequest and PersistentSession objects that
+have already done the adapting work between the HTTP and Java worlds.
+ </para>
+ </sect1>
+ <sect1>
+ <title>Theory</title>
+ <para>
+In addition to being able to use transitions to transfer control between
+states and controllers, Expresso offers a basic form of workflow and allows
+for quite simple linear flows between states in a wizard like fashion.
+Each workflow state may also be re-useable in other controllers. This means
+that the workflow through the controller is defined at a higher level and
+not in the states themselves.
+ </para>
+ <para>
+This approach expects controller objects to only perform 'controller' functions
+as described in the MVC design pattern. This should limit the controller
+to 'adapt' and 'mediate' type of actions. Thanks to the Controller class'
+support for the HTTP protocol, there should be little need for web applications
+to provide their own 'adapting' code. This is because Expresso provides
+abstractions such as ControllerRequest and PersistentSession objects that
+have already done the adapting work between the HTTP and Java worlds.
+ </para>
+ <para>
+Most of the code required in Controller descendant classes should be solely
+for the purpose of providing ‘mediation’ logic. This is the logic that
+ties components of the application together. The new features that have
+been added to the Controller class encourage developers to keep this mediation
+logic inside controller objects by providing support for mediation between
+state objects. This should help to avoid mediation code finding its way
+into state classes.
+ </para>
+ <para>
+Keeping the mediation code away from state classes allows these objects
+to focus on 'model' functions as described in MVC. In particular the state
+classes are 'commands' as defined in the Command design pattern. The state
+classes accept any required input via a StateForm at the time they are
+invoked, perform their 'business' logic and return any results via the
+state's form and/or the ControllerResponse object. If the business logic
+that needs to be performed is fairly complex then the state should act
+as a type of adapter. This is adapting between a user interaction and the
+classes used to support that user request (note: this is NOT like the adapting
+role of the Controller from HTTP to Java). The state would be responsible
+to take the single user request and adapt it into many calls onto supporting
+custom business 'model' objects. The state class (and any supporting model
+objects) should rarely include any mediation or workflow logic otherwise
+this reduces the potential for reuse in different application configurations.
+Therefore a properly coded state will be ignorant about who called it as
+well as who it should call once complete.
+ </para>
+ <para>
+The workflow features were designed with the following intent: A controller
+should relate to a 'use-case'. This is a single business function such
+as 'Register to a website' or 'Add a new widget to the inventory'. These
+use cases could comprise either a single user interaction or many user
+interactions/screens. A state relates to a single user interaction such
+as 'Enter address information' where the address information fits on a
+single screen (Note: since we kept mediation logic out of the state, this
+same state could be used both within a wizard-style registration and as
+part of a stand-alone screen to revise address information).
+ </para>
+ <para>
+
+When a controller is coded, states are added to the controller in one of
+3 ways:
+ <itemizedlist>
+ <listitem>
+ <para>As a 'prompt' state</para>
+ </listitem>
+ <listitem>
+ <para>As a 'handle' state</para>
+ </listitem>
+ <listitem>
+ <para>As a 'final' state</para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+Prompt states are those which build the contents of a screen using Input,
+Output, Block and Transition objects. The Controller will add its own transition
+objects based on the 'position' of the prompt state within the controller
+these are the Next and Previous buttons common in wizards. When errors
+are generated in a prompt state's associated handle state, the prompt state
+will be invoked by default assuming the handle state has not overridden
+this at runtime.
+ </para>
+ <para>
+
+Handle states accept user input from a state form and usually perform validation
+on that data. These states are normally directly invoked as a result of
+a user action however they will also be invoked as a result of a 'final'
+state execution. Restricting these states to validation-type logic allows
+them to be reused more easily (eg Within a wizard or stand-alone). Even
+in a single-screen controller it is a good practice to separate the validation
+from the commit processing using a final state.
+ <note>
+ <para>
+Keeping the handle state ignorant of its associated prompt state allows
+it to be reused more readily. This can be useful for example, when automating
+a number of user tasks in a batch or proxy mode.
+ </para>
+ </note>
+ </para>
+ <para>
+Final states are the last state to be invoked in a controller. They usually
+contain the guts of the processing involved in a use-case. This could include
+database updates or any other processing that needs to occur once all validation
+has succeeded. Before a final state is executed, all handle states within
+that controller are rerun and any validation errors cause the appropriate
+prompt state to be redisplayed. This is useful for wizard-style screen
+flow.
+ </para>
+ <para>
+Structuring your states in this fashion allows a use-case to be fairly
+easily reconfigured so that it can be used via different user interfaces.
+In a WAP interface for example, a new controller would be created with
+states defined at a lower level of granularity. The final state could then
+be reused to complete the use-case operation.
+ </para>
+ <para>
+Data collected through the wizard process may also be conveniently held
+in a ControllerForm which may be given request or session level scope.
+See the section on session data persistence for a detailed explanation.
+ </para>
+ <para>
+Note: Use-cases with no user input form should be coded as separate controllers
+containing only a final state.
+ </para>
+ </sect1>
+ <sect1>
+ <title>Controller & State Forms</title>
+ <para>
+
+The new features provide for the concept of both a state and controller
+form. The controller form holds all the data required by the final state
+to complete the use-case. The state form holds only the data required by
+the individual states to perform their duties. Controller forms should
+inherit from
+ <classname>com.jcorporate.expresso.core.controller.ControllerForm</classname>
+and state forms should implement the
+ <classname>com.jcorporate.expresso.core.controller.StateForm</classname>
+interface. </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+<emphasis role='bold'>Controller form</emphasis>: The ActionForm defined
+in struts-config.xml for the given controller
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<emphasis role='bold'>State form</emphasis>: A bean that implements the
+StateForm interface
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+The state form defines the contract between the controller and the state.
+A state's form is specified using the addStatePairing() method as described
+below. It is populated from the controller form just prior to invoking
+the state. A new method has been defined within the State class called
+'perform()'. This method replaces the existing run() method and should
+therefore be overridden to define state logic. This method has an additional
+parameter of type StateForm. Once the perform() has completed, the updated
+state form data is put back into the controller form. In a wizard setup,
+the attributes of the state form would be made up of a subset of the attributes
+on the controller form - this helps isolate the state from the other states
+in the controller. In a single screen controller, the state form would
+be the actual controller form.
+ </para>
+ <para>
+The controller form holds all the data required to perform the use-case.
+For wizard controllers, the form would normally be kept in session scope
+as defined in the Struts config file. In these controllers, the controller
+form will accumulate more data as the user progresses through the screens.
+In single screen situations, the controller form is used as the state form.
+ </para>
+ <para>
+Struts calls an ActionForm's reset() method every time it is populated
+from a user request. This would cause our data to be wiped out so the reset()
+method should not be overridden. Instead the resetController() method should
+be overridden. This method is only called when the initial state of a controller
+is invoked. The initial state is automatically defined when the controller's
+states are added in the controller constructor.
+ </para>
+ </sect1>
+ <sect1>
+ <title>Workflow</title>
+ <para>
+The new features to support workflow capabilities revolve around the ability
+to direct execution between states and controllers. A new method, enableReturnToSender(),
+has been added to the Transition class and special Transition parameters
+can also be added to direct traffic.
+ </para>
+ <para>
+Calling the enableReturnToSender() method will indicate that the destination
+controller/state as specified in the transition object should return control
+back to this source state once the destination controller/state has completed
+without errors. If the target state is in a different controller and is
+also a wizard controller then the flow does not return to the source state
+until the successful completion of all the wizard screens. If the target
+state is in the current controller then flow returns to the source state
+after the target state has completed without errors. In either case, when
+the source state is eventually re-invoked, it will have its parameter context
+reestablished.
+ </para>
+ <para>
+An alternative to return-to-sender allows a transition to be defined with
+one of the following special workflow parameter pairs (as defined in the
+Controller class):
+ </para>
+ <para>
+CTL_SUCC_CTL and CTL_SUCC_STATE STATE_SUCC_CTL and STATE_SUCC_STATE STATE_ERR_CTL
+and STATE_ERR_STATE
+ </para>
+ <para>
+CTL_SUCC_XXXX - indicate to the target controller where it should transition
+to once the final state has completed successfully. This should always
+be used for transitions between different controllers.
+ </para>
+ <para>
+STATE_SUCC_XXXX - indicate to the target state where it should transition
+to if it completes successfully. This should always be used for transitions
+within a controller.
+ </para>
+ <para>
+STATE_ERR_XXXX - indicate to the target state where it should transition
+to if it completes with errors. This should always be used for transitions
+within a controller.
+ </para>
+ <para>
+The flow of execution can be defined at any or all of the following times:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>Controller Definition time</para>
+ </listitem>
+ <listitem>
+ <para>Screen Build time</para>
+ </listitem>
+ <listitem>
+ <para>State Run time</para>
+ </listitem>
+ </itemizedlist>
+ <para>
+The preferred approach is to define the flow at Controller Definition time.
+This keeps the mediation logic in the controller wherever possible. Placing
+it in the Screen Build time allows for more flexibility while keeping the
+flow logic outside of the state handlers. The last option allows a state
+to determine where to route to based on run time considerations that are
+only known by the state itself (use one of the first two options whenever
+possible). If flow is defined in more than one of these locations then
+the following priority is followed:
+ </para>
+ <orderedlist>
+ <listitem>
+ <para>State Run</para>
+ </listitem>
+ <listitem>
+ <para>Screen Build</para>
+ </listitem>
+ <listitem>
+ <para>Controller Definition</para>
+ </listitem>
+ </orderedlist>
+ </sect1>
+ <sect1>
+ <title>Controller Definition</title>
+ <sect2>
+ <title>State default flow</title>
+ <para>
+When using the new features, a controller’s constructor should no longer
+call the addState() method. The new addStatePairing(State, State, String)
+and addFinalState(State) methods should be called instead.
+ </para>
+ <para>
+The addStatePairing() pairs up a handle state with its associated prompt
+state. This will automatically cause a prompt state to be invoked if any
+errors were generated in the associated handle state.
+ </para>
+ <para>
+The addFinalState() will be used by the Controller to identify when it
+needs to rerun all its handle states. This will occur automatically just
+prior to running the final state. This is needed in wizard screens to ensure
+that all data is still valid. If any errors are generated by a handle state
+then the associated prompt state will be invoked in order to display the
+error message on the appropriate screen.
+ </para>
+ <para>
+The sequencing of calls to addStatePairing() is significant. This defines
+the order of the screens in the controller. The Controller will add 'Previous'
+and 'Next' transition object(s) to all prompt states’ responses. These
+transitions refer to either the previous prompt state (for 'Previous' buttons)
+or to a prompt state’s associated handle state (for 'Next' or 'Done' buttons).
+If no errors are generated by the handle state then the next prompt state
+in the sequence is then invoked.
+ </para>
+ </sect2>
+ <sect2>
+ <title>Controller Security & Chaining flow</title>
+ <para>
+The following two new attributes have been added to the Controller class:
+controllerChainingTransition and controllerSecurityTransition. Controller
+Chaining allows an application to specify the controller to be invoked
+once the current one completes. A controller completes once the final state
+has run without errors. Controller Security allows an application to specify
+the controller to be invoked if a user encounters controller authorization
+failure on a state in the current controller. Both of these new attributes
+are of type Transition, and if needed would normally be setup in the controller’s
+constructor.
+ </para>
+ </sect2>
+ </sect1>
+ <sect1>
+ <title>Screen Build</title>
+ <para>
+A state normally adds transition objects to its response in order to be
+displayed as links or buttons on an HTML page. These transition objects
+can have either the special parameters set or can have the enableReturnToSender()
+method called before doing this. Enabling the return-to-sender would cause
+the target controller/state to re-invoke this source state so that it can
+redisplay the screen to the user. Alternatively the special parameters
+can be used to route to some other destination.
+ </para>
+ </sect1>
+ <sect1>
+ <title>State Run</title>
+ <para>
+The State class now has two new attributes of type Transition:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>successTransition</para>
+ </listitem>
+ <listitem>
+ <para>errorTransition</para>
+ </listitem>
+ </itemizedlist>
+ <para>
+These attributes can be set in the state’s perform() method. Doing so would
+override any routing that was defined at the Controller Definition or Screen
+Build times. When a state completes its execution, these two attributes
+will be used to determine which transition to execute (if any). If the
+ErrorCollection is not empty then the errorTransition will be used otherwise
+the successTransition will be executed. This approach is similar to returning
+an ActionForward in Struts and so state logic should now create transition
+objects and set the required attributes but not call the transition() method.
+Rather, the newly created transition objects should be assigned to one
+of the two new attributes of the State class.
+ </para>
+ </sect1>
+ <sect1>
+ <title>Practice</title>
+ <para>
+
+Basically, the workflow allows you to build a sequence of screens with
+Previous, Next and Finish buttons. Validation and error handling management
+to the correct states also occurs. A special transition called
+ <function>enableReturnToSender()</function>
+
+allows for a wizard to essentially call another wizard and continue where
+it left off.
+ </para>
+ <para>An example of a state flow diagram:</para>
+ <graphic fileref='../images/edg/WorkflowStateDiagram.gif' width='5in' />
+ <para>
+There are three different types of states used in Expresso workflow:
+ </para>
+ <orderedlist>
+ <listitem>
+ <para>
+<emphasis role='bold'>Prompt states</emphasis>: These are typically used
+to input user information.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<emphasis role='bold'>Handle (validate) states</emphasis>: These are used
+to validate the input entered in the prompt state.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<emphasis role='bold'>Final states</emphasis>: This is always the last
+state called in the wizard and usually is responsible for performing the
+final actions on the data collected in previous states.
+ </para>
+ </listitem>
+ </orderedlist>
+ <para>
+The sequence of transitions between workflow states can be defined in three
+different places, namely:
+ </para>
+ <orderedlist>
+ <listitem>
+ <para>
+<emphasis role='bold'>In the Controller (preferred)</emphasis>: The state
+sequence is defined in the Controller object, externally to the states.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<emphasis role='bold'>At screen build time</emphasis>: The state sequence
+is defined at screen build time.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<emphasis role='bold'>At runtime</emphasis>: The state itself can decide
+the sequence. This is not the preferred approach as remember we are trying
+to build re-usable states here.
+ </para>
+ </listitem>
+ </orderedlist>
+ <sect2>
+ <title>Defining the workflow in the Controller</title>
+ <para>
+Prompt states are paired off with handle (validate) states using the addStatePairing()
+function. The sequence in which the addStatePairing() function is called
+essentially defines the state transition sequence or routing.
+ </para>
+ <para>
+For example the following code in the Controller constructor will create
+three wizard screens/states that can be navigated backwards and forwards
+from state 1 through 3.
+ </para>
+ <informalexample>
+ <programlisting><![CDATA[addStatePairing(promptState1, handleState1, null); addStatePairing(promptState2, handleState2, null); addStatePairing(promptState3, handleState3, null); addFinalState(handleFinalState);]]></programlisting>
+ </informalexample>
+ <para>
+It is not possible to achieve sequences other than a simple linear sequence
+using a controller definition. If your wizard requires multiple paths,
+then you need to use either "screen build time" or "runtime" transitions.
+ </para>
+ <para>
+Errors defined in the handle states will cause a return to the prompt state
+with appropriate error.
+ </para>
+ <para>
+A special transition can be used to route all security authorization failures
+to a new state (for example a login state).
+ </para>
+ <programlisting>
+<function>setControllerSecurityTransition(oneTransition);</function>
+</programlisting>
+ <para>or</para>
+ <programlisting>
+<function>setControllerChainingTransition(oneTransition);</function>
+</programlisting>
+ <para />
+ <para>
+By setting the return-to-sender it is possible to return to the original
+place in the first wizard once the called wizard completes.
+ </para>
+ <programlisting>
+<function>oneTransition.enableReturnToSender(null);</function>
+</programlisting>
+ </sect2>
+ <sect2>
+ <title>Defining the workflow at screen build time</title>
+ <para>
+
+Divergent transitions can be coded into the prompt states to show additional
+buttons for state paths. These transitions may also be defined using the
+ <function>enableReturnToSender()</function>
+
+. Obviously these transitions are built into the State code and will appear
+in all wizards that choose to reuse the state code, and hence are not as
+favorable as Controller definitions.
+ </para>
+ </sect2>
+ <sect2>
+ <title>Defining workflow at runtime</title>
+ <para>
+In this case the transition is coded into the handle states and usually
+depends on testing of some user input. For example:
+ </para>
+ <informalexample>
+ <programlisting><![CDATA[if (addDeviceForm.getSMSCarrier().equals("MTS")) { oneTransition = new Transition(); oneTransition.setState("prompt1"); oneTransition.setControllerObject("com.xyz.controller.ActivateMTS"); oneTransition.enableReturnToSender(response); setErrorTransition(oneTransition); response.addError("This message will be displayed on ActivateMTS"); }]]></programlisting>
+ </informalexample>
+ <para>
+This piece of code will cause the wizard to branch off to the com.xyz.controller.ActivateMTS
+controller if the form field SMScarrier in the StateForm is equal to "MTS".
+ </para>
+ </sect2>
+ <sect2>
+ <title>Workflow sample code</title>
+ <para>
+This sample code uses a ControllerForm to store user input data for the
+session. The ControllerForm bean is defined as follows:
+ </para>
+ <informalexample>
+ <programlisting><![CDATA[// // Standard form bean with getter and setter methods // This form is listed in struts-config.xml as a Struts ActionForm // and associated with the AddDevice.do action // public class AddDeviceForm extends ControllerForm { private String smsCarrier; public void setSMSCarrier(String val) { smsCarrier = val; } // This method is only called upon entry to a controller so this is // where the wizard values can all be reset public void resetController() { setSMSCarrier(null); } }]]></programlisting>
+ </informalexample>
+ <para>
+This bean has to be defined in the struts-config.xml file as follows:
+ </para>
+ <informalexample>
+ <programlisting><![CDATA[<form-beans> <form-bean name="addDeviceForm" type="com.xyz.controller.AddDeviceForm"/> </form-beans>]]></programlisting>
+ </informalexample>
+ <para>Here's the Controller code:</para>
+ <informalexample>
+ <programlisting><![CDATA[// // Here the 'use-case' is AddDevice // public class AddDevice extends Controller { public AddDevice() throws NonHandleableException { super(); State promptState = null; State handleState = null; promptState = new PromptAddDevice("prompt1", "Add Device"); handleState = new ValidateAddDevice("handle1", "Add Device"); // Null in addStatePairing() indicates that a controller form // should be used addStatePairing(promptState, handleState, null); // More addStatePairing calls could go here if ever the AddDevice // function needs to split into multiple screens (ie wizard). handleState = new CompleteAddDevice("handleFinal", "Save"); addFinalState(handleState); // The next few lines will cause controller authorization failures // to route to the SignIn controller. // The return-to-sender is activated to return back to // the State that caused the authorization failure. Transition oneTransition = new Transition(); oneTransition.setState("prompt1"); oneTransition.setControllerObject("com.xyz.controller.SignIn"); try { // Will cause return to AddDevice on SignIn completed oneTransition.enableReturnToSender(null); } catch (ControllerException ce) { throw new NonHandleableException(ce); } setControllerSecurityTransition(oneTransition); // To use controller chaining call: // setControllerChainingTransition(oneTransition) } }]]></programlisting>
+ </informalexample>
+ <para>
+The controller also has to be registered in struts-config.xml as follows:
+ </para>
+ <informalexample>
+ <programlisting><![CDATA[<action path="/AddDevice" type="com.xyz.controller.AddDevice" name="addDeviceForm" scope="session" validate="false"> </action>]]></programlisting>
+ </informalexample>
+ <para>
+Note that the action name refers to the form bean (ControllerForm) we defined
+above, and that the scope is set to 'session'. The session scope will keep
+the values stored in the ControllerForm intact for the duration of the
+session across all states. Setting scope='request' will only keep the form
+bean values valid across a single prompt/handle state transition.
+ </para>
+ <para>
+Note also that there are no action forwards defined in our example so all
+views will be rendered by the default view handler.
+ </para>
+ <para>
+[Authors note: It would be nice to have and example of a JSP that implements
+the action forwards]
+ </para>
+ <para>Code for the sample prompt state:</para>
+ <informalexample>
+ <programlisting><![CDATA[public class PromptAddDevice extends State { // // The 'perform' method is overridden to provide State logic. // // The StateForm class is a new interface for the new features. // public void perform(StateForm stateForm, ControllerRequest request, ControllerResponse response) throws ControllerException, NonHandleableException { super.perform(stateForm, request, response); AddDeviceForm addDeviceForm = (AddDeviceForm) stateForm; // Add Blocks, Inputs, Outputs, Transitions here...standard stuff. // Use stateForm to populate controller elements. Input I = new Input("SMSCarrier"); Reponse.addInput(I); // This will add a link/button on the AddDevice page that will take // user to AuthorizeDevice page and when that controller completes // normally, flow returns to this prompt state again. Transition oneTransition = new Transition(); oneTransition.setState("prompt1"); oneTransition.setControllerObject("com.xyz.controller.AuthorizeDevice"); oneTransition.setName("promptAuthorizeDevice"); oneTransition.setLabel("Authorize Device"); oneTransition.enableReturnToSender(response); // To flow to another state after AuthorizeDevice is completed replace // the enableReturnToSender(response) with: // oneTransition.addParam(Controller.CTL_SUCC_CTL, // "com.xyz.controller.AnotherState"); // oneTransition.addParam(Controller.CTL_SUCC_STATE, "prompt1"); } }]]></programlisting>
+ </informalexample>
+ <para>
+Note that when populating a response with input fields that will store
+values in the ControllerForm. The exact name of the bean property must
+be used, in our case SMSCarrier . This way the default renderer will automatically
+fill in the ControllerForm from the Input data entered by the user.
+ </para>
+ <para>
+This code also shows how to use screen time build transitions.
+ </para>
+ <para>Code for the sample handle state:</para>
+ <informalexample>
+ <programlisting><![CDATA[public class ValidateAddDevice extends State { // // Validate the form. // If any errors are generated here then by default the // associated promptState will be called. This can be overridden for // certain runtime conditions/errors. // public void perform(StateForm stateForm, ControllerRequest request, ControllerResponse response) throws ControllerException, NonHandleableException { super.perform(stateForm, request, response); AddDeviceForm addDeviceForm = (AddDeviceForm) stateForm; //Example of 'State Run' routing if (addDeviceForm.getSMSCarrier().equals("MTS")) { oneTransition = new Transition(); oneTransition.setState("prompt1"); oneTransition.setControllerObject("com.xyz.controller.ActivateMTS"); oneTransition.enableReturnToSender(response); //If no msg/error is added then use setSuccessTransition(oneTransition) setErrorTransition(oneTransition); response.addError("This message will be displayed on ActivateMTS"); } else { //This will route back to prompt state so user can correct input response.addError("Invalid Carrier."); } } }]]></programlisting>
+ </informalexample>
+ <para>And lastly the final state</para>
+ <informalexample>
+ <programlisting><![CDATA[public class CompleteAddDevice extends State { public void perform(StateForm stateForm, ControllerRequest request, ControllerResponse response) throws ControllerException, NonHandleableException { super.perform(stateForm, request, response); // This is the final state so use the controller form AddDeviceForm controllerForm = (AddDeviceForm) stateForm; // Perform any database updates here } }]]></programlisting>
+ </informalexample>
+ </sect2>
+ </sect1>
+ <sect1>
+ <title>Conclusion</title>
+ <sect2>
+ <title>Contributors</title>
+ <para>
+
+The following persons have contributed their time to this chapter:
+ <itemizedlist mark='bullet'>
+ <listitem>
+ <para>Aime Bazin</para>
+ </listitem>
+ <listitem>
+ <para>Anonymous Contributor</para>
+ </listitem>
+ <listitem>
+ <para>
+David Lloyd <link linkend='jgroup'>(JGroup Expert)</link>
+ </para>
+ </listitem>
+ <listitem>
+ <para>Sandra Cann</para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+
+ <note>
+ <para id='donate_workflow'>
+Was this EDG documentation helpful? Do you wish to express your appreciation
+for the time expended over years developing the EDG doc? We now accept
+and appreciate monetary donations. Your support will keep the EDG doc alive
+and current. Please click the Donate button and enter ANY amount you think
+the EDG doc is worth. In appreciation of a $35+ donation, we'll give you
+a subscription service by emailing you notifications of doc updates; and
+donations $75+ will also receive an Expresso T-shirt. All online donation
+forms are SSL secured and payment can be made via Credit Card or your Paypal
+account. Thank you in advance.
+ </para>
+ <para>
+<ulink url='http://www.jcorporate.com/edgdoc.html'> <inlinemediaobject>
+<imageobject> <imagedata fileref='../images/edg/paypal.bmp' format='BMP'
+/> </imageobject> </inlinemediaobject> </ulink>
+ </para>
+ </note>
+ </para>
+ <para>
+Copyright © 2001-2004 Jcorporate Ltd. All rights reserved.
+ </para>
+ </sect2>
+ </sect1>
+</chapter>
Index: dbobject-types.xml
===================================================================
RCS file: /home/javacorp/.cvs/expresso/expresso/expresso-web/expresso/doc/edg/dbobject-types.xml,v
retrieving revision 1.17
retrieving revision 1.18
diff -Lexpresso-web/expresso/doc/edg/dbobject-types.xml -Lexpresso-web/expresso/doc/edg/dbobject-types.xml -u -r1.17 -r1.18
--- expresso-web/expresso/doc/edg/dbobject-types.xml
+++ expresso-web/expresso/doc/edg/dbobject-types.xml
@@ -46,8 +46,9 @@
<note>
<para>
As of Expresso 5.1, it is recommended that new developers work with JoinedDataObject
-due to its increased flexibility and speed over MultiDBObject. However, MultiDBObject will remain
- supported for the foreseeable future, since it is used quite a bit in existing applications.
+due to its increased flexibility and speed over MultiDBObject. However,
+MultiDBObject will remain supported for the foreseeable future, since it
+is used quite a bit in existing applications.
</para>
</note>
<para>
@@ -444,19 +445,20 @@
the foreign keys in an object, so referential integrity will not be destroyed)
</para>
</sect2>
- <sect2>
- <title>Using JoinedDigesterBean for polymorphism</title>
- <para>Question: I'm trying to make a JoinedDataObject within
-RowSecuredDBObject.searchAndRetrieveList() which will help do security
-checks w/i DB, rather than as N tests after getting the list. But RowSecuredDBObject is the superclass. The real join is on the subclass.
-In MultiDBObject, I can use getInstance(), but in the XML for
-JoinedDataObject, I think I'm out of luck.
- </para>
- <para>Answer:
- You can provide a populated JoinedDigesterBean instance that is
-built on the fly that gives you the query capabilities: </para>
-
-<programlisting><![CDATA[
+ <sect2>
+ <title>Using JoinedDigesterBean for polymorphism</title>
+ <para>
+Question: I'm trying to make a JoinedDataObject within RowSecuredDBObject.searchAndRetrieveList()
+which will help do security checks w/i DB, rather than as N tests after
+getting the list. But RowSecuredDBObject is the superclass. The real join
+is on the subclass. In MultiDBObject, I can use getInstance(), but in the
+XML for JoinedDataObject, I think I'm out of luck.
+ </para>
+ <para>
+Answer: You can provide a populated JoinedDigesterBean instance that is
+built on the fly that gives you the query capabilities:
+ </para>
+ <programlisting><![CDATA[
JoinedDigesterBean bean = new JoinedDigesterBean();
bean.setName("RowPermissions/" + this.getClass().getName());
bean.addDataObject(this.getClass().getName(),null,"class1");
@@ -466,33 +468,25 @@
JoinedDataObject jdo = new JoinedDataObject(bean, "RowPermissions/" +
this.getClass().getName());
]]></programlisting>
-
-<para>
-Because it's expensive to initialize metadata, each ad-hoc join will need a
-distinct name since the metadata is cached. However, after the initial
+ <para>
+Because it's expensive to initialize metadata, each ad-hoc join will need
+a distinct name since the metadata is cached. However, after the initial
build, then you get:
-</para>
-
-<programlisting><![CDATA[
+ </para>
+ <programlisting><![CDATA[
JoinedDataObject jdo = new JoinedDataObject("RowPermissions/" +
this.getClass().getName());
]]></programlisting>
-
-<para>
+ <para>
And you will get a JoinedDataObject with the definition previously defined.
-</para>
-
-<para>
-[Begin API Dreaming]
-In reality, what is really needed is a "JoinedDigesterBean Builder" in the
-form of a query specification object. It would be similar, but less
-powerful than the Hibernate "Criteria" API.
-
-
-Off the top of my head pseudocode:
-</para>
-
-<programlisting><![CDATA[
+ </para>
+ <para>
+[Begin API Dreaming] In reality, what is really needed is a "JoinedDigesterBean
+Builder" in the form of a query specification object. It would be similar,
+but less powerful than the Hibernate "Criteria" API. Off the top of my
+head pseudocode:
+ </para>
+ <programlisting><![CDATA[
QueryObject query = dataObject.buildQuery();
query = query.criteria(example(dbobject1)
@@ -522,18 +516,16 @@
query.close();
}
]]></programlisting>
-
-<para>
-Now, I think the only real problem with the above is that I don't think we
-embed enough relationship metadata within DBObjects to have the dataobject
-API automatically navigate the relations. So DataObject API metadata may
+ <para>
+Now, I think the only real problem with the above is that I don't think
+we embed enough relationship metadata within DBObjects to have the dataobject
+API automatically navigate the relations. So DataObject API metadata may
need some steroids. :)
-
-</para>
-</sect2>
+ </para>
+ </sect2>
</sect1>
<sect1>
- <title>MediaDBObject [Since Expresso 5.1]</title>
+ <title>MediaDBObject [Since Expresso 5.1]</title>
<indexterm>
<primary>MediaDBObject</primary>
<secondary>BLOB Fields</secondary>
@@ -808,16 +800,36 @@
<para>
The following classes extend SecurityDBObject in Expresso:
<itemizedlist>
- <listitem><para>ControllerSecurity</para></listitem>
- <listitem><para>DBObjSecurity</para></listitem>
- <listitem><para>DefaultUserInfo</para></listitem>
- <listitem><para>GroupMembers</para></listitem>
- <listitem><para>GroupNest</para></listitem>
- <listitem><para>JobSecurity</para></listitem>
- <listitem><para>TmpUser</para></listitem>
- <listitem><para>UserGroup</para></listitem>
- <listitem><para>UserPreference</para></listitem>
- <listitem><para>UserPreferenceDef</para></listitem>
+ <listitem>
+ <para>ControllerSecurity</para>
+ </listitem>
+ <listitem>
+ <para>DBObjSecurity</para>
+ </listitem>
+ <listitem>
+ <para>DefaultUserInfo</para>
+ </listitem>
+ <listitem>
+ <para>GroupMembers</para>
+ </listitem>
+ <listitem>
+ <para>GroupNest</para>
+ </listitem>
+ <listitem>
+ <para>JobSecurity</para>
+ </listitem>
+ <listitem>
+ <para>TmpUser</para>
+ </listitem>
+ <listitem>
+ <para>UserGroup</para>
+ </listitem>
+ <listitem>
+ <para>UserPreference</para>
+ </listitem>
+ <listitem>
+ <para>UserPreferenceDef</para>
+ </listitem>
</itemizedlist>
</para>
<para>
@@ -826,21 +838,25 @@
all of the classes listed above) via Setup values. Two Setup values are
important for SecurityDBObject:
<itemizedlist>
-
<listitem>
<para>
+
<indexterm>
<primary>SecurityDB</primary>
</indexterm>
- SecurityDB -- Database to use for User/Group Security Info
+
+SecurityDB -- Database to use for User/Group Security Info
</para>
</listitem>
<listitem>
<para>
+
<indexterm>
<primary>SecurityDBObjs</primary>
</indexterm>
- SecurityDBObjs -- Database Objects that should use the 'fixed' context found in the SecurityDB parameter
+
+SecurityDBObjs -- Database Objects that should use the 'fixed' context
+found in the SecurityDB parameter
</para>
</listitem>
</itemizedlist>
@@ -921,11 +937,10 @@
</listitem>
</itemizedlist>
</para>
-
- <para>
+ <para>
- <note>
- <para id='donate_dbobject-types'>
+ <note>
+ <para id='donate_dbobject-types'>
Was this EDG documentation helpful? Do you wish to express your appreciation
for the time expended over years developing the EDG doc? We now accept
and appreciate monetary donations. Your support will keep the EDG doc alive
@@ -935,17 +950,17 @@
donations $75+ will also receive an Expresso T-shirt. All online donation
forms are SSL secured and payment can be made via Credit Card or your Paypal
account. Thank you in advance.
- </para>
- <para>
+ </para>
+ <para>
<ulink url='http://www.jcorporate.com/edgdoc.html'> <inlinemediaobject>
<imageobject> <imagedata fileref='../images/edg/paypal.bmp' format='BMP'
/> </imageobject> </inlinemediaobject> </ulink>
- </para>
- </note>
- </para>
- <para>
+ </para>
+ </note>
+ </para>
+ <para>
Copyright © 2001-2004 Jcorporate Ltd. All rights reserved.
- </para>
+ </para>
</sect2>
</sect1>
</chapter>
Index: configuration.xml
===================================================================
RCS file: /home/javacorp/.cvs/expresso/expresso/expresso-web/expresso/doc/edg/configuration.xml,v
retrieving revision 1.26
retrieving revision 1.27
diff -Lexpresso-web/expresso/doc/edg/configuration.xml -Lexpresso-web/expresso/doc/edg/configuration.xml -u -r1.26 -r1.27
--- expresso-web/expresso/doc/edg/configuration.xml
+++ expresso-web/expresso/doc/edg/configuration.xml
@@ -1,991 +1,958 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<chapter id="configure" xreflabel="Configuration and Initialization">
- <title>Application Configuration and Initialization</title>
-
- <para><note>
- <para>If you find this EDG documentation helpful please consider <link
- linkend="donate_configure">DONATING</link>! to keep the doc alive and
- current.</para>
- </note></para>
-
- <para><informaltable colsep="0" frame="none" pgwide="1" rowsep="0">
- <tgroup cols="2" colsep="0" rowsep="0">
- <colspec align="left" colsep="0" colwidth="50%" rowsep="0" />
-
- <colspec align="right" colsep="0" colwidth="50%" rowsep="0" />
-
- <tbody>
- <row>
- <entry><emphasis role="bold">Version:</emphasis> <emphasis
- role="version">Expresso 5.5</emphasis></entry>
-
- <entry><emphasis role="bold">Maintainer:</emphasis><ulink
- url="mailto:dlloyd at jgroup.net?Subject=EDG"><emphasis
- role="maintainer">David Lloyd</emphasis></ulink></entry>
- </row>
- </tbody>
- </tgroup>
- </informaltable></para>
-
- <remark><emphasis role="bold"> Expresso contains a number of facilities
- designed to manage the setup and configuration data for an application, such
- as the information required to connect to the applications database (or
- databases if more than one), values required to connect to the email and
- other servers, and user-specific preferences. </emphasis></remark>
-
- <sect1>
- <title>Introduction</title>
-
- <para>Expresso provides various degress of control in both the framework
- and the applications, through both the schema objects and the
- configuration files written in XML. These configuration controls
- include:</para>
-
- <itemizedlist>
- <listitem>
- <para><emphasis role="bold">Schema objects</emphasis>: enables
- management of the security for all of the applications component
- objects</para>
- </listitem>
-
- <listitem>
- <para><emphasis role="bold">Configuration Manager</emphasis>: used for
- configuration information used by all applications running in a
- particular context</para>
- </listitem>
-
- <listitem>
- <para><emphasis role="bold">Setup Values</emphasis>: specific to a
- particular context, but are further specific to one application</para>
- </listitem>
-
- <listitem>
- <para><emphasis role="bold">User Preferences</emphasis>: finer level
- of detail that are specific to a particular user.</para>
- </listitem>
- </itemizedlist>
- </sect1>
-
- <sect1>
- <title>Steps to setup an Expresso Application</title>
-
- <para>There are 3 basic steps (and a few optional ones) in creating your
- own Expresso Application. They are:</para>
-
- <orderedlist>
- <listitem>
- <para>Create your Schema object</para>
- </listitem>
-
- <listitem>
- <para>Create your XML files in the config directory</para>
- </listitem>
-
- <listitem>
- <para>Register your Schema and create/intialize the
- schema/database</para>
- </listitem>
- </orderedlist>
-
- <sect2 id="expresso-config">
- <title>expresso-config.xml</title>
-
- <para>Expresso requires this configuration file in order to specify
- customizable settings controlling it's operation on your system. This
- file is called expresso-config.xml and it is found in the WEB-INF/config
- directory. This file contains the primary configuration information for
- Expresso. It is read during system startup, and it's information is
- available to any object in the application from the ConfigManager
- object.</para>
-
- <para>The expresso-config.xml configuration file supplies all of the
- information for each of the contexts set up with Expresso on your
- system, and provides information for each of those contexts to connect
- to their appropriate database engine(s), LDAP servers, and other core
- information.</para>
-
- <para>The expresso-config.xml that comes with Expresso is set up only
- for acces to the built-in Hypersonic databases (the "default" and "test"
- contexts). In order to add access to your own databases you must edit
- this file and either alter one of the existing contexts or add an
- entirely new context. See the documentation onsite for example jdbc
- configuration entities for various database types under the
- Database-specific Installation Notes header.</para>
-
- <para>The top-level element is the "expresso-config" object, which
- contains definitions that apply to the entire application. This
- top-level contains attributes and sub-elements that are applied to all
- contexts in Expresso. This includes properties like the log directory
- location where the application will write its log files, which are
- essential in tracking the application's operation.</para>
-
- <para>Another important section of the expresso-config file is the
- "class-handlers" section. This section specifies the classes that are
- used for various implementation classes within Expresso and it's
- applications. One of these classes, for example, is the "userInfo"
- implementation, which by default is supplied by the
- "com.jcorporate.expresso.services.dbobj.DefaultUserInfo" class.</para>
-
- <para>Specifying an alternate implementation: <programlisting><class-handlers>
- <class-handler name="userInfo" classHandler="com.jcorporate.expresso.services.dbobj.DefaultUserInfo"/>
-</class-handlers></programlisting></para>
-
- <para>There are several classhandler already defined in Expresso. Here
- are a few along with their default values: <table frame="all">
- <title>Pre-Defined Class Handlers</title>
-
- <tgroup align="center" cols="3" colsep="1" rowsep="1">
- <thead>
- <row>
- <entry>Class Handler Name</entry>
-
- <entry>Default Class</entry>
-
- <entry>Description</entry>
- </row>
- </thead>
-
- <tbody>
- <row>
- <entry>userInfo</entry>
-
- <entry>com.jcorporate.expresso.services.dbobj.DefaultUserInfo</entry>
-
- <entry><para> userInfo is the implementation of
- com.jcorporate.expresso.core.security.UserInfo. It is used for
- mapping login names to integer user id's and also is
- responsible for storing passwords and other information.
- </para></entry>
- </row>
-
- <row>
- <entry>orderedCache</entry>
-
- <entry>com.jcorporate.expresso.core.cache.OrderedCache</entry>
-
- <entry><para> Ordered cache specifies a cached list where
- order must be preserved. It is usually backed by some sort of
- java.util.List implementation. </para></entry>
- </row>
-
- <row>
- <entry>unOrderedCache</entry>
-
- <entry>com.jcorporate.expresso.core.cache.UnOrderedCache</entry>
-
- <entry><para> UnOrdered cache specifies a cached list where
- order must be preserved. It is usually backed by some sort of
- java.util.List implementation </para></entry>
- </row>
-
- <row>
- <entry>registration</entry>
-
- <entry>com.jcorporate.expresso.services.controller.SimpleRegistration</entry>
-
- <entry><para> Registration is a controller that allows for an
- end user registration process </para></entry>
- </row>
-
- <row>
- <entry>login</entry>
-
- <entry>com.jcorporate.expresso.services.dbobj.DefaultUserInfo</entry>
-
- <entry><para> login is a Controller that provides
- authentication capabilities for end users </para></entry>
- </row>
-
- <row>
- <entry>nextNumber</entry>
-
- <entry>com.jcorporate.expresso.core.dbobj.NextNumberImpl</entry>
-
- <entry><para></para></entry>
- </row>
-
- <row>
- <entry>cacheSynchronizer</entry>
-
- <entry>N/A</entry>
-
- <entry><para> cache Synchronizer is an object that
- communicates to remote machines that cache has been modified
- and remote caches should be cleared. Expresso itself does not
- contain a cacheSynchronizer component, but commercial add-ons
- are available through products such as Expresso Enterprise(tm)
- </para></entry>
- </row>
- </tbody>
- </tgroup>
- </table></para>
-
- <para>Nested within the "expresso-config" element are one or more
- "context" elements, with various nested elements within them. Settings
- with the context element only affect that one context. Each "context"
- element defines a different context of operation - typically including a
- separate database. To recap, context elements contain settings that are
- specific to one individual context. Another example is the JDBC
- sub-element, which specifies the connection parameters for
- DBConnectionPool to connect to your JDBC data source.</para>
-
- <para>Each context has a name, and a description - these are used when
- the context is displayed, for example, by the Login controller.</para>
-
- <sect3>
- <title>Format</title>
-
- <para>The format of the expresso-config.xml file is specified by a DTD
- (stored in
- WEB-INF/classes/com/jcorporate/expresso/core/expresso-config_5_5.dtd
- or a similar name for later versions), and the configuration file is
- validated against this DTD during startup of the system - any
- deviation from the expected format will cause a startup failure, and
- Expresso will likely not initialize. The expresso-config.xml file is
- organized into a hierarchy, as specified in the DTD file.</para>
-
- <para>It is important to check the DTD for the expresso-config.xml
- configuration file, and to read the comments in that DTD to understand
- all of the options available. There are extensive comments in the DTD
- file that provide the explanation for each setup option - this file
- will always be your best reference as to alterations of configuration
- values, as it is the final word on what the valid format for
- expresso-config.xml is.</para>
- </sect3>
-
- <sect3>
- <title>Startup Problems</title>
-
- <para>If you encounter startup problems with Expresso, the fault is
- quite possibly the expresso-config.xml file. In order to resolve the
- problems, examine the output in the expresso.log file in your
- specified log directory (by default WEB-INF/log). If there is no such
- file, then the log4j logging system is also not initializing - look
- for the standard error or standard output log for your servlet
- engine/application server. For Tomcat, for example, these log files
- would be in the logs subdirectory of a standard Tomcat
- installation.</para>
-
- <para>This log files should explain why the loading of the
- configuration is failing: if it is a DTD validation exception, the
- element causing the problem should have been specified - correct your
- expresso-config.xml file and try again.</para>
-
- <para>If the configuration file does not appear to be loading at all,
- make sure that the DefaultInit servlet is set to be run on system
- startup - this servlet is the one which actually calls the
- ConfigManager object, which performs the loading of the
- expresso-config.xml file itself.</para>
- </sect3>
- </sect2>
-
- <sect2>
- <title>struts-config.xml</title>
-
- <para>This file provides the mappings required by the Struts frameworks
- from URL's to "Action" objects - in this case, to the Controller objects
- in Expresso (which are sub-classes of the Action object.) See the Struts
- documentation included with Expresso for details of the format of this
- file. Ordinarily, you will not need to adjust this file at all.</para>
- </sect2>
-
- <sect2>
- <title>Other application-specific xxx-config.xml files</title>
-
- <para>In addition to the normal struts-config.xml file, applications
- that have their own Controller objects can provide mappings for them in
- separate configuration files. For example, eForum has an
- eforum-config.xml file that provides mappings for it's Controllers. In
- this way, the core struts-config.xml file for Expresso does not need to
- be adjusted as you install or develop other applications, and the
- configurations for own applications can be easily adjusted.</para>
- </sect2>
-
- <sect2>
- <title>Logging Configuration</title>
-
- <sect3>
- <title>Setting up Explicit Log4j Initialization</title>
-
- <para>Since Expresso 5.1, Expresso uses a unified logging
- configuration file that is initialized and utilized quite differently
- from previous versions.</para>
-
- <para>For servlet environments, it is a matter of setting the 'logDir'
- property in the Servlet context for your web.xml. Look for the
- configDir in existing installations and add the logDir parameter under
- that. This is the directory to expand expresso related macros in the
- log file. What the configuration system does with the logDir parameter
- is set the system property. 'expresso.logDir' for which log4j can
- expand upon. Example Snippet: <programlisting><?xml version="1.0"?>
-<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
- "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
-<web-app>
- <distributable/>
- <context-param>
- <param-name>configDir</param-name>
- <param-value>WEB-INF/config</param-value>
- </context-param>
- <context-param>
- <param-name>logDir</param-name>
- <param-value>WEB-INF/log</param-value>
- </context-param>
-....</programlisting></para>
-
- <para>If you are using a non-servlet environment, you need to call new
- LogManager(String loggingConfig, String logDirectory) to initialize
- the log4j system with the same kind of parameters.</para>
-
- <para>Step #2 is implementing the expressoLogging.xml file. For most
- of the public downloads, and example of this is already created. To do
- this, add the expresso.logDir macro to wherever you would normally put
- the logging directory name. <programlisting><appender name="expressoLog" class="org.apache.log4j.FileAppender">
- <param name="File" value="${expresso.logDir}/expresso.log"/>
- <param name="Append" value="true"/>
- <layout class="org.apache.log4j.PatternLayout">
- <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%l) - %m\n"/>
- </layout>
-</appender></programlisting></para>
-
- <para>Please note, that depending on your installation, you may not
- need ANY such macros expanded. For example, if you're logging to a
- database or JMS system, then logging directory doesn't make sense. So
- you can pass null values into LogManager for the logDirectory if this
- is the case without fear.</para>
- </sect3>
-
- <sect3>
- <title>Fancy tidbits for initializing LogManager</title>
-
- <para>There are several special instances for instantiating LogManager
- that may better fit your special application needs. Use them as you
- need or see fit. <itemizedlist>
- <listitem>
- <para>System Property: <emphasis>expresso.logDir</emphasis>
- Thanks to the log4j System, all log manager does is set the
- system property expresso.logDir. Depending on your server
- environment, it may be more practical to set this system
- property yourself. (Depending on your security settings)</para>
- </listitem>
-
- <listitem>
- <para>System Property: <emphasis>log4j.configuration</emphasis>
- Log4j can automatically find its own logging configuration file
- through this system property. You can use this directly
- too.</para>
- </listitem>
-
- <listitem>
- <para>Put <emphasis>log4j.xml</emphasis> in class path. If no
- configuration file is used, log4j automatically looks for
- log4j.xml in the classpath. This may be initialized as
- needed.</para>
- </listitem>
-
- <listitem>
- <para>Use <emphasis>LogManager(URL logConfigFile, String
- logDirectory)</emphasis> This is perfect if the logConfigFile is
- at a central configuration location or if you have it embedded
- in a jar file, for example. Note that when you use this method,
- Log4j does not watch for changes in the logging configuration
- file.</para>
- </listitem>
-
- <listitem>
- <para><emphasis>Use Other System Properties instead</emphasis>
- Any system property can be used inside the log4j file by
- enclosing the system property with ${}. So, for example, under
- Tomcat installations you could write your log file to the
- catalina log directory by having: <programlisting><appender name="expressoLog" class="org.apache.log4j.FileAppender">
- <param name="File" value="${catalina.home}/logs/expresso.log"/>
- <param name="Append" value="true"/>
- <layout class="org.apache.log4j.PatternLayout">
- <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%l) - %m\n"/>
- </layout>
-</appender></programlisting> Notice the use of ${catalina.home} instead
- of ${expresso.logDir}.</para>
- </listitem>
- </itemizedlist></para>
- </sect3>
-
- <sect3>
- <title>Changing Logging Configuration During Runtime</title>
-
- <para>As long as you don't use the last initialization method
- described above, log4j will watch the configuration file and
- automatically reload all configuration information once changed. This
- allows you to selectively turn on and off debugging information
- without restarting the server.</para>
- </sect3>
- </sect2>
-
- <sect2>
- <title>Schema List Table</title>
-
- <para>The Schema list table by itself doesn't do much. This table gets
- written to after you register your schema's get registered.</para>
- </sect2>
- </sect1>
-
- <sect1>
- <title>Schema Object</title>
-
- <para>The fundamental structure of an Expresso application is defined in
- the application's "Schema" object. This object extends the "Schema" class,
- and serves as a list of all of the other objects that make up a particular
- application. Expresso itself uses a Schema class
- (com.jcorporate.expresso.core.ExpressoSchema) to describe the classes
- "common" to all Expresso applications. A list of all known Schema objects
- for a particular installation is held in the SCHEMALIST table in the
- default database (which may maintained from the Setup page).</para>
-
- <para>The Schema Object is method of control, allowing the application(s)
- to register the different parts of applications with Expresso. These parts
- could include database objects (dbobjects), controllers, jobs, and
- servlets. These different parts are integrated into Expresso to allow
- better management and services. Also, once integrated into the Expresso
- Framework, the schema objects allow the various databases in one's
- application to be populated (if necessary), various initial setups, and
- fine grained security to be applied without giving up flexibility over the
- rest of the framework. Every application (such as eContent, eForum, or
- your own application) has one "schema"ues are very flexible and can be
- adapted to a wide breath of situations and environments.</para>
-
- <para>To create your own Schema simply extend the
- com.jcorporate.expresso.core.dbobj.Schema class. Then in the constructor
- be sure do the following: <itemizedlist>
- <listitem>
- <para>Add all of your controllers, dbobjects or jobs that are
- associated with that Schema.</para>
- </listitem>
-
- <listitem>
- <para>Modify/add any Setup table values</para>
- </listitem>
-
- <listitem>
- <para>Set the MessageBundle path (implement
- getMessageBundlePath())</para>
- </listitem>
-
- <listitem>
- <para>Define a version number with getVersion()</para>
- </listitem>
- </itemizedlist></para>
-
- <para>For details on how to do that see the API or view
- ExpressoSchema.</para>
- </sect1>
-
- <sect1>
- <title>Initialization</title>
-
- <para>When the application server/servlet container that contains Expresso
- starts up, a number of initialization steps are triggered. These steps
- begin with the DefaultInit servlet and the ExpressoActionServlet servlet.
- These two servlets are specified to execute on system startup in the
- web.xml file for any Expresso application.</para>
-
- <sect2>
- <title>DefaultInit</title>
-
- <para>The DefaultInit servlet begins by setting a system property to
- specify which XML parser is to be used during the remainder of the
- initialization process - this is set to the Xerces parser, and should
- not be changed.</para>
-
- <para>DefaultInit's next step is to call the ConfigManager object to
- initialize itself. ConfigManager then deals with the remainder of
- Expresso's initialization process.</para>
- </sect2>
-
- <sect2>
- <title>Configuration Manager</title>
-
- <para>The Configuration Manager allows further configuration of the
- Expresso Framework as well as the applications that run under the
- framework. The Configuration Manager achieves this by a set of XML files
- under the config directory. In these configuration XML files, there are
- properties that applies to the entire Expresso Framework, such as the
- underlying database, and some that apply to certain applications. Using
- the Configuration Manager in conjunction with the set of both Expresso
- Framework XML and application XML files gives utmost flexibility in the
- day-to-day operations of the framework.</para>
-
- <para>Every application has access to the Configuration Manager
- (ConfigManager), an object automatically initialized when Expresso
- starts up. This object gives access to one or more sets of "properties"
- - typically used for configuration information used by all applications
- running in a particular context, such as the required information to
- connect to the database server. Multiple "contexts" are supported by the
- database manager to allow many different database contexts to be used at
- once, either by one or a number of applications.</para>
-
- <para>A context is a separate section in expresso-web.xml, and is
- typically associated with a single database. A user can log in to a
- specific context. Most of the configuration properties held by
- ConfigManager are therefore specific to one context, although there are
- a few "system-wide" configuration values available as well.</para>
-
- <para>ConfigManager's first step on initialization is to read the
- expresso-config.xml file, and to create one or more "contexts" of
- configuration values. The Configuration Manager works by reading in the
- XML files at Expresso's startup. ConfigManager is a singleton object -
- that is, there is a single instance of ConfigManager running in any
- given Expresso application's virtual machine, even if several different
- db contexts and applications are installed. On startup, the
- Configuration Manager (expresso/core/misc/ConfigManager.java) will open
- and read the express-config.xml as well as any the application xml
- files. Furthermore, the ConfigManager will create one or more "contexts"
- of the configuration values. A context is a separate section in
- expresso-web.xml, and is typiclaly associated with a single database.The
- contexts are separate instances that a user can log into. Most of the
- configuration properties held by ConfigManager are therefore specific to
- one context, although there are a few "system-wide" configuration values
- available as well. Thus the ConfigManager holds both application
- specific and system-wide configuration values, even though the
- application specific values grossly outweighs the system-wide
- values.</para>
-
- <para>Technically, ConfigManager uses the Struts Digester class to read
- it's XML configuration file, and any problems during this initialization
- process are logged to the standard output of the servlet container . The
- Digester class was designed by the Struts folks to map XML to java
- objects in a systematic fashion with pre-defined rules being applied
- along the way. Any problems during this initialization process are
- logged to the standard output of the servlet container - so if your
- system does not initialize properly, or you see ConfigurationException
- errors when you attempt to work with your application, examine the
- system output log. The Digester class provides logging through Log4J
- (another Apache package) to a predefined logging area (usually a file).
- This logging provides both record of transactions as well as a simpler
- debugging process. One of the advantages of Expresso, is Expresso is
- built upon other projects to provide a best of breed solution. For
- Tomcat, this is a file in the "logs" directory called "catalina.out".
- For other servlet containers the location and name will vary - you must
- check the documentation for your servlet container for details. Once the
- expresso-config.xml file is read, the Setup values are read from each
- database into their appropriate context, and the connection pool is
- initialized for each context.</para>
-
- <para>Another advantage to the ConfigManager is the flexiblity of the
- class. The ConfigManager supports "custom" configuration values, so you
- can use it's capabilities to read and maintain values that are specific
- to your particular application, eliminating the need to create a custom
- object to do this. This aleviates the need for the application developer
- to write any custom code to read and parse the application's values.
- Furthermore, once the application has been registered with the Expresso
- framework, the ConfigManager is at the application's disposable.</para>
-
- <para>ConfigManager then becomes available to all applications running
- in that environment for them to request the value of any of it's
- configuration settings - see the JavaDoc documentation for ConfigManager
- and the various "Config" objects in the
- com.jcorporate.expresso.core.misc package for details on what
- configuration values are available.</para>
- </sect2>
-
- <sect2>
- <title>Setup Values</title>
-
- <para>'s content in. The values are available to any part of the package
- at runtime with a simple method call to the Configuration Management
- object (ConfigManager), and are automatically maintained in cache or
- read again as required. Setup values are different from the values held
- by the ConfigManager in several ways. They are more application-specific
- - e.g. there can be a number of setup values that are only used by one
- particular application, and another set of simiar values that are used
- by another application, whereas ConfigManager's values are available to
- all applications. Setup values can also be changed and re-read during
- execution, whereas ConfigManager's values are only read during system
- startup. Setup values are stored in the database for a particular
- context, and can be accesed via methods in the "Setup" object in the
- com.jcorporate.expresso.services.dbobj package. Once Expresso is set up,
- you can use the Database Maintenance Servlet to display and edit the
- list of configuration values. Setup Values can be accessed from the
- Expresso Framework admin page (running on one's machine of course). To
- view the Setup Values, click on the Setup in the left bar to get a list
- of all currently installed setup values. After the Setup Page finishes
- loading, click on Setup Values in the first table.</para>
-
- <para>Some uses of Setup values are to hold the connection information
- for a SMTP server, so that various Expresso functions can send emails
- automatically, and to hold the preferences for the URL of the header of
- the standard frameset used by Expresso - allowing easy customization to
- your own frame header for your applications.</para>
-
- <para>Setup values take affect as soon as they are saved.</para>
-
- <table>
- <title>Setup Values</title>
-
- <tgroup cols="2">
- <tbody>
- <row>
- <entry><emphasis role="bold">Setup Code</emphasis></entry>
-
- <entry><emphasis role="bold">Description</emphasis></entry>
- </row>
-
- <row>
- <entry>AdminEmail</entry>
-
- <entry>Email address of Administrative / Support Contact. This
+<chapter id='configure' xreflabel='Configuration and Initialization'>
+ <title>Application Configuration and Initialization</title>
+ <para>
+ <note>
+ <para>
+If you find this EDG documentation helpful please consider <link linkend='donate_configure'>DONATING</link>!
+to keep the doc alive and current.
+ </para>
+ </note>
+ </para>
+ <para>
+ <informaltable colsep='0' frame='none' pgwide='1' rowsep='0'>
+ <tgroup cols='2' colsep='0' rowsep='0'>
+ <colspec align='left' colsep='0' colwidth='50%' rowsep='0' />
+ <colspec align='right' colsep='0' colwidth='50%' rowsep='0' />
+ <tbody>
+ <row>
+ <entry><emphasis role='bold'>Version:</emphasis>
+
+<emphasis role='version'>Expresso 5.5</emphasis>
+ </entry>
+ <entry>
+<emphasis role='bold'>Maintainer:</emphasis><ulink url='mailto:dlloyd at jgroup.net?Subject=EDG'><emphasis
+role='maintainer'>David Lloyd</emphasis></ulink>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ <remark>
+<emphasis role='bold'> Expresso contains a number of facilities designed
+to manage the setup and configuration data for an application, such as
+the information required to connect to the applications database (or databases
+if more than one), values required to connect to the email and other servers,
+and user-specific preferences. </emphasis>
+ </remark>
+ <sect1>
+ <title>Introduction</title>
+ <para>
+Expresso provides various degress of control in both the framework and
+the applications, through both the schema objects and the configuration
+files written in XML. These configuration controls include:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+<emphasis role='bold'>Schema objects</emphasis>: enables management of
+the security for all of the applications component objects
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<emphasis role='bold'>Configuration Manager</emphasis>: used for configuration
+information used by all applications running in a particular context
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<emphasis role='bold'>Setup Values</emphasis>: specific to a particular
+context, but are further specific to one application
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<emphasis role='bold'>User Preferences</emphasis>: finer level of detail
+that are specific to a particular user.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </sect1>
+ <sect1>
+ <title>Steps to setup an Expresso Application</title>
+ <para>
+There are 3 basic steps (and a few optional ones) in creating your own
+Expresso Application. They are:
+ </para>
+ <orderedlist>
+ <listitem>
+ <para>Create your Schema object</para>
+ </listitem>
+ <listitem>
+ <para>Create your XML files in the config directory</para>
+ </listitem>
+ <listitem>
+ <para>
+Register your Schema and create/intialize the schema/database
+ </para>
+ </listitem>
+ </orderedlist>
+ <sect2 id='expresso-config'>
+ <title>expresso-config.xml</title>
+ <para>
+Expresso requires this configuration file in order to specify customizable
+settings controlling it's operation on your system. This file is called
+expresso-config.xml and it is found in the WEB-INF/config directory. This
+file contains the primary configuration information for Expresso. It is
+read during system startup, and it's information is available to any object
+in the application from the ConfigManager object.
+ </para>
+ <para>
+The expresso-config.xml configuration file supplies all of the information
+for each of the contexts set up with Expresso on your system, and provides
+information for each of those contexts to connect to their appropriate
+database engine(s), LDAP servers, and other core information.
+ </para>
+ <para>
+The expresso-config.xml that comes with Expresso is set up only for acces
+to the built-in Hypersonic databases (the "default" and "test" contexts).
+In order to add access to your own databases you must edit this file and
+either alter one of the existing contexts or add an entirely new context.
+See the documentation onsite for example jdbc configuration entities for
+various database types under the Database-specific Installation Notes header.
+ </para>
+ <para>
+The top-level element is the "expresso-config" object, which contains definitions
+that apply to the entire application. This top-level contains attributes
+and sub-elements that are applied to all contexts in Expresso. This includes
+properties like the log directory location where the application will write
+its log files, which are essential in tracking the application's operation.
+ </para>
+ <para>
+Another important section of the expresso-config file is the "class-handlers"
+section. This section specifies the classes that are used for various implementation
+classes within Expresso and it's applications. One of these classes, for
+example, is the "userInfo" implementation, which by default is supplied
+by the "com.jcorporate.expresso.services.dbobj.DefaultUserInfo" class.
+ </para>
+ <para>
+Specifying an alternate implementation:
+ <programlisting><![CDATA[<class-handlers>
+ <class-handler name="userInfo" classHandler="com.jcorporate.expresso.services.dbobj.DefaultUserInfo"/>
+</class-handlers>]]></programlisting>
+ </para>
+ <para>
+
+There are several classhandler already defined in Expresso. Here are a
+few along with their default values:
+ <table frame='all'>
+ <title>Pre-Defined Class Handlers</title>
+ <tgroup align='center' cols='3' colsep='1' rowsep='1'>
+ <thead>
+ <row>
+ <entry>Class Handler Name</entry>
+ <entry>Default Class</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>userInfo</entry>
+ <entry>com.jcorporate.expresso.services.dbobj.DefaultUserInfo</entry>
+ <entry>
+ <para>
+userInfo is the implementation of com.jcorporate.expresso.core.security.UserInfo.
+It is used for mapping login names to integer user id's and also is responsible
+for storing passwords and other information.
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>orderedCache</entry>
+ <entry>com.jcorporate.expresso.core.cache.OrderedCache</entry>
+ <entry>
+ <para>
+Ordered cache specifies a cached list where order must be preserved. It
+is usually backed by some sort of java.util.List implementation.
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>unOrderedCache</entry>
+ <entry>com.jcorporate.expresso.core.cache.UnOrderedCache</entry>
+ <entry>
+ <para>
+UnOrdered cache specifies a cached list where order must be preserved.
+It is usually backed by some sort of java.util.List implementation
+ </para>
+ </entr