Announcement Announcement Module
Collapse
No announcement yet.
Proposal for Object Graph Assembler Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Proposal for Object Graph Assembler

    I think this issue deserved it's own topic so here goes:

    Objective:
    This proposal fulfils the need of the Assembler and Mapper pattern described by Martin fowler. I find in almost all situations that the bulk of work in building a web app is to collect data from varying locations and ultimately construct an object graph that you can then persist or use for querying. This is especially true if using hibernate or another ORM tool.

    Struts Minded people say don't put much code in framework related class because if framework changes you will have to refactor class. Hibernate minded people say don't use DTO dump a ton of code in action to assemble objects and then make business logic be a simple save of the object graph. My thought is that this code to assemble objects is usually driven by the business and is also in constant flux as the business and technical requirements change.

    Where the data is coming from changes. So this proposal is aimed at providing the flexibility of being able to change these details in configuration and it also aims to keep the controller layer thin.

    Conclusion:
    With this mechanism, by the time you get to an action in the controller layer, the object graph of populated objects would already be pre-populated and waiting for you in the scope you specify. So the action code would then be very simple or non-existent.

    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.

    Example Configuration
    Code:
    <bean id="phoneBookInjector" class="spring.ContextInjector">
        <property name="defaultAttributeSourceChain" value="parameter,request,flow,session,context"/>
        <property name="defaultPopulation" value="true"/>
        <property name="attributeResolvers">
           <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</value>
        </property> 
        <property name="formObjectClass">
              <value>app.domain.PhoneBook</value> 
        </property>
        <property name="formObjectScopeAsString">
              <value>request</value>
        </property> 
        <property name="validator"> 
            <bean id="queryValidator" class="app.domain.PhoneBookValidator"/> 
        </property> 
    </bean>
    
    <bean id="phoneBookAddressInjector" class="spring.ContextInjector">>
        <property name="attributeResolvers">
           <map>
             <entry key="street">
               <bean class="spring.PortletAttributeResolver">
                  <property name="from" value="STREET"/> 
                 <property name="attributeSourceChain" 
                                 value="request"/>
               </bean>
             </entry>
           </map>
        </property>
        <property name="formObjectName".
             <value>phoneBook.addresses</value>
        </property> 
        <property name="formObjectClass">
             <value>app.domain.PhoneBookAddress</value>
        </property> 
        <property name="formObjectScopeAsString">
             <value>request</value>
        </property> 
        <property name="validator"> 
            <bean id="queryValidator" class="app.domain.PhoneBookValidator"/> 
        </property> 
    </bean>
    
    <bean id="app.phone.SavePhone" 
       class="org.springframework.web.flow.action.InjectionAction"> 
       <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) extends AbstractAction
    2) It reads list of injectors in order and proccess them calling bindAndValidate


    The functionality of ContextInjector class is as follows:
    1) extends from FormAction
    2) bindAndValidate of FormAction works as usual except that
    • a) property resolution is done with attributeResolvers instead of just using request parameters
      b) These attributeResolvers support retrieval from nested properties
      c) it has the ability to populate nested properties on objects already in scope
      d) properties can be assigned from constant injected value instead of attributeResolvers. Value can be defined at the ContextInjector level in which case properties are resolved from object if property itself doesn't have a value assigned to it rather than an attribute resolver.
      e) Something I haven't fully thought through, but it makes a lot of sense, is that AttributeResolver can be assigned at ContextInjector level to populate the object itself. This would allow developers to create a AttributeResolver that accepts a service layer method as an injection returning the object that could then be used to populate scope object. So you could basically set up the entire view without a line of code.
      f) If property resolver is not an attributeResolver and it is a constant value instead. Property is automatically set to value.
    3) 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
    4) for each property instantiate PropertyResolver and
    • a) inject defaultAttributeSourceChain if not already specified for the property resolver
      b) inject property name as "to"
    5) Once instantiated object has been populated, validate if necessary and then set formObjectName to point to object in the scope formObjectScopeAsString specified

    6) 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:

    Code:
    PhoneBook phoneBook = &#40;PhoneBook&#41;context.getRequestScope&#40;&#41;.getAttribute&#40;"phoneBook"&#41;;
    and save it since it is already populated (both the properties and the associated addresses).

  • #2
    Here is the working code I have so far...

    Here is the working code I have so far... Please comment.

    InjectionAction
    Code:
    package org.springframework.web.flow.action;
    
    import java.util.Iterator;
    import java.util.List;
    
    
    import org.springframework.web.flow.Event;
    import org.springframework.web.flow.RequestContext;
    
    /**
     * Action that implements logic calling injectors to inject objects into scope
     * 
     * @author Keith Garry Boyce
     */
    public class InjectionAction extends AbstractAction &#123;
    	private List injectors;
    
    	
    	/**
    	 * @param injectors The injectors to set.
    	 */
    	public void setInjectors&#40;List injectors&#41; &#123;
    		this.injectors = injectors;
    	&#125;
    	/* &#40;non-Javadoc&#41;
    	 * @see org.springframework.web.flow.action.AbstractAction#doExecuteAction&#40;org.springframework.web.flow.RequestContext&#41;
    	 */
    	protected Event doExecuteAction&#40;RequestContext context&#41; throws Exception &#123;
    		Event outcome = success&#40;&#41;;
    		for &#40;Iterator iter = injectors.iterator&#40;&#41;; iter.hasNext&#40;&#41;;&#41; &#123;
    			ContextInjector injector = &#40;ContextInjector&#41; iter.next&#40;&#41;;
    			Event injectorStatus = injector.bindAndValidate&#40;context&#41;;
    			if &#40;! injectorStatus.toString&#40;&#41;.equals&#40;outcome.toString&#40;&#41;&#41;&#41; &#123;
    				outcome = injectorStatus;
    				//TODO&#58; decide if to continue with other injectors.
    				break;
    			&#125;
    			
    		&#125;
    		return outcome;
    	&#125;
    
    &#125;
    ContextInjector
    Code:
    package org.springframework.web.flow.action;
    
    import java.beans.PropertyDescriptor;
    import java.util.HashMap;
    import java.util.Map;
    
    
    import org.springframework.beans.BeanWrapper;
    import org.springframework.beans.BeanWrapperImpl;
    import org.springframework.beans.MutablePropertyValues;
    import org.springframework.beans.PropertyValue;
    import org.springframework.validation.DataBinder;
    import org.springframework.web.flow.Event;
    import org.springframework.web.flow.RequestContext;
    import org.springframework.web.flow.execution.portlet.PortletRequestEvent;
    import org.springframework.web.flow.execution.servlet.HttpServletRequestEvent;
    
    /**
     * Multi-action that implements logic dealing with input forms&#58; It extends FormAction to provide 
     * binding using AttributeResolvers
     * 
     * It works as follows&#58;
     * <ul>
     * <li>Extends from FormAction</li> 
     * <li>bindAndValidate of FormAction works as usual except that&#58;
     *     <ul>
     *     <li>property resolution is done with attributeResolvers instead of just using request 
     *         parameters</li>
     *     <li>These attributeResolvers support retrieval from nested properties</li>
     *     <li>it has the ability to populate nested properties on objects already in scope</li>
     *     <li>properties can be assigned from constant injected value instead of attributeResolvers. 
     *         Value can be defined at the ContextInjector level in which case properties are resolved 
     *         from object if property itself doesn't have a value assigned to it rather than an 
     *         attribute resolver.</li>
     *     <li>An AttributeResolver can be assigned at ContextInjector level to populate the object itself. 
     * 	       This would allow developers to create a AttributeResolver that accepts a service layer method 
     *         as an injection returning the object that could then be used to populate scope object. So you 
     *         could basically set up the entire view without a line of code.</li>
     *     <li>If property resolver is not an attributeResolver and it is a constant value instead. 
     *         Property is automatically set to value.</li>
     *     <ul>
     * </li> 
     * <li>for each property in class find a property resolver in following precedence 
     *     <ul>
     *     <li>an explicit PropertyResolver in propertyResolvers</li>
     *     <li>a default PropertyResolver &#40;which can be injected&#41; if none specified and 
     *         defaultPopulation is true</li>
     *     <ul>
     * </li> 
     * <li>for each property instantiate PropertyResolver and 
     *     <ul>
     *     <li>inject defaultAttributeSourceChain if not already specified for the property 
     *         resolver</li>
     *     <li>inject property name as "to"</li>
     *     <ul>
     * </li> 
     * <li>Once instantiated object has been populated, validate if necessary and then set 
     *     formObjectName to point to object in the scope formObjectScopeAsString specified</li> 
     * <li>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.</li>  
     * 
     * @author Keith Garry Boyce
     */
    public class ContextInjector extends FormAction &#123;
    	private String defaultAttributeSourceChain;
    	private Map attributeResolvers = new HashMap&#40;&#41;;
    	private AttributeResolver defaultAttributeResolver;
        private boolean defaultPopulation = false;
        private Object value;
        private AttributeResolver objectAttributeResolver;
    
    
        
    	/**
    	 * @param attributeResolver The attributeResolver to set.
    	 */
    	public void setObjectAttributeResolver&#40;AttributeResolver objectAttributeResolver&#41; &#123;
    		this.objectAttributeResolver = objectAttributeResolver;
    	&#125;
    	/**
    	 * @param value The value to set.
    	 */
    	public void setValue&#40;Object value&#41; &#123;
    		this.value = value;
    	&#125;
    	/**
    	 * @param defaultAttributeResolver The defaultAttributeResolver to set.
    	 */
    	public void setDefaultAttributeResolver&#40;
    			AttributeResolver defaultAttributeResolver&#41; &#123;
    		this.defaultAttributeResolver = defaultAttributeResolver;
    	&#125;
    	/**
    	 * @param attributeResolvers The attributeResolvers to set.
    	 */
    	public void setAttributeResolvers&#40;Map attributeResolvers&#41; &#123;
    		this.attributeResolvers = attributeResolvers;
    	&#125;
    
    	
    	
    	/**
    	 * @param defaultAttributeSourceChain The defaultAttributeSourceChain to set.
    	 */
    	public void setDefaultAttributeSourceChain&#40;
    			String defaultAttributeSourceChain&#41; &#123;
    		this.defaultAttributeSourceChain = defaultAttributeSourceChain;
    	&#125;
    	/**
    	 * @param defaultPopulation The defaultPopulation to set.
    	 */
    	public void setDefaultPopulation&#40;boolean defaultPopulation&#41; &#123;
    		this.defaultPopulation = defaultPopulation;
    	&#125;
    	public Object getObject&#40;String objectName, RequestContext context&#41;  &#123;
    		Object attributeResolverObj = attributeResolvers.get&#40;objectName&#41;;		
    		if &#40;attributeResolverObj != null&#41; &#123;
    			if &#40;attributeResolverObj instanceof AttributeResolver&#41; &#123;
    				AttributeResolver attributeResolver = &#40;AttributeResolver&#41; attributeResolvers.get&#40;objectName&#41;;
    				if &#40;! attributeResolver.isAttributeSourceChainDefined&#40;&#41;&#41; &#123;
    					attributeResolver.setAttributeSourceChain&#40;defaultAttributeSourceChain&#41;;
    				&#125;
    				return attributeResolver.getObject&#40;objectName,context&#41;;
    			&#125; else &#123;
    				// instead of using attribute resolver you can set property to constant value
    				return attributeResolverObj;
    			&#125;
    		&#125; else if &#40;defaultPopulation&#41; &#123;
    			if &#40;defaultAttributeResolver == null&#41; &#123;
    				if &#40;context.getOriginatingEvent&#40;&#41; instanceof PortletRequestEvent&#41; &#123;
    					defaultAttributeResolver = new PortletAttributeResolverImpl&#40;&#41;;
    				&#125; else if &#40;context.getOriginatingEvent&#40;&#41; instanceof HttpServletRequestEvent&#41; &#123;
    					defaultAttributeResolver = new ServletAttributeResolverImpl&#40;&#41;;
    				&#125; else &#123;
    					//TODO&#58; decide what to do if attribute resolver not assigned
    					throw new RuntimeException&#40;"Attribute resolver not assigned"&#41;;
    				&#125;
    				defaultAttributeResolver.setAttributeSourceChain&#40;defaultAttributeSourceChain&#41;;
    			&#125;
    			return defaultAttributeResolver.getObject&#40;objectName,context&#41;;
    		&#125; else &#123;
    			// if you don't have a attribute resolver for property and you don't want default population to occur then return null
    			// as request.getAttribute would do
    			return null;
    		&#125;
    		
    		
    	&#125;
    	
    	protected Event bindAndValidateInternal&#40;RequestContext context, DataBinder binder&#41; throws Exception &#123;
    		if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
    			logger.debug&#40;"Binding allowed matching event parameters to object '" + binder.getObjectName&#40;&#41;
    					+ "', details='" + binder.getTarget&#40;&#41; + "'"&#41;;
    		&#125;
    		// this is an addition to code in FormAction
    
    		
    		// the only possible fields to bind are those in object itself so use reflection to determine fields
    		// in formbean associated with binder.
    		
    		MutablePropertyValues propertyValues = new MutablePropertyValues&#40;&#41;;
    		PropertyDescriptor&#91;&#93; propertyDescriptors = new BeanWrapperImpl&#40;binder.getTarget&#40;&#41;&#41;.getPropertyDescriptors&#40;&#41;;
    		for &#40;int i = 0; i < propertyDescriptors.length; i++&#41; &#123;
    			PropertyDescriptor propertyDescriptor = propertyDescriptors&#91;i&#93;;
    			if &#40;propertyDescriptor.getWriteMethod&#40;&#41; != null&#41; &#123;
    				Object resolvedObject = null;
    				//TODO&#58; decide what precedence between
    				//      1&#41; object has value
    				//      2&#41; object has AttributeResolver
    				//      3&#41; property has value or AttributeResolver
    				// if value is defined get property values from it if possible
    				if &#40;value != null&#41; &#123;
    					BeanWrapper wrappedValue = new BeanWrapperImpl&#40;value&#41;;
    					resolvedObject = wrappedValue.getPropertyValue&#40;propertyDescriptor.getName&#40;&#41;&#41;;
    	
    				&#125;
    				if &#40;objectAttributeResolver != null&#41; &#123;
    					if &#40;! objectAttributeResolver.isAttributeSourceChainDefined&#40;&#41;&#41; &#123;
    						objectAttributeResolver.setAttributeSourceChain&#40;defaultAttributeSourceChain&#41;;
    					&#125;
    					resolvedObject = objectAttributeResolver.getObject&#40;getFormObjectName&#40;&#41;,context&#41;;
    				&#125;
    				if &#40;resolvedObject == null&#41; &#123;
    					resolvedObject = getObject&#40;propertyDescriptor.getName&#40;&#41;,context&#41;;
    				&#125;
    				PropertyValue pv = new PropertyValue&#40;propertyDescriptor.getName&#40;&#41;,resolvedObject&#41;;
    				propertyValues.addPropertyValue&#40;pv&#41;;
    			&#125;
    		&#125;
    		//TODO&#58; binder needs to smart enough to address nested properties
    		binder.bind&#40;propertyValues&#41;;
    		// this is end of addition of FormAction code
    		
    		// this is what used to be in FormAction code that is replaced by above.
    		//binder.bind&#40;new MutablePropertyValues&#40;context.getLastEvent&#40;&#41;.getParameters&#40;&#41;&#41;&#41;;
    		onBind&#40;context, binder.getTarget&#40;&#41;, binder.getErrors&#40;&#41;&#41;;
    		if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
    			logger.debug&#40;"Binding completed for object '" + binder.getObjectName&#40;&#41; + "', details='"
    					+ binder.getTarget&#40;&#41; + "'"&#41;;
    		&#125;
    		if &#40;getValidator&#40;&#41; != null && isValidateOnBinding&#40;&#41; && validationEnabled&#40;context&#41;&#41; &#123;
    			validate&#40;context, binder.getTarget&#40;&#41;, binder.getErrors&#40;&#41;&#41;;
    		&#125;
    		return onBindAndValidate&#40;context, binder.getTarget&#40;&#41;, binder.getErrors&#40;&#41;&#41;;
    	&#125;
    
    &#125;
    AttributeResolver
    Code:
    package org.springframework.web.flow.action;
    
    import org.springframework.web.flow.RequestContext;
    
    /**
     * Interface to be implemented by beans which act as Attribute Resolvers.
     *
     * @author Keith Garry Boyce
     */
    public interface AttributeResolver &#123;
    
    	/**
    	 * Retreives object named with objectName using context and resolving using attribute source chain
    	 * 
    	 * @param objectName name of object to find
    	 * @param context context in which to find object
    	 * @return found object or null if object is not found
    	 */
    	Object getObject&#40;String objectName, RequestContext context&#41;;
    
    
    	/**
    	 * Set attribute source chain
    	 * 
    	 * @param attributeSourceChain
    	 */
    	void setAttributeSourceChain&#40;String attributeSourceChain&#41;;
    
    
    	/**
    	 * Whether this Attribute Source Chain is defined
    	 * @return whether this Attribute Source Chain is defined
    	 */
    	boolean isAttributeSourceChainDefined&#40;&#41;;
    &#125;
    AbstractAttributeResolverImpl
    Code:
    package org.springframework.web.flow.action;
    
    
    import org.springframework.util.StringUtils;
    import org.springframework.web.flow.RequestContext;
    
    /**
     * Base attribute resolver implementation that for each scope specified in attributeSourceChain, 
     * search in that scope for "from" name or "to" name if "from" not specified.
     * 
     * @author Keith Garry Boyce
     */
    public abstract class AbstractAttributeResolverImpl implements AttributeResolver &#123;
    	private String attributeSourceChain = "parameter,request,flow,session,context";
    	private String fromObject;
    	protected static final Object SESSION = "session";
    	protected static final Object REQUEST = "request";
    	protected static final String FLOW = "flow";
    	protected static final Object PARAMETER = "parameter";
    	protected static final Object CONTEXT = "context";
    
    	
    	/**
    	 * @param fromObject The fromObject to set.
    	 */
    	public void setFromObject&#40;String fromObject&#41; &#123;
    		this.fromObject = fromObject;
    	&#125;
    
    	/* &#40;non-Javadoc&#41;
    	 * @see org.springframework.web.flow.action.AttributeResolver#setAttributeSourceChain&#40;java.lang.String&#41;
    	 */
    	public void setAttributeSourceChain&#40;String attributeSourceChain&#41; &#123;
    		this.attributeSourceChain = attributeSourceChain;
    	&#125;
    	
    	/* &#40;non-Javadoc&#41;
    	 * @see org.springframework.web.flow.action.AttributeResolver#isAttributeSourceChainDefined&#40;&#41;
    	 */
    	public boolean isAttributeSourceChainDefined&#40;&#41; &#123;
    		return attributeSourceChain != null;
    	&#125;
    
    	/* &#40;non-Javadoc&#41;
    	 * @see org.springframework.web.flow.action.AttributeResolver#getObject&#40;java.lang.String, org.springframework.web.flow.RequestContext&#41;
    	 */
    	public Object getObject&#40;String toObject, RequestContext context&#41; &#123;
    		String objectToFind = &#40;fromObject == null ? toObject &#58; fromObject&#41;;
    		//TODO&#58; handle nested properties in objectToFind
    		String&#91;&#93; attributeSourceChainSet = StringUtils.commaDelimitedListToStringArray&#40;attributeSourceChain&#41;;
    
    		for &#40;int i = 0; i < attributeSourceChainSet.length; i++&#41; &#123;
    			String attributeSource = attributeSourceChainSet&#91;i&#93;;
    			if &#40;isSupported&#40;attributeSource&#41;&#41; &#123;
    				Object obj = findObject&#40;context,objectToFind,attributeSource&#41;;
    				//if attribute resolver cannot resolve object continue to check in other scopes
    				if &#40;obj != null&#41; &#123;
    					return obj;
    				&#125;
    			&#125;
    		&#125;
    		// if object cannot be found in any scope then return null just like request.getAttribute would do.
    		return null;
    		
    	&#125;
    	
    	/**
    	 * Subclasses must override the following method to provide the implementation by which
    	 * the object is looked up in the context
    	 * 
    	 * <p>
    	 * This implementation does nothing
    	 * @param context The context in which to look
    	 * @param objectToFind The object to find
    	 * @param attributeSource The attribute source
    	 * @return
    	 */
    	protected abstract Object findObject&#40;RequestContext context, String objectToFind,String attributeSource&#41;;
    	
    	/**
    	 * Whether implementation supports the specified attribute source
    	 * @param attributeSource
    	 * @return whether implementation supports the specified attribute source
    	 */
    	protected abstract boolean isSupported&#40;String attributeSource&#41;;
    
    &#125;
    ServletAttributeResolverImpl
    Code:
    package org.springframework.web.flow.action;
    
    import org.springframework.web.context.support.WebApplicationContextUtils;
    import org.springframework.web.flow.RequestContext;
    import org.springframework.web.flow.execution.servlet.HttpServletRequestEvent;
    
    
    /**
     * Servlet implementation of AbstractAttributeResolverImpl
     * 
     * @author Keith Garry Boyce
     */
    public class ServletAttributeResolverImpl extends AbstractAttributeResolverImpl &#123;
    
    	/* &#40;non-Javadoc&#41;
    	 * @see org.springframework.web.flow.action.AbstractAttributeResolverImpl#findObject&#40;org.springframework.web.flow.RequestContext, java.lang.String, java.lang.String&#41;
    	 */
    	protected Object findObject&#40;RequestContext context,String objectToFind,String attributeSource&#41; &#123;
    		HttpServletRequestEvent event = &#40;HttpServletRequestEvent&#41; context.getOriginatingEvent&#40;&#41;;
    		//TODO&#58; should use typesafe enum from ScopeType
    		Object obj;
    		if &#40;attributeSource.equals&#40;SESSION&#41;&#41; &#123;
    	        obj = event.getRequest&#40;&#41;.getSession&#40;&#41;.getAttribute&#40;objectToFind&#41;;
    		&#125; else if &#40;attributeSource.equals&#40;REQUEST&#41;&#41; &#123;
    			obj = context.getRequestScope&#40;&#41;.getAttribute&#40;objectToFind&#41;;
    		&#125; else if &#40;attributeSource.equals&#40;FLOW&#41;&#41; &#123;
    			obj = context.getFlowScope&#40;&#41;.getAttribute&#40;objectToFind&#41;;
    		&#125; else if &#40;attributeSource.equals&#40;PARAMETER&#41;&#41; &#123;
    			obj = event.getParameter&#40;objectToFind&#41;;
    		&#125; else if &#40;attributeSource.equals&#40;CONTEXT&#41;&#41; &#123;
    			obj = WebApplicationContextUtils.getWebApplicationContext&#40;event.getRequest&#40;&#41;.getSession&#40;&#41;.getServletContext&#40;&#41;&#41;.getBean&#40;objectToFind&#41;;
    		&#125; else &#123;
    			throw new RuntimeException&#40;"This should not happen because this method should only be called with supported attributeSource"&#41;;
    		&#125;
    		return obj;
    	&#125;
    
    	/* &#40;non-Javadoc&#41;
    	 * @see org.springframework.web.flow.action.AbstractAttributeResolverImpl#isSupported&#40;java.lang.String&#41;
    	 */
    	protected boolean isSupported&#40;String attributeSource&#41; &#123;
    		if &#40;   attributeSource.equals&#40;SESSION&#41;
    			|| attributeSource.equals&#40;REQUEST&#41;
    			|| attributeSource.equals&#40;FLOW&#41;
    			|| attributeSource.equals&#40;PARAMETER&#41;
    			|| attributeSource.equals&#40;CONTEXT&#41;&#41; &#123;
    	        return true;
    		&#125; else &#123;
    			return false;
    		&#125;
    	&#125;
    
    
    
    &#125;
    PortletAttributeResolverImpl
    Code:
    package org.springframework.web.flow.action;
    
    import org.springframework.web.flow.RequestContext;
    import org.springframework.web.flow.execution.portlet.PortletRequestEvent;
    import org.springframework.web.portlet.context.support.PortletWebApplicationContextUtils;
    
    /**
     * Portlet implementation of AbstractAttributeResolverImpl
     * 
     * @author Keith Garry Boyce
     */
    public class PortletAttributeResolverImpl extends AbstractAttributeResolverImpl &#123;
    
    	/* &#40;non-Javadoc&#41;
    	 * @see org.springframework.web.flow.action.AbstractAttributeResolverImpl#findObject&#40;org.springframework.web.flow.RequestContext, java.lang.String, java.lang.String&#41;
    	 */
    	protected Object findObject&#40;RequestContext context,String objectToFind,String attributeSource&#41; &#123;
    		PortletRequestEvent event = &#40;PortletRequestEvent&#41; context.getOriginatingEvent&#40;&#41;;
    		//TODO&#58; should use typesafe enum from ScopeType
    		Object obj;
    		if &#40;attributeSource.equals&#40;SESSION&#41;&#41; &#123;
    	        obj = event.getRequest&#40;&#41;.getPortletSession&#40;&#41;.getAttribute&#40;objectToFind&#41;;
    		&#125; else if &#40;attributeSource.equals&#40;REQUEST&#41;&#41; &#123;
    			obj = context.getRequestScope&#40;&#41;.getAttribute&#40;objectToFind&#41;;
    		&#125; else if &#40;attributeSource.equals&#40;FLOW&#41;&#41; &#123;
    			obj = context.getFlowScope&#40;&#41;.getAttribute&#40;objectToFind&#41;;
    		&#125; else if &#40;attributeSource.equals&#40;PARAMETER&#41;&#41; &#123;
    			obj = event.getParameter&#40;objectToFind&#41;;
    		&#125; else if &#40;attributeSource.equals&#40;CONTEXT&#41;&#41; &#123;
    			obj = PortletWebApplicationContextUtils.getWebApplicationContext&#40;event.getRequest&#40;&#41;.getPortletSession&#40;&#41;.getPortletContext&#40;&#41;&#41;.getBean&#40;objectToFind&#41;;
    		&#125; else &#123;
    			throw new RuntimeException&#40;"TODO&#58; What should happen if scope is invalid"&#41;;
    		&#125;
    		return obj;
    
    	&#125;
    	/* &#40;non-Javadoc&#41;
    	 * @see org.springframework.web.flow.action.AbstractAttributeResolverImpl#isSupported&#40;java.lang.String&#41;
    	 */
    	protected boolean isSupported&#40;String attributeSource&#41; &#123;
    		if &#40;   attributeSource.equals&#40;SESSION&#41;
    			|| attributeSource.equals&#40;REQUEST&#41;
    			|| attributeSource.equals&#40;FLOW&#41;
    			|| attributeSource.equals&#40;PARAMETER&#41;
    			|| attributeSource.equals&#40;CONTEXT&#41;&#41; &#123;
    	        return true;
    		&#125; else &#123;
    			return false;
    		&#125;
    	&#125;
    
    
    &#125;
    Here is an very simple example how I have used it
    (
    look above for more complete and complicated configuration example.
    This example does not even show use of attribute resolvers
    so don't judge code by this example.
    This is only showing one simple thing the code can do!!
    ):


    Code:
    	<bean id="profile"
    		  class="com.bcbsma.contactus.business.domain.Profile">
    		<property name="type">
    			<value>80</value>
    		</property>
    		<property name="specialty">
    			<value>99</value>
    		</property>
    	</bean>
    	
    	<bean id="profileInjector" class="org.springframework.web.flow.action.ContextInjector"> 
    		<property name="value"> 
    			 <ref local="profile"/> 
    		</property> 
    		<property name="formObjectName"><value>profile</value></property>
    		<property name="formObjectClass"><value>com.bcbsma.contactus.business.domain.Profile</value></property>
    		<property name="formObjectScopeAsString"><value>request</value></property>
    	</bean> 
    	
    	<bean id="contactUs.InjectionAction"
    		  class="org.springframework.web.flow.action.InjectionAction" > 
    	   <property name="injectors"> 
    			<list> 
    				<ref bean="profileInjector"/> 
    			 </list> 
    	   </property> 
        </bean>

    and webflow:
    Code:
    <webflow id="contactUs" start-state="bindAndValidate">
    
    	<action-state id="bindAndValidate">
    		<action bean="contactUs.InjectionAction"/>
    		<transition on="success" to="pre.select.topic.action"/>
    		<transition on="error" to="error"/>
    	</action-state>
    ...

    In action code refer to object like:
    Code:
    &#40;Profile&#41; context.getRequestScope&#40;&#41;.getAttribute&#40;"profile"&#41;;
    Opened JIRA on issue:
    http://opensource.atlassian.com/proj...browse/SPR-945

    Comment


    • #3
      What I can't get working

      What I can't get working
      Code:
      event.getRequest&#40;&#41;.getPortletSession&#40;&#41;.getPortletContext&#40;&#41;&#41;
      appears to be broken. I get a NullPointerException.

      This turned out to be a bug in jetspeed2

      http://issues.apache.org/jira/browse/JS2-254

      Comment


      • #4
        Please some feedback...

        Please some feedback... Recommendations, Corrections, Criticisms. Anything...

        Comment


        • #6
          Communication...

          I'll meet on irc #spring chanel on irc.freenode.net or skype chat to garpinc

          Comment

          Working...
          X