Announcement Announcement Module
Collapse

Spring Modules forum decommissioned in favor of Spring Extensions

As the Spring Modules project has been replaced by the Spring Extensions (http://www.springsource.org/extensions) project, this forum has been decommissioned in favour of Spring Extensions one at:
http://forum.springsource.org/forumdisplay.php?f=44

Please see the Spring Extensions home page for a complete list of current projects in Java, .NET and ActionScript. You can also propose one if you want.

Cheers,
Costin Leau
SpringSource - http://www.SpringSource.com- Spring Training, Consulting, and Support - "From the Source"
http://twitter.com/costinl
See more
See less
FormDataAccessor SWF implementation Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • FormDataAccessor SWF implementation

    I wrote an implementation of FormDataAccessor for accessing Spring Web Flow. I posted it here if someone needs it.

    Code:
    package ro.theredpoint.util.xt;
    
    import java.util.Map;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.log4j.Logger;
    import org.springframework.validation.Errors;
    import org.springframework.webflow.action.FormObjectAccessor;
    import org.springframework.webflow.context.ExternalContext;
    import org.springframework.webflow.context.ExternalContextHolder;
    import org.springframework.webflow.context.servlet.ServletExternalContext;
    import org.springframework.webflow.execution.FlowExecution;
    import org.springframework.webflow.execution.repository.FlowExecutionKey;
    import org.springframework.webflow.execution.repository.FlowExecutionRepository;
    import org.springframework.webflow.executor.FlowExecutorImpl;
    import org.springframework.webflow.executor.mvc.FlowController;
    import org.springframework.webflow.executor.support.FlowExecutorArgumentHandler;
    import org.springmodules.xt.ajax.FormDataAccessor;
    
    /**
     * 
     * @author Elvis Ciocoiu
     *
     */
    public class SWFFormDataAccessor implements FormDataAccessor {
    	private static final Logger LOG = Logger.getLogger(SWFFormDataAccessor.class);
    	
    	public Object getCommandObject(HttpServletRequest request,
    			HttpServletResponse response, Object handler, Map model) {
    		
    		Object command = extract(request, response, (FlowController) handler, new SWFAccessorCallback() {
    
    			public Object extract(FlowExecution flowExecution) {
    				return flowExecution.getActiveSession().getScope().get(FormObjectAccessor.getCurrentFormObjectName());
    			}
    			
    		});
    		
    		return command;
    	}
    
    	public Errors getValidationErrors(HttpServletRequest request,
    			HttpServletResponse response, Object handler, Map model) {
    		Errors errors = (Errors) extract(request, response, (FlowController) handler, new SWFAccessorCallback() {
    
    			public Object extract(FlowExecution flowExecution) {
    				return (Errors) flowExecution.getActiveSession().getFlashMap().get(
    						FormObjectAccessor.getCurrentFormErrorsName(), Errors.class);
    			}
    			
    		});
    		
    		return errors;
    	}
    
    	private Object extract(HttpServletRequest request,
    			HttpServletResponse response, FlowController flowControllerflowControllertroller, SWFAccessorCallback callbak) {
    		FlowExecutionRepository repository = ((FlowExecutorImpl)flowControllerflowControllertroller.getFlowExecutor()).getExecutionRepository();
    		FlowExecutorArgumentHandler argumentHandler  = flowControllerflowControllertroller.getArgumentHandler();
    		
    		ExternalContext externalContext = new ServletExternalContext(request.getSession().getServletContext(), request, response);		
    		
    		if (argumentHandler.isFlowExecutionKeyPresent(externalContext)) {
    			try {
    				ExternalContextHolder.setExternalContext(externalContext);
    				FlowExecutionKey flowExecutionKey =
    					repository.parseFlowExecutionKey(argumentHandler.extractFlowExecutionKey(externalContext));
    				FlowExecution flowExecution = repository.getFlowExecution(flowExecutionKey);
    				
    				return callbak.extract(flowExecution);
    			}
    			catch (Exception e) {
    				e.printStackTrace();
    			}
    			finally {
    				ExternalContextHolder.setExternalContext(null);
    			}
    		}
    		
    		return null;
    	}
    
    	interface SWFAccessorCallback {
    		Object extract(FlowExecution flowExecution);
    	}
    }

  • #2
    Originally posted by fanfy View Post
    I wrote an implementation of FormDataAccessor for accessing Spring Web Flow. I posted it here if someone needs it.
    Hi,

    that's great! Thank you very much for your contribution!
    May you open a Jira issue (http://jira.springframework.org/browse/MOD) and attach there your source code?
    I'll include it in the next release.

    Thanks again,
    Cheers,

    Sergio B.

    Comment


    • #3
      I've created a jira issue for it and attached my implementation (needs cleaning allready ...). In this context I'm having a problem. I want to send the SWF's _eventId when submiting with XT. Help me with a solution. I've tried using a hidden field but my page uses also some submit buttons and SWF first checks to see if there is a parameter named _eventId and use it in all submits. I thought that the server-parameters argument is the solution but on server side I receive an array named json-params. What I need is a parameter named _eventId. What is your sugestion?

      Comment


      • #4
        Originally posted by fanfy View Post
        I want to send the SWF's _eventId when submiting with XT.
        Help me with a solution.
        I thought that the server-parameters argument is the solution but on server side I receive an array named json-params. What I need is a parameter named _eventId. What is your sugestion?
        Server parameters should be the best solution: you can access those parameters through the AjaxEvent#getParameters() method.
        May you explain in a more detailed way why it doesn't work for your use case?

        Comment


        • #5
          Well the xt submit is handled first by Spring Web Flow that searches for a request parameter named "_eventId". in the request I want to include this parameter when submiting. Probably one solution is to obtain it from AjaxEvent.getParamewters and put it myself under the name "_eventId" but this require to extend AjaxInterceptor in order to do it before passing the control to the FlowController. It seems to much for this usecase...
          Regarding the unit tests for SWFFormDataAccessor I will write them (hopefully tonight).
          Last edited by fanfy; Jan 23rd, 2008, 09:46 AM.

          Comment


          • #6
            Originally posted by fanfy View Post
            Well the xt submit is handled first by Spring Web Flow that searches for a request parameter named "_eventId". I want to include this parameter when submiting.
            Ok, now I understand.
            And what about hidden input fields?

            Comment


            • #7
              I'm not very comfortable with this either because when using a hidden field named "_eventId" when I submit using a submit button the hidden parameter will be considered by the SWF as the event and not one extracted from the submit button's name. Folowing SWF convention I named the button "_eventId_back" but because there is also a parameter (the hidden one) name "_eventId" SWF will use this one. Eventually I have to rename dynamically this hidden field .... very ugly. I was hoping for other solutions, something like xt allowing me to send server parameters as I named them and not in "json-params". Ignore my poor and verbose english.

              Comment


              • #8
                Originally posted by fanfy View Post
                I'm not very comfortable with this either because when using a hidden field named "_eventId" when I submit using a submit button the hidden parameter will be considered by the SWF as the event and not one extracted from the submit button's name. Folowing SWF convention I named the button "_eventId_back" but because there is also a parameter (the hidden one) name "_eventId" SWF will use this one.
                My suggestion could seem a bit obvious, but why don't you put an hidden field named, say, "_customEventId", giving it the actual value of your "_eventId"?
                Doing so, "_customEventId" should be ignored by SWF, and you should be able to later access it through servlet parameters and have your "_eventId" value.
                What do you think?

                Comment


                • #9
                  Thank you for your reply.
                  Just to be sure that I presented my problem clear enough. In the end the SWF searches for a request parameter named "_eventId". So I'll have to extract it server side from "json-params" somehow an put a parameter with this name in request parameters. In case of a hidden field I have to rename from "customEventId" to "_eventId" in order for SWF to properly access it. In both cases I need to do something on server side. This is ugly . The first solution for that is to make a servlet filter that wraps HttpServletRequest and respond for a parameter named "_eventId" with the value of "customEventId" (or json-params["_eventId"]). That's because the HttpServletFilter.getParameterMap() returns a readonly map. I've done that and it works but I think it's to much effort just for that. Another solution I think is to implement a custom FlowExecutorArgumentExtractor and configure the FlowController with it. This implementation will search for the event id also in other parameters (like "customEventId" or json-params["_eventId"]. I don't think that SWF team implemented the FlowExecutorArgumentExtractor hierarchy with this kind of customization in mind. Or I could use a custom FlowController implementation that adds this parameter in ServletExternalContext along with the request parameters. I think that the easiest and cleanest way to integrate xt and SWF is to let me send directly a parameter (in my case named "_eventId") to the server. What do you think?

                  Comment


                  • #10
                    Originally posted by fanfy View Post
                    I think that the easiest and cleanest way to integrate xt and SWF is to let me send directly a parameter (in my case named "_eventId") to the server. What do you think?
                    Please forgive me if I'm saying something obvious (I've never used SWF), but if you say that you cannot send an "_eventId" parameter through an hidden field, wouldn't it be the same if you sent the parameter through XT as a request parameter?
                    I'm sorry but I don't see any difference between the two. It's the way HTTP requests work: they are both request parameters.

                    Let me know.
                    Cheers,

                    Sergio B.

                    Comment


                    • #11
                      Put it simple SWF pauses it's flow when entering a view state and resume's it when the next request arrives. The next SWF state is determined by the event id send by the browser. There are to ways to send a event id to swf. One is using a request parameter named _eventId. And the other is to use a submit button named "_eventId_something". In the later case swf will extract "something" as the event id. When determining if in the request is a event id swf uses a FlowExecutorArgumentExtractor implementation that first searches for a request parameter named "_eventId". If there is no such parameter it will search for a pattern like "_eventId_something" in the request parameters. In my case if I'll use a hidden field named "_eventId" then in all requests this will take precedence. My page has both use cases: I have a button named "_eventId_back" and a select that uses xt in onchange. If I use a hidden field named "_eventId" and want to click the submit button the event extracted by swf will be the hidden field value and not the "something" from the submit button because the request parameter named "_eventId" will be searches first by swf. So I have to somehow rename it dynamically or not using it. But in every case I have to write code server side or client side .

                      Comment


                      • #12
                        Originally posted by fanfy View Post
                        Put it simple SWF pauses it's flow when entering a view state and resume's it when the next request arrives. The next SWF state is determined by the event id send by the browser. There are to ways to send a event id to swf. One is using a request parameter named _eventId. And the other is to use a submit button named "_eventId_something". In the later case swf will extract "something" as the event id. When determining if in the request is a event id swf uses a FlowExecutorArgumentExtractor implementation that first searches for a request parameter named "_eventId". If there is no such parameter it will search for a pattern like "_eventId_something" in the request parameters. In my case if I'll use a hidden field named "_eventId" then in all requests this will take precedence. My page has both use cases: I have a button named "_eventId_back" and a select that uses xt in onchange. If I use a hidden field named "_eventId" and want to click the submit button the event extracted by swf will be the hidden field value and not the "something" from the submit button because the request parameter named "_eventId" will be searches first by swf. So I have to somehow rename it dynamically or not using it. But in every case I have to write code server side or client side .
                        Now I understand everything, but I think there's no other solution than the ones previously mentioned: sending the parameter through XT would not solve your problem (AFAIU).

                        However, let us know how you'll end up solving it, it may be useful to other people

                        Cheers!

                        Sergio B.

                        Comment


                        • #13
                          For the moment I implemented a filter that extracts the swf event id from xt server parameters.

                          Code:
                          package ro.theredpoint.evaluation.xt;
                          
                          import java.io.IOException;
                          import java.util.HashMap;
                          import java.util.Iterator;
                          
                          import javax.servlet.Filter;
                          import javax.servlet.FilterChain;
                          import javax.servlet.FilterConfig;
                          import javax.servlet.ServletException;
                          import javax.servlet.ServletRequest;
                          import javax.servlet.ServletResponse;
                          import javax.servlet.http.HttpServletRequest;
                          import javax.servlet.http.HttpServletRequestWrapper;
                          
                          import net.sf.json.JSONObject;
                          
                          /**
                           * Filter that extracts th SWF event id from XT json-params and put it
                           * in request. Because the request's parameter map is readonly we wrap it
                           * in a HttpServletRequestWrapper. The client is responsible to send the
                           * swf event id in xt server parameters. 
                           * 
                           * @author Elvis Ciocoiu
                           *
                           */
                          public class SWFEventIdFromServerParamsFilter implements Filter {
                          	private static final String XT_JSON_PARAMS_PARAMETER = "json-params";
                          	private static final String SWF_EVENT_ID_PARAMETER = "_eventId";
                          	
                          	/**
                          	 * We extract from the request the parameter json-params and from it the _eventId
                          	 * If there is no json-params or _eventId we return null.
                          	 */
                          	private String extractSWFEventId(HttpServletRequest request) {
                          		String paramsString = request.getParameter(XT_JSON_PARAMS_PARAMETER);
                          	    if (paramsString != null) {
                          	        JSONObject json = JSONObject.fromString(paramsString);
                          	        Iterator<?> keys = json.keys();
                          	        while (keys.hasNext()) {
                          	            String key = keys.next().toString();
                          	            if(key.equals(SWF_EVENT_ID_PARAMETER))
                          	            	return json.opt(key).toString();
                          	        }
                          	    }
                          	    return null;
                          	}
                          
                          	public void destroy() {
                          		// nothing here
                          	}
                          
                          	public void doFilter(ServletRequest request, ServletResponse response,
                          			FilterChain chain) throws IOException, ServletException {
                          		HttpServletRequest httpRequest = (HttpServletRequest)request;
                          		final String eventId = extractSWFEventId(httpRequest);
                          		if(eventId != null) {
                          			HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(
                          					httpRequest) {
                          				private HashMap<String, String[]> parameterValues;
                          				
                          				public String getParameter(String name) {
                          					if (SWF_EVENT_ID_PARAMETER.equals(name))
                          						return eventId;
                          					else
                          						return super.getParameter(name);
                          				}
                          				
                          				@Override
                          				public String[] getParameterValues(String name) {
                          					if (SWF_EVENT_ID_PARAMETER.equals(name)) {
                          						String[] result = null;
                          						if(parameterValues == null)
                          							parameterValues = new HashMap<String, String[]>();
                          						else
                          							result = (String[])parameterValues.get(name);
                          						
                          						if(result == null) {
                          							result = new String[]{eventId};
                          							parameterValues.put(name, result);
                          						}
                          						return result;
                          					} else						
                          						return super.getParameterValues(name);
                          				}
                          			};
                          			chain.doFilter(wrapper, response);  	
                          		} else
                          			chain.doFilter(request, response);
                          	}
                          
                          	public void init(FilterConfig filterConfig) throws ServletException {
                          		// nothing here
                          	}
                          }

                          Comment

                          Working...
                          X