----- Original Message ----- From: "Craig R. McClanahan" <[EMAIL PROTECTED]> To: "Struts Developers List" <[EMAIL PROTECTED]> Sent: Saturday, October 04, 2003 1:02 PM Subject: Re: Struts-chain Behavior Discussion
> > They could indeed be made part of the chain, and "checking the current > state to see if it's appropriate for me to do anything" is certainly > more in the spirit of the original CoR pattern. However, I worry a > little that creating the need for that state information increases > coupling between commands, and therefore increases the complexity of > reusing commands in alternative chains. Exactly. The index-based jumping creates the need for a command to recognize a fixed position to jump to. That is why I said they have a similar problem - coupling between commands. They have to either honor a state/flag or a fixed position. And such coupling can be easily destroyed by adding a command in between without recognizing the state/flag or changing the target position. > > In the real-life use case we actually have in the CVS repository (the > one in struts-chain, which actually does work :-), the case in question > was to deal with the fact that the Struts request processing lifecycle > has a branch in it, based on whether or not validation succeeds. The > basic flow goes like this (using the class name of the command > implementation class, and presuming we're all pretty familiar with the > corresponding RequestProcessor behavior): > > (1) LookupCommand (analogous to the processPreprocess() hook) > (2) ExceptionCatcher (no direct analog - used to implement exception > mapping behavior) > (3) SelectLocale > (4) SelectAction > (5) CreateActionForm > (6) PopulateActionForm > (7) ValidateActionForm > (8) CreateAction > (9) ExecuteAction > (10) PerformForward > > The conditional behavior happens in Step (7) -- if validation fails, an > alternative path is desired: > (7a) SelectInput (uses the "input" attribute on the <action>) > (7b) PerformForward > > At the moment, this is implemented as a branch, to a separate chain that > the author of ValidateActionForm need not even know the name of at > design time (it's a configuration property). If ValidateActionForm > detects a failure, it looks up an alternate chain in the Catalog, > executes this chain, and then returns true (so that steps 8-10 of the > original chain are never executed). Note that step (10) in the original > chain and step (7b) in the alternate chain share a single Command > implementation instance, because Struts ends up doing the same thing > either way (RequestDispatcher.forward() or redirect based on what > ActionForward it is passed). Nothing had to be coded twice. > My question is whether or not we could find a simpler syntax in the chain-config.xml and implement "jump behavior". This is because we could have a lot of "jump to the last command" or "jump to the second to last command", etc. It is very easy to relate "branch behavior" and "jump behavior" to a rule engine or workflow engine, so people might get me wrong in the early reactions. Although we use the term "branch behavior" and "jump behavior" in a chain, we should not see the "if" and "go to" statements in the chain-config.xml file. So a chain is still a fully connected chain (no skipping by definition). The "if" statement is simulated by commands which provide "branch behavior" by terminating the current chain and invoking a nested chain. Could we simulate the "go to" statement without breaking the chain? Note that the chain's execution path should be remained connected because we need to do post processing in the reversed order. The answer, I deeply believe, is yes. We know that index-based jumping is flawed, so we should use label-based jumping. When a command is added to a chain, an optional label could be specified which must be unique within the chain. For example, <chains> <chain name="servlet-standard"> <command className="o.a.s.c.s.ValidationFormAction" jumpLabel="last" /> .... <!-- the label "last" is specified in the next command --> <command label="last" className="o.a.s.c.s.PerformForward" /> </chain> </chains> Now, in the ValidationFormAction class, we almost do the same thing as before except in two statements: > 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()); // we get the main chain again Command command = catalog.getCommand("servlet-standard"); // execute the main chain starting with the given label ((Chain)command).execute(context, getJumpLabel()); > } catch (Exception e) { > ... deal with exception ... > } > return (true); // Terminate the main chain > > Craig > Here, we introduce a concept of implicit chains. Whenever you add a command with its label in a chain, an implicit chain is defined starting from that command to the end of the chain. The Chain.execute(Context ctx, String label) will execute the chain from the labeled command. So, the "go to" statement is actually simulated by a nested chain which is implicitly defined in the main chain. Obviously, we do not have to require each command to honor a state/flag, and the fixed index problem is also solved. Now, looped commands can be specified as follows. (Note that you can not simulate the looped commands using "branch behavior"). <chains> <chain name="demo"> <command className="A" /> <command label="start" className="B" /> // ... other looped commands <command jumpLabel="start" className="C" /> </chain> </chains> This way also saves a lot of repeatedly defined commands/chains in the chain-config.xml file. What I am asking is still a Chain, but with additional capabilities and more concise syntaxes. Although its syntaxes looks like a "jump", its implementation never jumps. (It may not be necessary to add new execute method if we could put label into Context somewhere, so API changes are very limited). Any ideas or comments on the algorithm? Are there any technique flaws? If anywhere is not clear or could be improved, please point it out. Jing Netspread Carrier http://www.netspread.com --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]