Sorry for taking so long to respond, I am having DSL problems so I have to wait till 
I'm at work
to send mail.

I am pretty certain that I understand your requirements and I agree **100%** about the 
[extremely]
annoying "Whoops, you gotta login first (hehe) and start over!!!".

I have encountered that situation on many different web sites in my days of browsing.  
It is
especially annoying if you have just filled out a long form such as a reply to a news 
group (wink)
only to find that your time was wasted because the developers were "smart" enough to 
use pragma
no-cache but failed to implement what you are suggesting.

I have a couple more questions regarding your approach.
1. Does every action require the form to be session scope?
2. If any form element names a the same as the login page, wouldn't it overwrite them? 
(I think
you may have answered that for me already)
3. When you get the originally called action by doing 
HttpServletRequest.getRequestURI(), how does
using <html:base/> in the original form affect what is returned?


If I am not totally off track here, I believe that a resolution for this situation can 
be
accomplished with the current code base "as is".


When I am creating any Struts application, I always add a layer of abstraction to all 
my struts
components.  For Action, I create an abstract BaseAction that extends Action and make 
all of *my*
Actions extend the base.  I just declare an abstract method in the base:

  abstract public ActionForward perform( ActionMapping mapping,
                              ActionForm form,
                              HttpServletRequest request)
    throws Exception;

When struts uses my WhateverAction.perform() my base class handles the perform() which 
does
anything I want to do with *every* action call, which then calls the subclassed 
perform() method.

I use a similar technique with my ActionForm.  I have a BaseActionForm which all my 
ActionForms
extend.  I add a few common fields including:

  private Integer           id             = null;
  private String            action         = null;
  private String            login          = null;
  private String            password       = null;
  private String            callingAction  = null;
  private String            name           = null;
  private String            description    = null;

(with getters and setters of course)


Note the BaseActionForm field.  I use it to store an existing ActionForm for reuse 
later.



OK, back to the solution.
In my base action class, I do....




  public ActionForward perform(ActionMapping mapping,
                               ActionForm form,
                               HttpServletRequest request,
                               HttpServletResponse response)
    throws IOException,
           ServletException
  {
   ...

        // check for authentication requirements 
        //  (however you've implemented your permissions by 
        //   action - personally,I've seen this done 100 
        //   different ways).
        ...
        HttpSession session = request.getSession(false);
        ...

    if ( "authentication is needed 
        and they are not logged in")
    {

    
                // Create a new BaseActionForm and then 
                BaseActionForm newForm = new BaseActionForm();

           // configure it for the "trip"....
           newForm.setCallingAction("whereIwasheadedBeforeInterception.do");
           //   stuff the old form into the users session.
           session.setAttribute("oldForm", form);
           return mapping.findForward("login"); //which is a global forward
   
        }else if ( "they just filled out the login page" ){
          //validate the user
          ...
          ...
          // if not valid then send them back to input
          
            return new ActionForward( mapping.getInput() );

                
        }
          
          // and here we are...logged in and everything (if required)
          form = session.getAttribute( "oldForm" );
          
          this.perform(mapping, form, request)
        
        
        }
  }   
   
   
Now you might be asking me "How did the browser know how to correctly post the form to
"/servletcontext/whereIwasheadedBeforeInterception.do" so that the base class could 
validate?

The answer is that I used a little client side javascript to change the form action 
(onsubmit).

Here is a working example which is not (Struts-ified:-)


----------------------------------------------------
<HTML>
<HEAD>
<TITLE>Testing</TITLE>
<SCRIPT ID=clientEventHandlersJS LANGUAGE=javascript>
<!--

function frm_onsubmit() {
// testing the javascript
// this will dynamically change the action value when the form is submitted
// document.frm.action = 'http://jakarta.apache.org/struts/index.html';


 document.frm.action = '/<%=request.getContextPath()%>/<bean:write 
name="callingAction">';

}

//-->
</SCRIPT>
</HEAD>
<BODY>

<form name="frm" action="/validate.do" method="POST" id="frm" LANGUAGE=javascript 
onsubmit="return
frm_onsubmit()" >
User Name
<input type=text name=login value="">
Password
<input type=text name=password value="">

<INPUT type="submit" value="Submit" id=submit1 name=submit1>
</form>



</BODY>
</HTML>
------------------------------------------------------

Anyway, I hope this helps.  I think it will keep developers from encountering too many
"oops-I-forgot-to" and keep the code base consistent.


James Mitchell
Open Source Junky

"My favorite word in software is 'free'"
             James Mitchell 2002


"I hope people don't steal my stupid quotes."
             James Mitchell 2001






>------- Additional Comments From [EMAIL PROTECTED]  2002-02-27 21:37 -------
>The mechanism for preserving the original formvars depends upon what Struts 
>does with the ActionForm bean after it finishes handling a request.
>
>I've gotten the impression that Struts permits a single bean to be used to tie 
>together the values from a multi-page form, with already-defined bean values 
>being left alone -- the bean itself stored in session context -- as long as no 
>formvar on the later-submitted page shares the same name (in which case the 
>newly-submitted value(s) would replace the existing value(s)). If that's 
>correct, nothing further needs to be done to preserve the original formvars. 
>For all intents and purposes, the intercepting login form is nothing more than 
>an ad-hoc multipage form. As long as the developer doesn't cause a namespace 
>conflict between the login formvars and the rest of the formvars, everything 
>should work as expected.
>
>Assuming I'm not totally off-base and the form/bean mechanism works like that, 
>making the "action" parameter of the html:form tag optionally inheritable is 
>basically a matter of noting its absence and retrieving the proper value from 
>HttpServletRequest.getRequestURI() to use as the action parameter.
>
>On the other hand, I guess it's possible that Struts might discard form beans 
>after it's finished processing the request. I don't know enough at this point 
>to conclusively say one way or the other. If I'm wrong about the way form beans 
>and multipage forms work, there will be a bit more work to do... But before 
>proceeding to the book I wrote regarding that scenario (*grin*), I'll address 
>the other workflow issues.
>
>The main rationale for going with passively-intercepted logins is to enable web 
>visitors to freely roam around until they want to do something that needs to be 
>restricted or has consequences. Why? Simply put, the more you force web users 
>to do up front, the more likely they are to simply go elsewhere instead. On the 
>other hand, NOTHING is more annoying than initiating an action on a web site, 
>being forced to log in, then finding yourself back at square one. This solution 
>conveniently solves both problems for  users AND developers.
>
>Insofar as not permitting users to enter form data whose submittal might 
>require authentication, the same goal can be achieved by simply ensuring that 
>the template displaying the form is only accessible via a mapping returned from 
>the hypothetical performLoggedInAction() method. 
>
>If I'm right about the way ActionForm beans are handled and you understand what 
>I'm trying to achieve, you can stop reading here. 
>
>OK, getting back to the issue of saving formvars... assuming I'm totally wrong 
>about the way form beans work and some mechanism needs to be explicitly created 
>to preserve them between requests, here is a scheme that might work quite well:
>
>First, we'll need to create a new object. For the sake of illustration, let's 
>call it an InterceptedFormvars object. It's basically a container for a HashMap 
>of String[] ("savedFormvars") and a String used to store a unique key 
>("interceptionId"). We need to use an array of Strings, because it's possible 
>to have multiple formvars with the same name, but different values in a single 
>submission. No big deal... singleton formvars will just have one element in the 
>array.
>
>Once Struts realizes that it needs to inherit the "action" for the html:form 
>it's about to render, it creates an InterceptedFormvars object, and populates 
>the InterceptedFormvars's "savedFormvars" HashMap with the names and values[] 
>of the just-submitted formvars within it. 
>
>Struts proceeds to generate some random string, and stores it in the 
>InterceptedActionForm's "interceptionId" variable. It then renders the 
>html:form. Somewhere inside the newly-rendered form, Struts adds a hidden 
>formvar with a name that's used consistently by Struts, but unlikely to ever be 
>used by a real form (say, Struts_PreservedFormVarUniqueId_ip.address.of.server) 
>whose value is the same as that stored in the 
>InterceptedFormvars's "interceptionId" value. Finally, the InterceptedFormvars 
>is stored in session context, and Struts continues as it always has.
>
>Now, getting to the other end of the equation, when a form submission is made 
>and BEFORE the ActionForm's subclass is  populated with the submitted formvars, 
>Struts notices that the user has an InterceptedFormvars object stored in 
>session context. It retrieves it, stores it within the ActionForm object 
>itself, and deletes it from session context. The ActionForm checks to see 
>whether the formvars just submitted contain a value keyed 
>to "Struts_PreservedFormVarUniqueId_ip.address.of.server". If they don't, it 
>nulls out the InterceptedFormvars object and pretends it never existed. If the 
>hidden formvar DOES exist, but its value doesn't match the 
>InterceptedFormvars's interceptionId, it does the same thing (unceremoniously 
>nulls it out). However, if the InterceptedFormvars object's interceptionId 
>matches that of the "Struts_PreservedFormVarUniqueId_ip.address.of.server" 
>formvar, it proceeds to repopulate the bean with the values from the 
>InterceptedFormvars. 
>
>Why bother with the interceptionId? To ensure that users who open multiple 
>browser windows, back-arrow around, and/or go to bookmarks that might have URL-
>encoded parameters won't get a rude surprise by mistake.
>
>Having repopulated the bean, the ActionForm processes the newly submitted 
>formvars. First, it wipes out any values stored in the bean whose names 
>conflict with the new formvars. It then populates the bean with the values from 
>the new formvars, leaving the non-conflicting restored values intact, and 
>proceeds as normal.
>
>By now, you might be wondering why we saved the InterceptedFormvars, or how we 
>plan to handle the consequences of namespace conflicts between the preserved 
>formvars and the login formvars.
>
>By default, the html:form tag's object should throw an exception whenever it 
>detects a namespace conflict while rendering a form with an inherited action -- 
>in other words, if any formvars in the new (intercepting) form that have the 
>same name as formvars from the previously-submitted (intercepted) form. This is 
>to prevent ACCIDENTAL namespace collisions. HOWEVER, the programmer should be 
>able to specify an additional parameter (silent="true"?) with the html:form tag 
>that will suppress the exception if he really, truly wants to do it and 
>understands the consequences. 
>
>Likewise, when there's a namespace collision, the formvars from the most 
>recently-subbmited form take precedence over those saved from the previous form 
>and are the values present when perform() gets called. 99.9% of the time, this 
>is fine. For the remaining .01%, the ActionForm class should have another 
>method -- restoreFormvars() -- that can be explicitly called once the tasks 
>related to intercepting the request have been completed to wipe the FormBean's 
>values clean and restore them to the EXACT condition they were in the first 
>time around. Once again, keeping things easy and convenient for the majority of 
>use cases, but keeping the door open a crack for the rare instance when 
>somebody MUST have the alternate behavior.
>
>************
>
>For the sake of clarification, here's an example use case I came up with. I 
>originally had this at the beginning, but decided to move it to the bottom 
>since it's quite long, probably unnecessary to most of the people reading this, 
>and I didn't want to lose everyone's attention before I even got to the main 
>point :-)
>
>Suppose a user wants to launch an action that will culminate in a SMS message 
>being sent to his phone. Obviously, the user needs to have logged in first. The 
>user has not yet logged in, because he's browsing around the site looking for 
>something to send.
>
>He finds something interesting and clicks a submit button. 
>
>A HTTP POST is made to "sendSomething.do" along with a few formvars -- say, a 
>radio button named "id" with an integer value and two hidden form fields 
>named "hidden1" and "hidden2". 
>
>The path "sendSomething" maps to a subclass (SendSomethingAction.class) of an 
>abstract subclass (AbstractLoggedInUserAction.class) of Struts' own Action 
>class and has a form bean (SendSomethingFormBean.class) with get/set methods 
>for three variables (id, hidden1, and hidden2) that is a subclass of an 
>abstract FormBean class used to handle logins (AbstractLoginFormBean.class) 
>that itself is a subclass of Struts' ActionForm. The AbstractLoginFormBean 
>class has get/set methods for three variables too... username, password, and 
>doLogin.
>
>SendSomethingAction has only a single method -- performLoggedInAction -- that 
>takes as input parameters an ActionMapping, an ActionForm, a 
>HttpServletRequest, a HttpServletResponse, a User object, and an errList object 
>(User encapsulates things like the user_id and phone number, errList is a 
>subclass of ActionErrors).
>
>AbstractLoggedInUserAction has only a single method -- perform(), that takes 
>the usual 4 parameters (ActionMapping, ActionForm, HttpServletRequest, 
>HttpServletResponse). It retrieves the User object from session context, or 
>instantiates a new one if necessary. 
>
>It casts the ActionForm into an AbstractLoginFormBean and checks to see whether 
>the bean's doLogin value is null. If it's not, it calls the User object's login
>() method, passing the bean's username and password values as parameters.
>
>if User.isLoggedIn(), AbstractLoggedInUserAction returns the ActionForward 
>returned by performLoggedInAction(*,*,*,*,*,*) (remember, we're actually using 
>SendSomethingAction... the perform() method is in SendSomethingAction's 
>superclass). Otherwise, it returns mapping.findForward("login"), which is 
>globally forwarded to "login.jsp".
>
>So far, so good. Now's when we get to find out how well I actually understand 
>how Struts works ;-)
>
>At this point, Struts' normal routines take over, and display the login form. 
>The login form takes advantage of Struts' newest feature (*grin*) whereby the 
>form inherits the action that called it if no "action" attribute is explicitly 
>specified in the html:form tag. In this case, it's "sendSomething.do".
>
>The user enters his username and password (doLogin is a hidden formvar set to 
>any non-null value) and hits submit.
>
>Once again, the Action servlet calls SendSomethingAction's perform() method 
>(inherited from AbstractLoggedInUserAction). Since the user hasn't logged in 
>yet, there's no User object stored in session context yet, so it instantiates 
>another one, then calls its login() method after discovering that the bean's 
>doLogin value is non-null.
>
>Success. The user is logged in. User.isLoggedIn() is true, so we clear the 
>username, password, and doLogin values in the bean, and performLoggedInAction
>(*,*,*,*,*,*) gets called to do its thing.
>
>What about the three formvars that were submitted in the first place before we 
>had to take the side trip to log in? Well, login.jsp inherited its action -- 
>sendSomething.do. The formBean mapped to sendSomething is still a 
>SendSomethingFormBean. We just happened to repurpose it a bit by using its 
>latent skills as a login form bean to communicate the username, password, and 
>doLogin command to the perform() class. 
>
>The mechanism for their preservation was addressed earlier...
>
>



__________________________________________________
Do You Yahoo!?
Yahoo! Greetings - Send FREE e-cards for every occasion!
http://greetings.yahoo.com

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to