Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 3
public final ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
HttpSession session = request.getSession();
ActionForward forward = null;
if (isTokenValid(request)) {
// Reset token and session attributes
reset(request);
try {
// Perform the action and store the results
forward = performSynchro(mapping, form, request, response);
session.setAttribute(FORM_KEY, form);
session.setAttribute(FORWARD_KEY, forward);
ActionErrors errors = (ActionErrors)
request.getAttribute(Action.ERROR_KEY);
if (errors != null && !errors.empty()) {
saveToken(request);
}
session.setAttribute(ERRORS_KEY, errors);
session.setAttribute(COMPLETE_KEY, "true");
} catch (IOException e) {
// Store and rethrow the exception
session.setAttribute(EXCEPTION_KEY, e);
session.setAttribute(COMPLETE_KEY, "true");
throw e;
} catch (ServletException e) {
// Store and rethrow the exception
session.setAttribute(EXCEPTION_KEY, e);
session.setAttribute(COMPLETE_KEY, "true");
throw e;
}
} else {
// If the action is complete
if ("true".equals(session.getAttribute(COMPLETE_KEY))) {
// Obtain the exception from the session
Exception e = (Exception) session.getAttribute(EXCEPTION_KEY);
// If it is not null, throw it
if (e != null) {
if (e instanceof IOException) {
throw (IOException) e;
} else if (e instanceof ServletException) {
throw (ServletException) e;
}
}
// Obtain the form from the session
ActionForm f = (ActionForm) session.getAttribute(FORM_KEY);
// Set it in the appropriate context
if ("request".equals(mapping.getScope())) {
request.setAttribute(mapping.getAttribute(), f);
} else {
session.setAttribute(mapping.getAttribute(), f);
}
// Obtain and save the errors from the session
saveErrors(request, (ActionErrors)
session.getAttribute(ERRORS_KEY));
// Obtain the forward from the session
forward = (ActionForward) session.getAttribute(FORWARD_KEY);
} else {
// Perform the appropriate action in case of token error
forward = performInvalidToken(mapping, form, request, response);
}
}
return forward;
}
As you see above, the protected action is performed only once, that is, if the token is valid. If other requests are received
while the action is running, they are directed to the performInvalidToken() method's result until the action completes. By default, this method simply returns an ActionForward named "synchroError". This forward should lead to a page signaling that the action is in progress and providing a button to continue. This button
simply resubmits to the same action without any form or parameter in the request (they will not be considered anyway). When
the action completes, it stores its forward, form, exception, and errors, if any, in the session, and it sets a flag to indicate
it has completed. The first request coming after the action completion will get the forward, form, exception, and errors from
the session and continue as if it was the first request itself.
I include an example in the source code to demonstrate the behavior of a simple synchronized action. The provided implementation is based on Struts 1.0.2, but can easily be adapted for release 1.1.