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


Matthias Bauer +++ [EMAIL PROTECTED] +++ LivingLogic AG +++ www.livinglogic.de


Niall Pemberton wrote:
> 
> Sounds intertesting.
> 
> How about you post your source to this list?
> 
> Niall
> 
> > -----Original Message-----
> > From: Nanduri, Amarnath [mailto:[EMAIL PROTECTED]]
> > Sent: 07 May 2001 13:20
> > To: '[EMAIL PROTECTED]'
> > Subject: RE: Struts questions for evaluation
> >
> >
> > me too
> >
> > -----Original Message-----
> > From: Jonathan Asbell [mailto:[EMAIL PROTECTED]]
> > Sent: Monday, May 07, 2001 8:21 AM
> > To: [EMAIL PROTECTED]; [EMAIL PROTECTED]
> > Subject: Re: Struts questions for evaluation
> >
> >
> > I am interested in the code.   Please send.
> >
> > > Hello Struts users,
> > >
> > > We are currently evaluating Struts and other web (MVC) frameworks and
> > would like to ask you some questions:
> > >
> > > - Is there some struts-config XML-generation from some
> > >   modeling tool (Rose for instance) ?
> > >
> > >   We would like to design a state or activity diagram in
> > >   such a tool then generate the navigation XML file.
> > >
> > > - Can struts behave like a real state machine ?
> > >
> > >   i.e. how to ensure that some Action is performed only if
> > >   another Action (or view but is not ideal) has been
> > >   performed with some result (success to be simple). We
> > >   would like this to be Struts-controlled, not developper-
> > >   controlled (check session variables, etc.).
> > >
> > >   We found the built-in Struts token system but this seem
> > >   only to be used to demarcate transactions in a set of
> > >   views. This allow to demarcate a transaction end (token
> > >   value change) but not an inconstency within the
> > >   transaction (i.e. i valide twice the first page of the
> > >   transaction) as the token value will not change.
> >
> > I hit the same weakness of struts a few months ago and I enhanced some
> > struts
> > classes in order to be able for each action to specify which action has to
> > precede this action. This allows you to implement some kind of
> > control flow.
> > For
> > instance you can now define an action that displays a logon page
> > (displayLogon)
> > and specify in the next action (logonAction, which gets executed when the
> > user
> > presses the submit button of the logon page) that displayLogon
> > needed to be
> > executed directly before. This is more powerful than the token mechanism.
> >
> > What I also did at the same time is an enhanced user
> > authentication checking
> > for
> > each action. You can now specify a different user authorization class for
> > each
> > action, so you can easily implement a very fine grained user
> > authorization.
> >
> > Exceptions (either control flow or user authentication exceptions) are
> > handled
> > by the framework and cause a forward to different error pages.
> >
> > I plan to propose this enhancement to the struts developer's group after
> > release
> > 1.0 is out. If you are interested in the meantime I can send you the code.
> >
> > > - Why does the struts-example perform some scoped-
> > >   variables cleanup at the end of every Action ?
> > >
> > >   As these variables are defined as scoped in the struts-
> > >   config XML file, they should naturally live during the
> > >   specified scope lifetime. We don't understand that in the
> > >   struts-example (maybe this is not mandatory).
> >
> > My understanding is: For request scope you normally do not need
> > to clean up,
> > because the object gets removed automatically, when the request is served.
> > For
> > the session scope you pretty often want to remove e. g. a form
> > bean, when it
> > is
> > no longer needed to keep your session data lean.
> >
> > > Thank you very much for your informations about Struts.
> > >
> > >   Laurent.
> >
> > --- Matthias
> > ----- Original Message -----
> > From: "Matthias Bauer" <[EMAIL PROTECTED]>
> > To: <[EMAIL PROTECTED]>
> > Sent: Monday, May 07, 2001 4:05 AM
> > Subject: Re: JDBC driver specific functionality/GenericConnections
> >
> >
> > > > Hello,
> > > >
> > > > I would like to access functionality specific to the MM MySql
> > driver in
> > an
> > > > application that uses Struts' datasource pool and am having a bit of
> > > > troubling casting a GenericConnection back to an
> > > > org.gjt.mm.mysql.Connection. Casting from a GenericConnection to an
> > > > org.gjt.mm.mysql.Connection results in a class cast
> > exception; casting a
> > > > Statement or PreparedStatement generated by the GenericConnection
> > produces a
> > > > null pointer exception. I am working with Struts 1.0-beta-1
> > and MM Mysql
> > > > 2.0.4.
> > > >
> > > > If it sheds any light on the problem, the MM function I would like to
> > access
> > > > is PreparedStatement.getLastInsertID(). Has anyone successfully
> > prosecuted
> > > > this?
> > >
> > > You must not cast GenericConnection to  What you really want to
> > do is call
> > > DataSource.getConnection which returns a Connection object like so:
> > >
> > >   DataSource ds = getServlet().findDataSource(null);
> > >   Connection con = ds.getConnection();
> > >
> > > You can cast this connection to org.gjt.mm.mysql.Connection without any
> > > problems!
> > >
> > >
> > > > On a tangential note--why is the getConnection() method in
> > GenericConnection
> > > > private? Were it public, I assume I could use it to get an MM
> > Connection
> > and
> > > > execute MySql specific functions to my heart's content.
> > >
> > > You should never need to call
> > GenericConnection.getConnection()! What you
> > want
> > > to do is always call DataSource.getConnection(), as stated above.
> > >
> > > > Thanks is advance,
> > > > David
> > >
> > > Hope this helps,
> > >
> > > --- Matthias
> > >
> >

GenericAction.java

GenericAuthentication.java

AnyUserAuthentication.java

ApplicationMapping.java

AuthenticationException.java

S/MIME Cryptographic Signature

Reply via email to