Struts action mappings: Devide Et Impera

Discussions

News: Struts action mappings: Devide Et Impera

  1. Struts action mappings: Devide Et Impera (12 messages)

    This article discusses different combinations of a Struts action class and a form bean and how these combinations can be used. For each combination, consequences are shown, as are usage tips. The combinations shown are 'Full action', 'Form-only action', 'Form-only call sequence', 'Action class-only action', 'JSP-only action', and more.

    Read Struts action mappings: Devide Et Impera

    Threaded Messages (12)

  2. Good One[ Go to top ]

    Good One. Nicely explained.
  3. sessions[ Go to top ]

    when submitting a form the session objects are gone. checked the session id and it has different id than that is needed. would be nice to know why struts creating new session. I didn't find any reference in the code that would call request.getSession(true).
  4. Spelling?[ Go to top ]

    It's been a while, but I think it's spelled "divide".
  5. Seems like an imaginary issue[ Go to top ]

    Regarding actions without forms, the author says:
    Action-class only action cannot be called from HTML FORM, because Struts expects to find the form bean definition in an action mapping which serves the FORM. As a workaround, you can call an action-class only action from a link.
    Unless I misunderstand, this issue does not exist.

    Without a form specified in the action mapping, Struts will pass the request, response, and mapping (and no ActionForm) to the Action, which can do whatever it likes with request parameters. The Action doesn't have to know or care whether the parameters were submitted in the query string of a GET request or the request body of a POST request, because the Servlet API nicely abstracts the difference between the two HTTP methods.
  6. Regarding actions without forms, the author says:
    Action-class only action cannot be called from HTML FORM, because Struts expects to find the form bean definition in an action mapping which serves the FORM. As a workaround, you can call an action-class only action from a link.
    Unless I misunderstand, this issue does not exist. Without a form specified in the action mapping, Struts will pass the request, response, and mapping (and no ActionForm) to the Action, which can do whatever it likes with request parameters. The Action doesn't have to know or care whether the parameters were submitted in the query string of a GET request or the request body of a POST request, because the Servlet API nicely abstracts the difference between the two HTTP methods.
    To my recollection, I could not make this work with Struts 1.1. Seems that Struts checks the "action" attribute of HTTP FORM and validates action mapping corresponding to form action. It did not want to go further until I defined a form bean. But I will check it again, thanks.
  7. Seems like an imaginary issue[ Go to top ]

    Regarding actions without forms, the author says:
    Action-class only action cannot be called from HTML FORM, because Struts expects to find the form bean definition in an action mapping which serves the FORM. As a workaround, you can call an action-class only action from a link.
    Unless I misunderstand, this issue does not exist. Without a form specified in the action mapping, Struts will pass the request, response, and mapping (and no ActionForm) to the Action, which can do whatever it likes with request parameters. The Action doesn't have to know or care whether the parameters were submitted in the query string of a GET request or the request body of a POST request, because the Servlet API nicely abstracts the difference between the two HTTP methods.
    From http://struts.apache.org/faqs/newbie.html

    "If your Action does not need any data and it does not need to make any data available to the view or controller component that it forwards to, it doesn't need a form.

    However, you cannot use the <html:form> tag without an ActionForm. Even if you want to use the <html:form> tag with a simple Action that does not require input, the tag will expect you to use some type of ActionForm, even if it is an empty subclass without any properties."
  8. Nicely done. I just wish I could say the same about Struts.
  9. Maybe I'm missing something here, but I still don't get how you cleaning deal with a couple problems when using the redirect.

    First, it seems that you can not use the validation that the action form provides. If you have an error, you go back to the user who corrects it and when you move on, you're left with the ugly post condition. So the form is only useful for getting/setting?

    Then you lose the messages. Sounds like the answer is to stuff the error list in some session variable?

    What about the user input that is wrong? That has to be saved and somehow resent as part of the get operation.
  10. Error handling[ Go to top ]

    Maybe I'm missing something here, but I still don't get how you cleaning deal with a couple problems when using the redirect.

    First, it seems that you can not use the validation that the action form provides. If you have an error, you go back to the user who corrects it and when you move on, you're left with the ugly post condition. So the form is only useful for getting/setting?

    Then you lose the messages. Sounds like the answer is to stuff the error list in some session variable?

    What about the user input that is wrong? That has to be saved and somehow resent as part of the get operation.

    I did not describe error handling, shame on me. You are absolutely right, I had to discard standard Struts validation path because either I would have to show errors immediately in response to POST which brings postdata condition, or if I redirect to another action, I would lose error messages during redirect.

    When errors are catched on step (4a), I need to store errors on the server, for example, in the session object. So the wording of (4a) step, saying that "control is forwarded to an error page" is misleading. Step (9a) should not be on the picture at all, because output action is not supposed to process errors.

    Say, we display an HTTP FORM for the first time, this is what outputAction does. It loads data from the database or from the session or from in-memory buseness objects, and displays the form. Now a user fill up this form and submits it. Form is submitted to inputAction, which processes input and collects errors. These errors are are stashed in the session. After that we can either return null and tell Struts that everything is ok, or we can return the saved error messages just to kick automatic transfer of control to "input" URL.

    The problem with automatic transfer is that the URL must be hardcoded in struts-config.xml, so it is not dynamic. It may not be an issue, if you cache input data or if you use one form bean both for input and output and set "session" or higher scope to it. In my application I chose to store data that I work with in an in-memory business object. The business data is identified with unique ID, basically with a database primary key. Nice thing about this approach that if I want to view or edit a particular object, I just give its ID to the outputAction, and it pulls this object either from database or from in-memory object if it has been loaded before.

    So, in my application I decided not to report errors back to Struts. Instead, I stash error messages in the session and tell Struts that everything is peachy. Struts calls my outputAction, where I may perform some business-layer related work if needed, and then I build URL to my outputAction, which includes the ID of the object I work with. As a base destination I use the mapping to the output action defined in the struts-config.xml file.

    ActionForward actionForward = actionMapping.findForward(forwardName);
    ActionForward actionRedirect =
      new ActionForward(actionForward.getName(),
      actionForward.getPath()+"?OBJID=12345",
      true /* REDIRECT */
    );

    This way outputAction becomes completely stateless. It can be used (1) to obtain existing object from database and show it, (2) to redisplay object which is being modified and contains errors (this is our case), (3) to display form for new object input, it this case object ID is generated beforehand. Output action does not think, it simply shows data corresponding to object ID. If there are errors related to this object, it shows the errors as well. Oh, right, I forgot to mention, that I store error messages in the same in-memory object, where the business data is stored. So, as long as this object is not discarded, the errors are there. This is another simple trick: to relate errors to a particular object, not to a web page.

    The main idea behind my approach is that it is object-oriented, model-driven solution. Pages are, well, just pages. Pages are not objects or windows or screens or components. Pages are just views, they do not define the underlying data, they simply show it, that is it. This is why I think Struts is still important: it does not try to create an "intellingent" page like ASP.NET or JSF do. Web page is not a desktop app window anyway. Instead, Struts allows to develop cleanly separated MVC applications, though not without some effort.

    Please, check my two other articles:
    http://www.theserverside.com/articles/article.tss?l=RedirectAfterPost
    http://www.theserverside.com/articles/article.tss?l=RedirectAfterPost2

    and feel free to use the sample code:
    http://www.superinterface.com/files/prgpattern.zip
  11. Error handling[ Go to top ]

    Oops, typo:

    Struts calls my inputAction, where I may perform some business-layer related work if needed, and then I build URL to my outputAction, which includes the ID of the object I work with.
  12. Error handling[ Go to top ]

    I agree with most (maybe all, been gone for a month attending making money). I think your approach is a cleaner approach. I had not really understood the 'post' problem adequately. As a result my whole struts application is pretty much standard struts (I'm still on v1.02). I'm dividing new actions into update and view actions. For existing actions, I am modifying the class:
    1 turn off validation in the form class
    2 modify the existing action to call the validation.
    3 saving the data/input and redirecting to self where the second pass picks up the saved info and pass on to the the display jsp.

    Seems to be working ok at this point.

    Rather than simply store to the session, we create datablock classes to store the data. Typically them block stores an instance of the class that is the actual interface to the db (contains the data and insert/get/update type methods). I'm now storing the error lists in the block there too. We use a subclass of the struts action (subsequently subclassed by the various 'user' actions) that manages which data block class is need by the current action class. This allows each action class to have a structured storage that has all data needed without any work on it's part.
  13. Error handling[ Go to top ]

    I agree with most (maybe all). I think your approach is a cleaner approach.
    Thanks, Bill. I am glad that you found it valuable. I guess, one of the reasons why Struts is considered inferior to other frameworks, because it does not promote proper way of doing things. Thus people think that it cannot do things properly, and turn to other frameworks that can. There is a hint on two-phase approach in Craig McClanahan's announcement of Shale: "the setup logic and processing logic end up in two different Actions". But I could not find an example of it in samples that are shipped with Struts, or in the book that I've read.

    Other frameworks are more active and vocal about input/output separation. Spring live demo redirects from POST to load the result page in a separate GET request. They even mark pages non-cachable, so pages are kept in sync with the server when you navigate back. They do not advertise this as a proper way to do things, maybe they think that everyone knows about it. I did not, so I reinvented the wheel. Or maybe they did not know it too, because the first time I tried this demo year and a half ago, it responded with result page directly on POST request.

    JSF allows to do this too, the only thing needed to be done is to insert <redirect> element in the navigation map. This works because JSF stores state on the server, instead of passing it back and forth in _viewstate hidden form field, like ASP.NET does. So, JSP is definetely not an ASP.NET ripoff.

    Another recent finding is Rails framework, see description of ActionController::Base class. They talk about "two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect." They provide an example with two actions, where sign action is a "do action", and index action is "get action": "The sign action isn’t interested in rendering a template. So after performing its main purpose (creating a new entry in the guest book), it sheds the rendering assumption and initiates a redirect instead. This redirect works by returning an external "302 Moved" HTTP response that takes the user to the index action."

    So, nothing is new under the Moon. Well, maybe the idea of linking error messages to business objects is somewhat novel ;)

    Another thing to consider is friendly URLs. I haven't thought about this issue before. Basically, friendly URL is something like /myapp/obj/12345/view instead of /myapp/view?obj=12345 I guess that latter is not much worse, you still can bookmark it and it will work. It is harder to type by a user in the address line, but how many of them do that? Anyway, this is something to think about.