View Full Version : How do you bind session attributes to object with webflow?
garpinc2
May 5th, 2005, 12:28 PM
How do you bind session attributes to an object with webflow? FormAction automatically binds request attributes but how do you bind session attributes or for that matter attributes from any arbitrary scope?
wiggles
May 5th, 2005, 03:49 PM
I think you are confusing the HttpServletRequest with the WebFlow RequestContext. WebFlow binds parameters from the HttpServletRequest, not attributes fromt the RequestContext.
garpinc2
May 5th, 2005, 03:58 PM
I want to bind from HttpSession
Keith Donald
May 5th, 2005, 10:27 PM
FormAction takes event parameters and binds them to a backing object in a configurable scope (either request or flow.) In a servlet environment, the Event is a HttpServletRequestEvent, so the parameters are actually request parameters...
What are you trying to do again? Connect parameters in the request to a object in the session? In this case, I'd use a custom action to take the "context.getOriginatingEvent().getParameters()" and bind them to the session object, casting the Originating event to a HttpServletRequestEvent and accessing the HttpSession.
garpinc2
May 5th, 2005, 10:56 PM
rather than accessing the session object through request.getSession I want to inject the dependencies which exist in session into the backing object. i.e rather than service locator I want IoC.
Keith Donald
May 6th, 2005, 08:00 AM
UPDATED
Hmmm....well, that's kind of tricky as you need the request to pull the object out of the session to be thread safe.
My recommendation is to have a locator interface that decouples you from any specific object retrieval strategy:
public HttpSessionObjectAccessor implements FlowObjectAccessor {
public Object getObject(String objectName, RequestContext context) {
HttpServletEvent event = (HttpServletEvent)context.getOriginatingEvent() ;
return event.getRequest().getSession().getAttribute(objec tName);
}
}
public interface FlowObjectAccessor {
public Object getObject(String objectName, RequestContext context);
}
This is actually a pretty good pattern, worth consideration adding as a suport class.
Inject a configured instance into your action code, and then invoke it on each request to get the session object.
Keith
garpinc2
May 6th, 2005, 09:14 AM
How does that work when there are > 1 objects?
Keith Donald
May 6th, 2005, 10:34 AM
We'll need to change the interface to take a object name parameter...
garpinc2
May 6th, 2005, 11:15 AM
Doesn't seem to accomplish what I want... i.e: still service locator vs Ioc.
Let me see if I understand.
1) Web flow is like struts in that instance variables that are specific to request and likely to change from user to user are bad because the action classes are actually singletons not instantiated per request. right?
2) Here is the example in practical guide
<bean id="person.Search.criteria.formAction"
class="org.springframework.web.flow.action.FormAction">
<property name="formObjectName" value="query"/>
<property name="formObjectClass" value="org.springframework.samples.phonebook.domain.Phone BookQuery"/>
<property name="formObjectScopeAsString" value="flow"/>
<property name="validator">
<bean id="queryValidator" class="org.springframework.samples.phonebook.domain.Phone BookQueryValidator"/>
</property>
</bean>
This code caused PhoneBookQuery to be created in flow scope if it didn't already exist and it's attributes to be set from requests.
Now what I want is as follows.
Attributes exist in session which apply to an object that may or may not exists in the scope I want. Similar to above I want the object of the specified class to be created if it doesn't exist in the scope I want and it's values to be populated from the session rather than request. Better yet rather than it explicitly being populated from request as above I would like something like:
<bean id="person.Search.criteria.formAction"
class="org.springframework.web.flow.action.FormAction">
<property name="populateFrom" value="request,session,flow"/>
<property name="formObjectName" value="query"/>
<property name="formObjectClass" value="org.springframework.samples.phonebook.domain.Phone BookQuery"/>
<property name="formObjectScopeAsString" value="flow"/>
<property name="validator">
<bean id="queryValidator" class="org.springframework.samples.phonebook.domain.Phone BookQueryValidator"/>
</property>
</bean>
See <property name="populateFrom" value="request,session"/>
That way it will try to populate from request first and then from session or in what ever order I specify with the first in the order taking precedent if it exists in both places.
Then
PhoneBookQuery query = (PhoneBookQuery)context.getFlowScope().getAttribut e("query");
as in your example is sufficient to retreive the created object. I don't see the benefit of the interface that you described.
Keith Donald
May 6th, 2005, 11:49 AM
Well, the benefit of what I proposed is it fully decouples your actions from the HttpSession. That's it, really.
Yes, actions are singletons, as documented in the Action interface...
I''m having a hard time following you. I'll try and give a read again in a bit.
garpinc2
May 6th, 2005, 11:50 AM
Parameters vs Attributes... So in addition I guess I need a mechanism to populate from attributes not just parameters so
<property name="populateFrom" value="requestParam,request,session,flow"/>
with the default being requestParam
Keith Donald
May 6th, 2005, 11:52 AM
When you say populate, you mean "bind" right. You want to bind input in some attribute source to a backing form object? You want to be able to take into account multiple attribute sources in a configurable order?
I think I get it, but please, let's use our terminology or else confusion results.
One issue here is there is no 'session' construct known to the web flow system: only request and flow. I'm not sure if it makes sense to add first-class support for session attributes, attempting to mask the location of the session store for different environments. We'll have to see about that.
I agree with a "AttributeSourceChain" that will try to resolve a attribute in one or more ordered sources, like event/request/flow. Sounds useful - if we can get the spring data binder retrofitted to work with the attribute source interface, it shouldn't be a big deal.
Keith
garpinc2
May 6th, 2005, 12:21 PM
Actuially it's fine not to have first-class support for session attributes. If I want to access a session attribute directly I can always put it into request scope or flow scope using the same method we described.
I'll give the actual example of what I am trying to accomplish.
in session there are 2 attributes (type:Integer and specialty:String)
I want to encapsulate those attributes in a Profile object that then I can access typesafely from my action. so in this case I would have a backing form object containing type and specialty. Workflow FormAction would automatically bind to my backing form object from session and I can chose to have this object in either request or flow scope so in my action I can simply call
Profile profile = (Profile)context.getRequestScope().getAttribute ("profile");
The only reason I would want to refer to my object in session scope is if the object is already there in which case your interface accomplishes this nicely. I don't like the interface method however because there could be different sources for the object (besides just session) and I didn't see how that would be addressed.
garpinc2
May 6th, 2005, 12:46 PM
Basically what you are say in the implementation as to where the source is is up to me but in that case I should be able to inject the implementation for resolving each object
i.e: action itself implements
public Object getObject(String objectName, RequestContext context);
public void setObjectRetreivalImplementation(Hashmap implementations);
The implementation will look up in implementations hashmap and call method
public Object getObject(String objectName, RequestContext context);
which all implementations will have to implement.
So basically I just inject in implementations and everything should work...
garpinc2
May 10th, 2005, 12:55 PM
I opened a JIRA for this:
http://opensource.atlassian.com/projects/spring/browse/SPR-934
garpinc2
May 10th, 2005, 04:31 PM
I'm thinking that FormAction is currently too limited. It only allows creation of one form object per action. My thought is that if using hibernate or another ORM tool what you want instead is multiple objects (one per each domain object) so that at the time you run the action the object graph will already be created and all you need to do is extract it from the context and use it to query or persist. This avoids the bulk of the work generally in actions to assemble domain objects.
So basically this is IoC for a scope object to either create object in scope and set properties, set property on object already in specified scope or add object to collection of an object already in specified scope.
<bean id="phoneBookInjector" class="spring.ContextInjector">
<property name="defaultAttributeSourceChain" value="parameter,request,flow,session,context"/>
<property name="defaultPopulation" value="true"/>
<property name="propertyAttributeResolvers">
<map>
<entry key="phoneNumber">
<bean class="spring.PortletAttributeResolver">
<property name="from" value="PHONE_NUMBER"/>
<property name="attributeSourceChain"
value="parameter,request,flow,session,context"/>
</bean>
</entry>
</map>
</property>
<property name="formObjectName" value="phoneBook"/>
<property name="formObjectClass" value="app.domain.PhoneBook"/>
<property name="formObjectScopeAsString" value="request"/>
<property name="validator">
<bean id="queryValidator" class="app.domain.PhoneBookValidator"/>
</property>
</bean>
<bean id="phoneBookInjector">
<property name="propertyResolver">
<map>
<entry key="phoneNumber">
<bean class="spring.PropertyResolver">
<property name="to" value="street">
<property name="from" value="STREET"/>
<property name="attributeSourceChain"
value="request"/>
</bean>
</entry>
</map>
</property>
<property name="formObjectName" value="phoneBook.addresses"/>
<property name="formObjectClass" value="app.domain.PhoneBook"/>
<property name="formObjectScopeAsString" value="request"/>
<property name="validator">
<bean id="queryValidator" class="app.domain.PhoneBookValidator"/>
</property>
</bean>
<bean id="app.phone.SavePhone"
class="org.springframework.web.flow.action.SavePhoneInjec tionAction">
<property name="injectors">
<list>
<ref bean="phoneBookInjector"/>
<ref bean="phoneBookAddressInjector"/>
</list>
</proerty>
</bean>
Basically an explanation of the intended implementation is as follows.
The functionality of InjectionAction class is as follows:
1) In example action in flow SavePhone is implemented by SavePhoneInjectionAction which extends InjectionAction and probably extends FormAction (if FormAction is not deprecated)
2) It reads list of injectors in order and proccess them
The functionality of ContextInjector class is as follows:
1) instantiate formObjectClass in scope if it does not already exist.
2) for each property in class find a property resolver in following precedence
a) an explicit PropertyResolver in propertyResolvers
b) a default PropertyResolver (which can be injected) if none specified and defaultPopulation is true
3) for each property instantiate PropertyResolver and
a) inject defaultAttributeSourceChain if not already specified for the property resolver
b) inject property name as "to"
4) Once instantiated object has been populated, validate if necessary and then set formObjectName to point to object in the scope formObjectScopeAsString specified
5) If formObjectName in the form "phoneBook.addresses" and addresses is a collection then find phoneBook in the scope specified by formObjectScopeAsString and invoke add method to insert newly populated object in collection.
The functionality of PropertyResolver is as follows:
1) For each scope specified in attributeSourceChain, search in that scope for "from" name or "to" name if "from" not specified.
To retreive the object PhoneBook you would simply call:
PhoneBook phoneBook = (PhoneBook)context.getRequestScope().getAttribu te("phoneBook");
and save it since it is already populated (both the properties and the associated addresses).
I am looking for feedback in the following categories:
1) Will this be as useful to you as it will be for me?
2) Does anyone have any feedback on the intended implementation (including improvements)?
3) Is anyone interested in implementing or helping with the intended implementation?
yfmoan
May 10th, 2005, 08:17 PM
Hi garpinc2 ,
u have 2 <bean id="phoneBookInjector"/> in the your above code.
If u need multi form objects , then multi validators are unavoidable , right ?
moon
garpinc2
May 10th, 2005, 08:30 PM
No actually... you can have as many injectors as you'd like thus as many form beans as you'd like but each injector has one validator.. Am I missing something? app.phone.SavePhone is where multiple injectors are applied...
Powered by vBulletin® Version 4.2.1 Copyright © 2013 vBulletin Solutions, Inc. All rights reserved.