[ http://issues.apache.org/struts/browse/WW-1514?page=comments#action_38807 
] 
            
Mark Menard commented on WW-1514:
---------------------------------

Hi Don,

I went back and took a longer look at ScopedModelDrivenInterceptor. A flight 
with no Internet access gave me a chance. 

I looked at it previously and decided not to change it because: 1) it is an 
existing part of the framework, 2) it uses an object factory to instantiate it 
models, and 3) it is designed to store only one version of the model per user. 
I also previously thought it depended on being configured in the config file, 
and that's partly true. My goal was to have an interceptor that you could just 
add and use. No configuration needed.

Basically the "idea" or "contract" is a different. The way I've implemented 
this the instantiation of the model object is delegated to the action, using 
the prepareConversationModel() method, much like Prepareable#prepare(). I 
prefer that, because it would allow you to set up any things that are needed to 
display your view, such as filling in drop down, etc. once. 

I first started out also having my Action implement Prepareable, and using the 
prepare() method to setup my mode, but then I needed to add a boolean to my 
conversation model to mark it "prepared" so I wouldn't do that work again. 
That's why I went with the prepareConversationModel() method. Setup your 
conversation model once and be done with it.

ScopedModelDrivenInterceptor uses the ObjectFactory to instantiate the model, 
which cuts the action out of the process. You'd need to have an indicator in 
the model to check if it has been initialized every time you run the action. 
Now, the ObjectFactory method is nice, because you can tie it back into Spring, 
but you could just as easily use a service bean in your action to instantiate 
your model based on Spring configuration.

Additionally getting your model from the factory doesn't allow you to use a 
parameter from the request to determine the model your going to use. Let's say 
you were working on an order using the prepareConversationModel() method. As 
long as you have the parameter interceptor in front of the conversation model 
interceptor you would have parameters to determine what order to place in the 
model. You could use your service bean injected into your action to look the 
order up, and your done. It's there for the using. In the 
ScopedModelDrivenInterceptor you would have to get the order in your prepare() 
method, and then check on each subsequent submission that the order is already 
filled in. To me that is cludgy at best.

Now, with all that said I could fold the conversation scope handling into the 
existing interceptor, but the I think the "contract" is quite different. I 
think the prepareConversationModel() method is powerful. It gives the developer 
a chance to create the model exactly to their liking, and not just based on 
configuration.

Some other thoughts on extending and improving this.

With the addition of a ConversationModelHolder, that stored the time the model 
was instantiated, the conversationId, and the conversationModel, which would be 
stored in the conversationMap instead of the model itself,  you could add these 
to a "global" collection of converstaionModels for all users. This would allow 
you to expire old abandoned conversations. 

The only issue with that is if a user has submitted a form that they had open 
for a long time and the model was expired, we'd need to have some means of 
telling the action, so it could display a message to the user. It could be as 
simple as adding setConversationExpired(Boolean) to the ConversationScopeAware 
interface. What I'm not sure is if you would want to instantiate a new 
conversationModel right then, so the user could start over immediately or not. 
I'm thinking you would, but if the conversationModel was parameterized you 
might no long have the parameters, but my inclination would be to leave that up 
to the Action developer.

Mark

> Conversation Scope
> ------------------
>
>                 Key: WW-1514
>                 URL: http://issues.apache.org/struts/browse/WW-1514
>             Project: Struts 2
>          Issue Type: New Feature
>          Components: Interceptors
>            Reporter: Mark Menard
>            Priority: Minor
>         Attachments: ConversationScopeAware.java, 
> ConversationScopeInterceptor.java
>
>
> Problem: A user may need to, or be able to use, multiple tabs on the same 
> work flow process,  using different data, that would usually be stored in the 
> user's session. The problem is if you use the Session scope you can only have 
> one version of the users data in memory. This can result in a "crossing of 
> the streams" between their two (or more) independent work flows. Ad-hoc 
> solutions on a per action basis could be implemented, but this is awkward at 
> best.
> Example: User opens two processes of the same type, using the same action(s), 
> in separate tabs in their browser, working on independent data sets. Each 
> data model must be stored separately in their session.
> Solution: Implement a "Conversation Scope" using an Interceptor and a marker 
> interface for Actions to implement that will transparently manage multiple 
> independent conversations of the same type on independent data sets.
> ConversationScopeInterceptor: This interceptor does the hard lifting. It 
> maintains a conversationMap in each users session that allows for the storage 
> of multiple converstaionModels. The conversationModels are keyed using an 
> incrementing int, and the current time in milliseconds when the conversation 
> began. The incrementing of the conversation counter is synchronized. 
> Additionally the interceptor pushes the conversationModel onto the ValueStack 
> much the same way as ModelDriven pushes the model onto the ValueStack.
> ConversationScopeAware: This is a marker interface for Actions to implement. 
> It includes methods to set/get the conersationId, set/get/prepare the 
> conversationModel. prepareConversationModel() is called at the start of a 
> conversation and allows the action to prepare the data model the user will be 
> working with over the conversation.
> Motivation: The major motivation for this was work that we did in Struts 1 to 
> work around the pervasive use of session forms. We realized that users could 
> cross the streams with multiple forms in memory. To work around this we 
> created a deep Action hierarchy that handled swapping the "conversation" data 
> into and out of a Map backed form. Although Struts 2 does not suffer from the 
> same pervasive need to use session forms there are times when a conversation 
> is needed and with modern browsers and the wide usage of tabs, it is just a 
> matter of time before a user opens two tabs on the same type of conversation.
> View requirements: In order to track the conversation the HTML form must 
> contain:
> <input type="hidden" name="S2_CONVERSATION_ID" value="<the_current 
> conversation_id">
> The current conversation id is available as a property of any Action that 
> implements ConversationScopeAware and can be included with the following:
> <s:hidden name="S2_CONVERSATION_ID" value="%{conversationId}" />
> Possible Improvements: Slip stream this right into ModelAware, and just use 
> getModel() from the ModelAware interface and supplement it with set and 
> prepare methods. I didn't want to do that initially because I'm not sure it 
> would be appropriate, nor do I like non-explicit things. It would make moving 
> a model aware action to a conversation model scope easier, but using 
> coversationModel just means changing getModel() to getConversationModel() in 
> your action.
> Additionally this could be improved by setting the conversatonId onto the 
> ValueStack, and retrieving it off the stack in the view. This would allow for 
> the removal of the get/setConversationId() from the ConversationScopeAware 
> interface. I just couldn't figure out how to do that.

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: 
http://issues.apache.org/struts/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

Reply via email to