As part of a web e-commerce application, I designed a framework strikingly
similar, but completely independent, to Struts. The major differences are
with respect to flow control, internationalization, and security. I thought
that I would describe this system for discussion since a large portion of
what I built concerned flow control.
My framework is called the Internet Application Framework (IAF). My
greatest concerns were security, flow control and rapid development using
Java and JSP-newbies. Ever see 20 developers new to Java and JSP (not to
mention EJB) let loose on a web app? Talk about formula for disaster!
My guiding principle was: everything must be explicit, formalized, and
consistent.
First, I defined a 'calling convention' for requests. All URLs must be of
the form:
http://host:port/<appPath>
and define a parameter called 'iafAction' that represents the logical name
of an action to be performed. Other parameters are of course permissable.
Example:
http://foo.com/e-commerce?iafAction="logoff"
My second step was to define how requests are dispatched. IAF's Dispatcher
dispatches requests as follows:
1. Log the request
2. Verify that the iafAction exists in the configuration
3. Checks that the user has permission to execute the action
4. Validate request parameters
5. Invokes the appropriate Controller
6. Handles system errors, if any
IAF's Dispatcher ensures the format of the request and rejects invalid
formats or requests for iafActions that do not exist. If iafAction is
undefined in a request, users are forwarded to a configurable page, usually
the entry point of the site.
One of the side-effects of having this request format and dispatch
processing was that developers had to implement requests accordingly or
their functionality simply didn't work. This was a major benefit as it
liberated them from having to come up with their own way of doing things and
enforced consistency from the very start.
My third step was to define a formalized way of defining application
behavior that could be used communicate with developers. This took the form
of a configuration file very similar to struts-config.xml. An example
configuration follows:
<Dispatcher>
<Actions>
<Action Name="LogIn" Controller="app.LogInController">
<NextAction>ShowLogInSuccess</NextAction>
<DisplayBean>app.LogInBean</DisplayBean>
<Validation Parameter="userID" ErrorMsg="User ID is blank"
Rule="NotNull"/>
<Validation Parameter="password" ErrorMsg="Password is blank"
Rule="NotNull"/>
</Action>
<Action Name="ShowLogInSuccess" Controller="PageDisplay"
Arguments="/Login.jsp">
</Action>
</Actions>
</Dispatcher>
In this configuration, there are two Actions, LogIn and ShowLogInSuccess.
In IAF, Actions are handled by Controllers. Controllers do whatever they
want with the request but must return a string representing the name of a
listed NextAction tag. NextAction is similar to the nested forwards tag in
Struts, except that IAF's Disptacher will only forward to a NextAction. The
NextAction simply indicates the name of another Action to dispatch but
unlike Struts, all Actions in IAF are global.
Upon completion of an Action, IAF Controllers must return a string that
corresponds to a NextAction described in the Action configuration. If it
does not, then it is treated as an application error. My policy was that
all forwards from a Controller must be explicit. If the string does
correspond to a NextAction, then IAF's Dispatcher dispatches to that Action
as if it were an incoming request so it gets logged, checked, etc. But, if
the string does not match a NextAction, then it is treated as an application
error because it indicated:
o the developer of the controller does not understand the
programmatic flow of the app, or
o there is a flaw in the design represented by the configuration
Regardless, IAF catches inconsistencies between design and implemenation so
they can be resolved.
In IAF, I did not distinguish pages from Actions; I had a special Action,
called PageDisplay, whose sole purpose was to forward to a jsp. Subclasses
of PageDisplay could forward use any other mechanism to come up with
content. This made the system quite consistent and yet flexible for content
rendering. ShowLogInSuccess is an example of a PageDisplay Action. The
argument which is simply the jsp to forward to but subclasses may interpret
the argument differently, as needed.
DisplayBean is the IAF version of Struts's ActionForm. The Validations are
listed directly in the configuration file rather than implemented in the
DisplayBean itself so that messages are more consistent with usage. Rule is
a simple name that maps to a static method used to validate the value of the
Parameter. If it returns false, the ErrorMsg is appended to a list of
ErrorMessages that are made available in the session. Any page can then
display ErrorMessages as needed.
While I did not handle internationalization explicitly, I left it up to
subclasses of my Validation Rule to use the ErrorMsg as a key to look up the
real message if needed. PageDisplay subclasses could be used to render
Locale-specific pages.
Security checks are delegated to an instance of IAFAuthorizer, whose runtime
implementation is specified in my configuration. IAFAuthorizer is given
access to the iafAction, request, session and all other goodies it might
need to figure out if the user is currently logged in or not. IAF's
Dispatcher did not however, perform authentication, this must be done by the
application since I knew we would deploy in a wide variety of situations in
which we would not not be able to dictate the security implementation. My
strategy then was to allow deployments to map onto an available security
system (say, based on LDAP) rather than have IAF implement security itself.
Implementations of the IAFAuthorizer are responsible for detecting
non-logged in users and delegating them to an appropriate log in page.
My team did create a package that provided a sophisticated user, group,
role-based security implementation that provided authentication for use when
no other security existed, however, it is provided as an optional package.
Additionally, IAFAuthorizer can be used protect fields, buttons, and other
html widgets by providing through a method that checks permissions to view,
edit or execute virtually anything. A user might have read, read and write,
execute, or no permission at all on a particlar field. With a simple
wrapper around the field, an access check is made at runtime and the widget
is displayed appropriately. Needless to say, the configuration for this
level of security was tedious, complex and time consuming. Of course, page
developers need only check sensitive widgets. Most widgets did not need to
be secure. In our e-commerce web app, we had 126 Action definitions and
approximately 300 checked widgets.
In summary, both a strength and a weakness of my system is its rigidity in
its configuration. By creating a detailed specification, we were able to
let loose a horde of developers and yet rapidly built an app that is secure
and maintainable. However, IAF is lacking a method (as explained by
Matthias) for keeping track of where the user has been, so it is impossible
to have call-return flexibilty. By call-return, I mean I would like to call
the functionality of an Action from multiple locations, and when that Action
is finished, I want to return to where I was called from. However, IAF, to
a large extent met my goals for enforcing secure and consistent
implementation that enabled rapid development of the project with a large
team.
Sorry this was long. I hope you stayed with me and got something out of it.
didge
-----Original Message-----
From: Matthias Bauer [mailto:[EMAIL PROTECTED]]
Sent: Thursday, June 07, 2001 3:44 AM
To: undisclosed-recipients
Subject: Re: Work flow RFC
Hi everybody,
there is a mail I already sent to this list with another subject, because
somebody asked for it.
There is some very simple extension to Struts which I implemented, because I
needed to have at least a minimum workflow support.
Maybe you want to check out this little extension and reuse at least some of
the
ideas.
Here is the mail text I am talking about. I also attached the files again.
--- Matthias
*******************************************************************
Hi everybody.
There were quite a few requests to share the code I talked about in an
earlier
mail with this subject.
The code subclasses some struts classes and provides the following
enhancements:
1. Control flow. For each action you can specify the action which must be
executed directly before. Thus, you can easily prevent someone from hitting
the
reload button or using the browser's back button and submit a form for the
second time. The mechanism I chose can replace the token mechanism provided
by
struts and brings some further enhancements. A small example: An action
displayLogon displays the logon page of a web application. The next action
logonAction defines the action displayLogon as the previous action. An
exception
is raised (and causes a forward to an appropriate error page) when the
previous
action is something else then displayLogon.
2. User authentication: For each action you can specify an object which does
a
check whether the user is authenticated to execute this action. Together
with
the paradigm (which I would warmly suggest to anybody) to only display jsp
pages
through actions like displayXXX you can easily do fine grained authorization
checks for your whole webapp.
3. Maintenance mode: If your web-site is currently under maintenance set the
debug level to -1 and all the actions automatically forward to a maintenance
page, which should display an appropriate message the the user.
Now, here is a brief code description. For details please have a look at the
source code which (I hope) is richly commented.
GenericAction
=============
Extends Action and is the class from which all other Actions need to be
derived.
It does the follwing:
1. Check if we are in maintenance mode. If we are, then forward to the
maintenance page.
2. Check for the authentication object (of type GenericAuthentication) which
is
specified by the action's parameter "authtype". If authentication fails,
forward
to an authentication exception action, which should display a page with a
reasonable message.
3. Check whether the action attribute "prevpath" matches the current
prevpath
value which was stored by the (directly before executed) GenericAction in
the
user's session. If it does not match forward to a control flow exception
action.
4. Now call the method "performAction" (which needs to be overridden by all
actions that subclass GenericAction). If debugging is switched off, all
exceptions thrown in this method are catched an a forward to an exception
action
is done.
5. Update the session variable which stores the prevpath value, so it holds
the
correct value when the next action checks it.
AuthenticationException
=======================
Exception thrown, when trying to access a resource that you are not allowed
to
access.
GenericAuthentication
=====================
Interface that should be implemented by classes which provide authentication
checks, which are used by GenericAction.
Please note that GenericAction uses only a single instance of each
authentication object. So you have to be very careful when you use data
members.
Normally only the single method check is provided, which does not work on
any
object's variables.
AnyUserAuthentication
=====================
Example for an authentication class which implements the interface
GenericAuthentication.
ApplicationMapping
==================
Implementation of enhanced ActionMapping.
It defines the following custom properties:
- prevpath - The context-relative URI of the previous action. This enables
the
Action-Classes to compare this value to the value they can easily track.
This
enables rudimentary flow control checks.
- authtype - The name of the authentication class which checks if the user
is
allowed to execute the mapping's action.
For the ApplicationMapping to become effective you have to change the
parameter
mapping in web.xml like this:
<init-param>
<param-name>mapping</param-name>
<param-value>ApplicationMapping</param-value>
</init-param>
struts-config.xml
=================
The struts configuration file can then look like this:
============================================================================
=======
<!-- ========== Global Forward Definitions ==============================
-->
<global-forwards>
<forward name="authenticationexception" path="/authenticationException.jsp"
/>
<forward name="maintenance" path="/maintenance.jsp" />
<forward name="controlflowexception" path="/controlFlowException.jsp"/>
<forward name="exception" path="/exception.jsp" />
</global-forwards>
<!-- ========== Action Mapping Definitions ==============================
-->
<action-mappings>
<!-- Display login -->
<action path="/displayLogin"
type="SuccessAction">
<forward name="success" path="/index.jsp" />
</action> <!-- Execute the login -->
<action path="/loginAction"
type="LoginAction"
name="loginForm"
scope="request"
input="/index.jsp">
<set-property property="prevpath" value="/displayLogin"/>
<forward name="success" path="/displayPasswordChange.do" />
</action>
<!-- Display change password page -->
<action path="/displayPasswordChange"
type="SuccessAction">
<set-property property="authtype" value="AnyUserAuthentication"/>
<forward name="success" path="/passwordChange.jsp" />
</action>
<!-- Change password action for an already registered user -->
<action path="/passwordChangeAction"
type="PasswordChangeAction"
name="passwordForm"
scope="request"
input="/passwordChange.jsp">
<set-property property="authtype" value="AnyUserAuthentication"/>
<set-property property="prevpath" value="/displayPasswordChange"/>
<forward name="success" path="/passwordChangeSuccess.jsp" />
</action>
</action-mappings>
============================================================================
=======
See what happens?
- You are not allowed to execute displayPasswordChange or
passwordChangeAction,
when you are not correctly logged in. The framework does the check for you.
- passwordChangeAction and loginAction are only executed, if
displayPasswordChange or displayLogin respectively, have been executed
directly
before.
I found these enhancements very helpful for my web applications, because it
deals with some basic issues you always have to solve in webapps. Therefore
I
would like to see these enhancements incorporated into the standard struts
framework. I plan to propose this officially after Struts 1.0 has been
released.
I am sending it to the list now, because some of you asked for it. In the
meantime I would appreciate any feedback (both positive or negative ones).
Hope you have fun,
--- Matthias
*******************************************************************
Jonathan Asbell wrote:
> Is there anyone on the list that actually HAS experience developing with
> workflow engines?
>
> ----- Original Message -----
> From: "Rey Francois" <[EMAIL PROTECTED]>
> To: <[EMAIL PROTECTED]>
> Sent: Wednesday, June 06, 2001 4:40 AM
> Subject: RE: Work flow RFC
>
>
>
>>Another possibility is to develop extensions for the TogetherJ CASE tool.
>>
> It
>
>>is entirely written in Java, therefore can run on most platform, and from
>>
> my
>
>>understanding it is possible to define new diagram types and patterns.
>>
>>This may not directly relate to workflow, but we have in our team created
>>the concept of a request servicing diagram which is a class diagram
>>representing the objects involved in servicing a request. Particularly on
>>this diagram we display the request object and the action it is mapped to.
>>Although we have not done it yet, it is quite possible to develop a
>>
> pattern
>
>>that generates the corresponding action mapping entry in the
>>struts-config.xml, and vice-versa.
>>
>>Fran�ois Rey
>>Financial WebSuite
>>Capco
>>http://www.capco.com/
>>
>>
>>-----Original Message-----
>>From: Craig Tataryn [mailto:[EMAIL PROTECTED]]
>>Sent: 05 June 2001 20:06
>>To: Jonathan
>>Cc: [EMAIL PROTECTED]
>>Subject: Re: Work flow RFC
>>
>>
>>Is this a workflow editor or just a configuration editor (which would be
>>nice
>>for struts)?
>>
>>craig.
>>
>>Jonathan wrote:
>>
>>
>>>Again, Ive got to say look at the Barracuda project. They have one of
>>>
>>these
>>
>>>gui configurers. Check it out at
>>>http://barracuda.enhydra.org/Barracuda/GetBConfig.event
>>>
>>>----- Original Message -----
>>>From: "Craig Tataryn" <[EMAIL PROTECTED]>
>>>To: <[EMAIL PROTECTED]>
>>>Sent: Tuesday, June 05, 2001 12:28 PM
>>>Subject: Work flow RFC
>>>
>>>
>>>>Hi, I would like your comments for the workflow item on our TODO list.
>>>>Currently this is how I've envisioned the workflow project:
>>>>
>>>>1) A nice GUI type Applet or Application that has visual constructs
>>>>which can be connected in a Visio type manner to create an Activity
>>>>diagram or some other type of flow diagram.
>>>>
>>>>2) This diagram will be persisted in an XML file which holds meta data
>>>>for the elements in diagram (position, type of construct (controller,
>>>>flat html page, cgi script, flow arrow, etc..)).
>>>>
>>>>3) The diagram can be exported to a struts config file via XSLT (i.e.
>>>>workflow.xml -> workflow2struts.xsl -> struts-config.xml)
>>>>
>>>>4) A diagram can also be imported from a struts-config.xml file via
>>>>
> XSLT
>
>>>>(i.e. struts-config.xml -> struts2workflow.xsl -> workflow.xml). Of
>>>>course some sort of "pretty layout" code would have to be used to
>>>>un-jumble the mess of constructs that are sucked out of the
>>>>struts-config.xml file (i.e. take a guess at proper positioning
>>>>information).
>>>>
>>>>The GUI should employ some sort of extensibility mechanism like BSF
>>>>(http://oss.software.ibm.com/developerworks/projects/bsf) or Bean
>>>>
> Shell
>
>>>>(http://www.beanshell.org/) to allow users to plug-in their own
>>>>functionality (i.e. validation code) without jeopardizing the core
>>>>
> code
>
>>>>(what I call the Emeril Lagasse technique -- BAM!).
>>>>
>>>>I realize this is a very high level look at the TODO but I think as we
>>>>get more comments we will get more granular and can start dishing out
>>>>segments.
>>>>
>>>>Let me know what you think.
>>>>
>>>><tataryn:craig/>
>>>>
>>************************************************************************
>>The information in this email is confidential and is intended solely
>>for the addressee(s).
>>Access to this email by anyone else is unauthorised. If you are not
>>an intended recipient, you must not read, use or disseminate the
>>information contained in the email.
>>Any views expressed in this message are those of the individual
>>sender, except where the sender specifically states them to be
>>the views of Capco.
>>
>>http://www.capco.com
>>***********************************************************************
>>
>>
y