I'm trying to write a Command or set of Commands that will process a Tiles definition to help complete the 1.x-compatible chained request processor. I've got something working, but I would like some community input before I submit it.
First, I created another subclass of AbstractPerformForward that looks like this:
// Resolve module-relative paths if (forwardPath.startsWith("/")) { uri = RequestUtils.forwardURL(swcontext.getRequest(), forwardConfig); } else { if (processTilesDefinition(context, forwardConfig)) { return; } else { uri = forwardPath; } }
// Perform redirect or forward if (forwardConfig.getRedirect()) { if (uri.startsWith("/")) { uri = swcontext.getRequest().getContextPath() + uri; } swcontext.getResponse().sendRedirect (swcontext.getResponse().encodeRedirectURL(uri)); } else { RequestDispatcher rd = swcontext.getContext().getRequestDispatcher(uri); rd.forward(swcontext.getRequest(), swcontext.getResponse()); }
You'll notice that's the same code as the other PerformForward class with the exception that I call processTilesDefinition(). The processTilesDefinition() method does the same thing as processTilesDefinition() in TilesRequestProcessor except that it adds the init() code that ensures a DefinitionsFactory is in place. If processTilesDefinition() returns true we exit the command, otherwise we perform the standard forward logic.
There's a few problems I have with the way this is written.
First, I would like to make processTilesDefinition() its own command because it will be inserted here and in the replacement for processForward() if/when that gets supported. But I can't make it a Command because it is only inserted here conditionally. So either 1) PerformForward will need to be broken up into multiple commands so logic such as Tiles can be inserted, 2) processTilesDefinition should be placed in a util class so it can be called from anywhere, or 3) I take a different approach with Tiles and not extend AbstractPerformForward at all. Maybe there's another approach. Any suggestions?
There's a "conditional behavior" use case in the existing code as well; when validation fails, we want to redisplay the input form. Originally, I modelled this command as a Chain that conditionally executed its child commands, but that seemed a little hokey. Now, this command definition says:
<command className="org.apache.struts.chain.servlet.ValidateActionForm"
failureCommand="servlet-validation-failure"/>so I'm declaring the name of another chain to go execute if validation fails. The actual code that does the conditional execution looks like this (in the abstract base class):
// Call the environment-specific protected validate() method in our implementation class
ActionErrors errors = validate(context, actionConfig, actionForm);
// If there were no errors, proceed normally
if ((errors == null) || (errors.isEmpty()) {
return (false); // Proceed with the main chain
} // Execute the specified validation failure command
try {
Catalog catalog = (Catalog)
context.get(getCatalogKey());
Command command = catalog.getCommand(getFailureCommand());
command.execute(context);
} catch (Exception e) {
... deal with exception ...
}
return (true); // Terminate the main chainThis approach seems more scalable -- for example, you can have several different conditional options, you aren't binding all the behavior definitions into a single chain definition, and you can decide based on your needs whether to return false or true from the main command (which dictates whether the owning chain thinks processing is complete or not).
Secondly, there's a lot of stuff in processTilesDefinition() that could and probably should go into an abstract base class with a standard implementation like all the other commands. Would it be preferable to create another abstract extension of AbstractPerformForward or just go down a different path altogether (i.e. AbstractPerformTilesForward extends AbstractPerformForward vs. AbstractPerformTilesForward implements Command)? Again, it's kind of a problem because the Tiles functionality is executed conditionally. If certain conditions are not met, the standard processing occurs. The Command interface doesn't support "if-else" logic and I don't think that's a shortcoming. I'd just assume write my if-else logic in Java as opposed to XML.
Or, factor it into separate commands that can be looked up and executed at the appropriate times. Then, you're building your conditional logic in Java, but only the "if-then-else" statement itself; the actual straight line processing is still configured with a chain.
Another thing is that the current TilesRequestProcessor doesn't support redirects unless I've misread it, so neither does the chain version. Also TilesRequestProcessor will do an include instead of a forward in certain places, so I included that functionality in the chain version.
Next, is there any problem with doing the initDefinitionsFactory() logic on every execution of the command instead of only during the init(). As far as I can tell, this function only checks ot see if the DefinitionsFactory is present. It doesn't actually initialize anything. That's all done in the PlugIn. I don't think there's the equivalent of the init() method in the chain interface. Should there be?
Which brings me to the PlugIn. The TilesPlugin verifies that the RequestProcessor is a TilesRequestProcessor and returns an error if not. Should we 1) remove this from TilesPlugin, 2) write another TilesPlugin that does not do this verification, 3) add ComposableRequestProcessor to the verification in TilesPlugin, or 4) write a TilesComposableRequestProcessor (yuck)?
I think that's enough for now. Let me know if some of that doesn't make sense.
I suspect there is a lot of useful refactoring to be had in order to employ chain underneath something like Tiles; perhaps the best approach for development might be to go ahead and let TilesPlugIn configure things the way it wants, but add your own plugin (running after the Tiles one) to replace the configured RequestProcessor for executing your chain(s) instead.
I'll sure be interested to see how this works out.
Thanks, Greg
Craig
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
