-----Original Message----- From: Jing Zhou [mailto:[EMAIL PROTECTED]
----- Original Message ----- From: "PILGRIM, Peter, FM" <[EMAIL PROTECTED]>
To: "'Struts Developers List'" <[EMAIL PROTECTED]>
Sent: Monday, October 13, 2003 5:39 AM
Subject: RE: Struts-chain Behavior Discussion
*Question of Architecture*Chaining debate?
Does this mean that we have solved the hideous Action
In theory an Struts Action could be refactored to be a type of ``Command'' and therefor Actions could be chained.Whenever there is a debate and each side seems to be very reasonable
in certain contexts, it is more likely the system itself is in-complete or
fails to meet the requirements. We use Controller Delegation Model
and/or Event Handler(s) to deal with such problems.
What are you talking about here? What I am talking about or rather
aiming the ability create common Struts components.
Suppose team T1 invents a generic web search engine component.
Let say another team T2 wanted to use that component, say
embed it is in own JSP views using say Tiles.
How does the search component forward or transfer control to the external component that T2.
One way could be to use an action chain. First, one writes a Struts
Action in such way that it puts a known public bean or put the bean
in the form bean. Whatever. This Output Bean contains contains
your search result or selected object. Second, one can generate a dynamic ActionForward that names the second Action
to forward.
But I am thinkgin Craig has given us an alternative option, of maybe using a Command to do this. But if and only if an Action is refactored as a Command.
It sort of is in the existing proof of concept (i.e. there is an adapter Command that can call an existing Action), but that will be of more interest to people with existing code that want to use it in a CoR based request processor. I'm imagining that new code based on such a pattern would be constructed out of Commands in the first place.
Commons-chain is not intended to solve that debate topic originally. Last year, people found when they have two RequestProcessor(s), say A and B, if they need to design a new RequestProcessor C that has methods from both A and B, the best they can do is to let C extend one of them, say A, and then copy the methods (possibly line by line) from B. So a configurable RequestProcessor is somehow needed to reuse portions of several RequestProcessor(s). This is what commons-chain is intended to solve initially.
Ay Ay, Captain! I don't mean to rain on the parade. I was involved
in the composable request processor discussion way way back in the
summer. As the Expresso core committer who integrated Struts 1.1 I found also a futuristic design issues merging the TilesRequestProcessor and our own ExpressoRequestProcessor.
How would you merge the next processor, and the next one, and
the next one after that.
But the problems have not been cleanly solved in regard to reusability.
When a Command is deeply coupled with other Command(s) in a
chain through one or more flags/states in Context (say cancel flag,
invalid token flag, invalid validation flag, etc.) its reusability in a different chain is very low in theory or very difficult to use (you need
to configure many flags to get it work and subject to the condition
the new Context offers the same set of flags).
I dont understand the reusability problems. To me having
read the Gang of Four original book, the design of Chain
of Responsibilty and Craig's code and what he has said
to me (to us) on list makes this a non-issue. Actual design
pattern is a linear path. The only difference I can see is the context, which I guess you should treat it as opaque object any way.
If you write a Command that need various context setting
that you should declare in the javadoc or documentation.
The code I've written to date illustrates how I propose to reduce problems of interdependence between commands. For every case where your command expects an input attribute in the Context (or plans to create an output attribute), have a String property on the Command's implementation class made up of the attribute name with "Key" on the end, that documents the name of the attribute you intend to use. This does several things for you:
* It self documents the appropriate attribute keys if you are diligent about applying this design pattern.
* It makes the actual keys to be used configurable (improves reusability in different environments).
* It means that your code can *ask* a Command instance where it stored something, or plans to store something, instead of just assuming.
For example, the Command that selects an appropriate ActionConfig based on the input path (the Command version of the processMapping) needs to store the ActionConfig it found under an agreed-upon location in order for later commands to find it. Snippets of this class (AbstractSelectAction) include:
public abstract class AbstractSelectAction implements Command {
private String actionConfigKey = Constants.ACTION_CONFIG_KEY; public String getActionConfigKey() { return (actionConfigKey); } public void setActionConfigKey(String actionConfigKey) { this.actionConfigKey = actionConfigKey; }
public boolean execute(Context context) throws Exception {
...
ActionConfig actionConfig = ...; // Look up the correct ActionConfig instance
context.put(getActionConfigKey(), actionConfig); // Store under the specified key
return (false); // Let the chain continue
}
Note also the use of constants to set the default value across all the commands that have an "actionConfig" property.
One of the core values of decomposing things into chains, however, relates to unit testing -- an individual command does not care *who* created its input parameters or *how* they got built. It only cares that they exist at the right time. Thus, it is much easier to set up unit testing frameworks for your fine grained commands: set up some input objects (mock objects if needed, like a MockServletContext), call the command's execute() method, check the return value for what is expected, and check the Context to observe the state changes that the Command was supposed to have created.
I did a little exercise to reduce the command coupling degree by introducing a NestCommand. Like LookupCommand to execute a specified chain, NestCommand always return false regardless what nested chain returns. So a termination of nested chains does not force a termination of calling chains when NestCommand is used. The following picture illustrates the idea:
You said your NestCommand always returns `false'
Main Chain: NestCommand Nested Chain: NestedCommand1 NestedCommand2 NestedCommand3 LastCommand
If any nested commands return true, the control flow will *jump* to LastCommand. It seems to give an answer to my original question without the notion of branch behavior :-)
And this paragraph NestCommand can return true. This conflicts
with the first. What if NestCommand return false does that
not terminate the chain, and thus stop LastCommand from executing.
NestedCommand2 does not need to check any flag set by NestedCommand1 in this way and NestedCommand3 does not need to check any flags set by NestedCommand1 and 2. They could be reused in other chains without concerning about flags.
It sounds to me like you trying to create conditional commands.
A | B-D-E | C
If B is a command that has IF THEN behaviour that is on a boolean flag V
then
if V == true then the order of execution is A,B,D,E,C (the execution terminates after C) otherwise V == false then order of executuion A,B,C (the execution terminates after C)
As Craig said, he wants to remain in the original linear CoR design pattern. I can really the problem is that branching leads to different conditional behaviours.
Let's consider SWITCH behaviour. If node B is a true branch then
if V == true then the order of execution is A,B,D,E (the execution terminates after E) otherwise V == false then order of executuion A,B,C (the execution terminates after C)
I can see why he doesn't want to get it to this, because it out-of-scope
for what the original design aims were.
To be clear ... Jing, or anyone else who needs it, is welcome to build their own Command implementations that do things like this. It's just that explicit support for conditional behavior does not belong in the fundamental commons-chain APIs.
--
Peter Pilgrim,
Struts/J2EE Consultant, RBoS FM, Risk IT
Tel: +44 (0)207-375-4923
Craig
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]