Announcement Announcement Module
Collapse
No announcement yet.
SWF Basic question 1 Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • SWF Basic question 1

    Hi ,
    Sometime l found it is difficult to do programming . May be because of lack of docs and examples ? or lack some stupid people like me to ask stupid question , hihihi ..... But now l wish to ask a series basic questions about SWF , hope that clear all my "basic HOW TO" . Hope the authors don't mind ....

    Q1.

    Is this the right implemetation for SWF ? doing messages and errors that different from spring MVC.

    Code:
    public class AbstractLibraryAction extends FormAction implements ApplicationContextAware {
    
    	private ApplicationContext applicationContext;
    
    	private MessageSourceAccessor messageSourceAccessor;
    
       private LibraryFacade library;
    
    	private static final String DATE_PATTERN = "yyyy-MM-dd";
    
    	public final void setLibrary(LibraryFacade library){
    		this.library = library;
    	}
    
    	protected final LibraryFacade getLibrary(){
    		return this.library;
    	}
    
    	public AbstractLibraryAction() {
    	}
    
    	public final void setApplicationContext(ApplicationContext context) throws BeansException {
    		if (context == null && !isContextRequired()) {
    			// reset internal context state
    			this.applicationContext = null;
    			this.messageSourceAccessor = null;
    		}
    		if (this.applicationContext == null) {
    			// initialize with passed-in context
    			if (!requiredContextClass().isInstance(context)) {
    				throw new ApplicationContextException(
    						"Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
    			}
    			this.applicationContext = context;
    			this.messageSourceAccessor = new MessageSourceAccessor(context);
    			initApplicationContext();
    		}
    		else {
    			// ignore reinitialization if same context passed in
    			if (this.applicationContext != context) {
    				throw new ApplicationContextException(
    						"Cannot reinitialize with different application context: current one is [" +
    						this.applicationContext + "], passed-in one is [" + context + "]");
    			}
    		}
    	}
    
    	protected boolean isContextRequired() {
    		return true;
    	}
    
    	protected Class requiredContextClass() {
    		return ApplicationContext.class;
    	}
    
    	protected void initApplicationContext() throws BeansException {
    	}
    
    
    	public final ApplicationContext getApplicationContext() throws IllegalStateException {
    		if (this.applicationContext == null && isContextRequired()) {
    			throw new IllegalStateException(
    					"ApplicationObjectSupport instance [" + this + "] does not run in an ApplicationContext");
    		}
    		return applicationContext;
    	}
    
    	protected final MessageSourceAccessor getMessageSourceAccessor() throws IllegalStateException {
    		if (this.messageSourceAccessor == null && isContextRequired()) {
    			throw new IllegalStateException(
    					"ApplicationObjectSupport instance [" + this + "] does not run in an ApplicationContext");
    		}
    		return this.messageSourceAccessor;
    	}
    
    
    	public final void afterPropertiesSet() {
    		if (this.library == null) throw new BeanCreationException("Library Facade is required");
    		initAction();
    	}
    
    	protected void initBinder(RequestContext context, DataBinder binder){
    
    		SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_PATTERN);
    		dateFormat.setLenient(false);
    		binder.registerCustomEditor(Date.class, null, new CustomDateEditor(dateFormat, false));
    
    		binder.registerCustomEditor(String.class, null, new StringTrimmerEditor("\r\n\f", false));
    	}
    	
    }
    Code:
    public class PublisherCRUDAction extends AbstractLibraryAction {
    
    	private static final String PUBLISHER_FORM_OBJECT_NAME = "publisher";
    
    	public PublisherCRUDAction() {
    
    		setFormObjectName(PUBLISHER_FORM_OBJECT_NAME);
    		setFormObjectClass(Publisher.class);
    		setValidator(new PublisherValidator());
    	}
    
     	public Event Save(RequestContext context) throws Exception {
    
                    // store publisher
                    Publisher publisher = (Publisher)context.getRequestScope().get(PUBLISHER_FORM_OBJECT_NAME);
    
    		HttpServletRequest request = ((HttpServletRequestEvent)context.getOriginatingEvent()).getRequest();
    		
                    Locale locale = RequestContextUtils.getLocale(request);
    		
    		try{
    		   	getLibrary().storePublisher(publisher);
    		   	
    		        request.getSession().setAttribute("message_publisher_added",
                                    getMessageSourceAccessor().getMessage("publisher.added",
                                    new Object[] {publisher.getPublisherName()}, locale));
    		}
    		catch(org.springframework.dao.DataIntegrityViolationException eive){
    
                            BindException errors = createBinder(context, publisher).getErrors();
                            errors.rejectValue("publisherName","publisher.name.already.exists",
                                                    "Publisher name already exists ...");
    		        exposeFormObjectAndErrors(context, publisher ,errors);
    
    			return error();
    		}
    
    		return success();
    
             }
    		
    		
    	public Event Search(RequestContext context) throws Exception {
    
                    // search publisher
    
                    HttpServletRequest request = ((HttpServletRequestEvent)context.getOriginatingEvent()).getRequest();
    
    		String publisherName = request.getParameter("publisherName");
    
    		Locale locale = RequestContextUtils.getLocale(request);
    		
    		if(publisherName != null){
    
    		   	ArrayList publishers = (ArrayList)getLibrary().findPublishersByName(publisherName);
    
                            if &#40;publishers.size&#40;&#41; < 1&#41; &#123;
                               // no subject found
                               context.getRequestScope&#40;&#41;.setAttribute&#40;"message_no_publisher_found",
                                    getMessageSourceAccessor&#40;&#41;.getMessage&#40;"no.publisher.found", locale&#41;&#41;;
    
                               return success&#40;&#41;;
    
                            &#125;
    
                            // multiple subjects found
    		        context.getRequestScope&#40;&#41;.setAttribute&#40;"model_name_publishers", publishers&#41;;
    
    		        return success&#40;&#41;;
    		&#125;
    		
    		return success&#40;&#41;;
    
    
             &#125;
    
    &#125;
    Q2.

    What is the different between the usage of RequestScope and FlowScope ?

    sometimes l saw example using RequestScope and sometime FlowScope.

    Code:
    BirthDate birthDate = &#40;BirthDate&#41;context.getRequestScope&#40;&#41;.get&#40;BIRTHDATE_FORM_OBJECT_NAME&#41;;
    Code:
    PhoneBookQuery query = &#40;PhoneBookQuery&#41;context.getFlowScope&#40;&#41;.getRequiredAttribute&#40;"query",PhoneBookQuery.class&#41;;

  • #2
    Well, webflow has a ton of docs that explain this stuff...are you reading them? We also have like 7 sample applications. :-)

    Here are some suggestions:

    - Stop downcasting to HttpServletRequestEvent to get the Request. It is not needed, and couples your action code with the servlet API unnecessarily. If you want to access HTTP equest parameters, just use the Event.getParameter(parameterName) method.

    - Don't capitalize method names. This IS Java remember.

    - Consider doing message resolution from your views instead of the controllers.

    - I guess we should consider having AbstractAction be ApplicationContextAware for easy access to a Spring MessageSource, ResourceLoader, and BeanFactory - among other services. You certainly shouldn't be duplicating all the context configuration code accross your actions. I'm not sure if this is general enough though to warrant that.

    - I would not recommend putting those success messages into the session like that. Use request scope, or do the resolution from the views. In general, keep your actions independent of the servlet API where possible.

    - For all that Errors stuff you're doing, use the FormObjectAccessor class or super class helper methods. It'll make your life much easier. You really shouldn't be creating a BindException directly.

    - Don't get/mess with the Locale manually in action code. The message source should always use the correct Locale based on a LocaleContext set for the request.

    requestScope = objects there exist for the duration of the request into the web flow system
    flowScope = objects there exist for the duration of the entire flow session

    Comment


    • #3
      Hi kdonald,

      Thanks for your lengthy reply ,

      l did read all the docs , the 7 sample applications did not handle messages +errors + locale like my example ,

      - Stop downcasting to HttpServletRequestEvent to get the Request. It is not needed, and couples your action code with the servlet API unnecessarily. If you want to access HTTP equest parameters, just use the Event.getParameter(parameterName) method.
      l used HttpServletRequestEvent to get the Request because it can avoid the problem l stated else where , command not clear problem
      http://forum.springframework.org/viewtopic.php?t=5341

      l have further comment about Event.getParameter(parameterName).
      l have been following SWF started from Erwin's implemetation b4 it merged into spring.Before 2 months ago , Action's methods were return Strings instead of Events, that was simple enough , then l stop sometime to learn Webwork2. When l back to SWF these day,l found it change a lot , no more request , response , all wrap it into RequestContext , if u wanna to get a request , this is not a trivial thing , l searched the SWF codes hard to find the answer , but have to ask Erwin finally .....hihi.
      You can see from SWF doc FAQ , people alway search for request , response and ApplicationContextAware , why ? may be it is hard to turn our (beginner , spring is my first MVC) head to those abstract things ...hihi
      OK , back to Event.getParameter(parameterName) , what my feeling is , l sometime don't know where to get the tools (request,response,parameters,...etc) , this is different from Webwork ( l did the comparison as a beginner , and l learn Webwork recently) , they put all the tools inside ActionContext , u know where to get it , almost just one place only , it preserved simplicity to users , l think this is important , simplicity is KING.

      - Don't capitalize method names. This IS Java remember.
      no problem.
      - Consider doing message resolution from your views instead of the controllers.
      In spring MVC , command will be clear after each successful action , so l have to do it inside the controller to display publisherName , theses codes are learn from mraible , Sping Live . Thanks to matt ,

      Code:
      new Object&#91;&#93; &#123;publisher.getPublisherName&#40;&#41;&#125;, locale&#41;&#41;;
      I guess we should consider having AbstractAction be ApplicationContextAware for easy access to a Spring MessageSource, ResourceLoader, and BeanFactory - among other services. You certainly shouldn't be duplicating all the context configuration code accross your actions. I'm not sure if this is general enough though to warrant that.
      l asked Erwin in private b4, Erwin replied that may be spring not intend to support these service , that's why l have to implement ApplicationContextAware that extends FormAction in my AbstractLibraryAction. If l do it in AbstractAction, it will lose a lot service that in FormAction. In simpleFormController , all these are support.

      to be continue ....

      Comment


      • #4
        I see no reason why you need to be accessing the HttpServletRequest for the issue you're having -- just clear the command object out of flow scope. I write a lot of actions, I find I rarely need to access the servlet API directly.

        Web flow is not Spring MVC, nor is it web work. It's a much more powerful controller that can run in any web environment. Don't use it where its not appropriate. It is appropriate for controlled navigations that guide the user through a multi-step process.

        In any case, Web flow's architecture is simple and highly consistent: at its core is a finite state machine. Events occur that drive state transitions. When a new state is entered, behavior happens. An action invoked when an action state is entered has full access to the event that triggered its execution, including any input event parameters.

        If the event originated from a HTTP environment, the parameters are HTTP request parameters. If the event orginated from a Portlet environment, well, they're portlet parameters. In any case, you access the event class consistently. It's that simple.

        Request scope gives you a store for attributes that span a request into the web flow system. Flow scope gives you a store for attributes that span the life of the flow, with automatic cleanup when the flow ends.

        The choice about the use of Event instead of String was one of consitency. It also gives you added flexbility, with very little cost there in terms of complexity. Erwin and I stand by the decision there.

        It's very important to understand the core model of how Spring web flow works. Again, this is not Spring MVC or web work. You need to understand the model in play here--its very simple, very elegant, very powerful, but different than what you're used to.

        Comment


        • #5
          Use request scope, or do the resolution from the views. In general, keep your actions independent of the servlet API where possible.
          Oh , sorry , that was my testing code.
          For all that Errors stuff you're doing, use the FormObjectAccessor class or super class helper methods. It'll make your life much easier. You really shouldn't be creating a BindException directly.
          l did not realise that the existance of this class , at lease not in example and docs , may be l miss .l will look at at ...
          Don't get/mess with the Locale manually in action code. The message source should always use the correct Locale based on a LocaleContext set for the request.
          l did this because l have this thread in my mind
          http://forum.springframework.org/vie...sourceaccessor ,
          getMessageSourceAccessor() is not request locale-aware

          Thanks kdonald.

          moon

          Comment


          • #6
            Well, that thread is old as dirt. :-)

            You're using Spring 1.2, right? It has the new locale context feature.

            Comment


            • #7
              Haha ...thanks for all your reply , l am getting satified.

              the codes wrote two months ago , l guess , at that time , it was just RC1 or sandbox code , haha.

              Will spring term add support for ApplicationContextAware in AbstractAction ? it is ugly to implement in AbstractLibraryAction , hihi...

              These indeed clear all my doubt now , l like to discuss problem vigorously(hope u don't mind) , this will eventually show all the unclear problems , make thing more clear than b4 , so in my questions , u will find my point of views , right or wrong . But one thing for sure , l did the homework b4 asking question . haha....

              Comment


              • #8
                After reading FormObjectAccessor , still don't understand what this mean ,
                For all that Errors stuff you're doing, use the FormObjectAccessor class
                Where can l get a errors (BindException) object , do the following
                Code:
                errors.rejectValue&#40;"publisherName","publisher.name.already.exists",
                                                                "Publisher name already exists ..."&#41;;
                l come out with
                Code:
                FormObjectAccessor formObjectAccessor = new FormObjectAccessor&#40;context&#41;;
                BindException errors = &#40;BindException&#41;formObjectAccessor.getFormErrors     &#40;PUBLISHER_FORM_OBJECT_NAME,ScopeType.REQUEST&#41;;
                errors.rejectValue&#40;"publisherName","publisher.name.already.exists",
                                                                "Publisher name already exists ..."&#41;;
                exposeFormObjectAndErrors&#40;context, publisher ,errors&#41;;
                seem not so right. hihi...

                don't understand this 2,
                or super class helper methods
                moon

                Comment


                • #9
                  Just do this:
                  Code:
                  Errors errors = new FormObjectAccessor&#40;context&#41;.getFormErrors&#40;&#41;;
                  errors.rejectValue&#40;...&#41;;
                  Since bindAndValidate already put a Errors instance in request scope for you and you call save after bindAndValidate, this should work fine. I see no need to expose another Errors instance.

                  Comment


                  • #10
                    Ok , done! this is the better version , hihihi....
                    Code:
                    public class PublisherCRUDAction extends AbstractLibraryAction &#123;
                    
                    	private static final String PUBLISHER_FORM_OBJECT_NAME = "publisher";
                    
                    	public PublisherCRUDAction&#40;&#41; &#123;
                    
                    		setFormObjectName&#40;PUBLISHER_FORM_OBJECT_NAME&#41;;
                    		setFormObjectClass&#40;Publisher.class&#41;;
                    		setValidator&#40;new PublisherValidator&#40;&#41;&#41;;
                    	&#125;
                    
                     	public Event Save&#40;RequestContext context&#41; throws Exception &#123;
                    
                          Publisher publisher = &#40;Publisher&#41;context.getRequestScope&#40;&#41;.get&#40;PUBLISHER_FORM_OBJECT_NAME&#41;;
                    
                    		try&#123;
                    		   	getLibrary&#40;&#41;.storePublisher&#40;publisher&#41;;
                    		   	
                    		        context.getRequestScope&#40;&#41;.setAttribute&#40;"message_publisher_added",
                                                    getMessageSourceAccessor&#40;&#41;.getMessage&#40;"publisher.added",
                                                    new Object&#91;&#93; &#123;publisher.getPublisherName&#40;&#41;&#125;&#41;&#41;;
                                  //context.getRequestScope&#40;&#41;.removeAttribute&#40;"publisher"&#41;;
                    
                    		&#125;catch&#40;org.springframework.dao.DataIntegrityViolationException eive&#41;&#123;
                    
                                  Errors errors = new FormObjectAccessor&#40;context&#41;.getFormErrors&#40;PUBLISHER_FORM_OBJECT_NAME,ScopeType.REQUEST&#41;;
                                  errors.rejectValue&#40;"publisherName","publisher.name.already.exists",
                                                               "Publisher name already exists ..."&#41;;
                                  return error&#40;&#41;;
                            &#125;
                    
                                  return success&#40;&#41;;
                    
                         &#125;
                    		
                    		
                    	public Event Search&#40;RequestContext context&#41; throws Exception &#123;
                    
                          String publisherName = &#40;String&#41;context.getLastEvent&#40;&#41;.getParameter&#40;"publisherName"&#41;;
                    
                          if&#40;publisherName != null&#41;&#123;
                    
                               ArrayList publishers = &#40;ArrayList&#41;getLibrary&#40;&#41;.findPublishersByName&#40;publisherName&#41;;
                    
                               if &#40;publishers.size&#40;&#41; < 1&#41; &#123;
                                    // no subject found
                                    context.getRequestScope&#40;&#41;.setAttribute&#40;"message_no_publisher_found",
                                                    getMessageSourceAccessor&#40;&#41;.getMessage&#40;"no.publisher.found"&#41;&#41;;
                    
                                    return success&#40;&#41;;
                    
                               &#125;
                               // multiple subjects found
                               context.getRequestScope&#40;&#41;.setAttribute&#40;"model_name_publishers", publishers&#41;;
                    
                               return success&#40;&#41;;
                    		&#125;
                    		
                    		return success&#40;&#41;;
                    
                      &#125;
                    
                    &#125;
                    Remark:
                    1. no need get/mess with the Locale manually in action code. This is done automatically after spring RC2 and above.
                    2. SWF don't have getFormErrors() method in FormObjectAccessor.java now , l found it in sandbox , it have to wait until Preview 3 , l guess.
                    3. no more downcasting to get request or response. Get parameters using Event.

                    Question:
                    1. NoSuchFlowExecutionException: No executing flow could be found with id '697B7869-3FC8-6F51-FD18-3CEC73D3649C'
                    -- perhaps the flow has ended or expired? --> if session expired , How SWF handle this ?
                    2. add support for ApplicationContextAware in AbstractAction ? Hihihi ...

                    the edit function is nice , --> u can undo regret thing ...hihihi

                    Comment


                    • #11
                      Moon,

                      Few remarks:
                      - Don't call context.getLastEvent() -- use context.getOriginatingEvent() instead. That will get you the event that orginated the client request into the web flow system -- in this case the http servlet event. lastEvent returns the last event that occured -- it could be an internal event signaled by an action that executed previously in the current request context, for example (not what you want here.)

                      - Session expiration: consider setting up a HandlerExceptionResolver that maps the NoSuchFlowExecutionException to an error page, perhaps with a flow restart capability.

                      - We'll see about adding support for ApplicationContextAware. I'm not so sure yet. To be honest, I really think you should be doing message resolution from the views.

                      Comment


                      • #12
                        Exception handling....

                        - Session expiration: consider setting up a HandlerExceptionResolver that maps the NoSuchFlowExecutionException to an error page, perhaps with a flow restart capability.
                        Is it possible or will it be possible to have this as part of the config xml rather than coding it explicitly. For instance something like:

                        Code:
                        <webflow id="contactUs" start-state="pre.select.topic.action">
                           <exception class="org.springframework.web.flow.NoSuchFlowExecutionException
                        " to="contactUs"/>
                        ...

                        Comment


                        • #13
                          Yes good idea.

                          We also want global states and global state transitions as well: e.g a global 'help' event always spawning a help flow regardless of where it is signaled.

                          Keith

                          Comment


                          • #14
                            Can you create a JIRA issue for this? Thanks!

                            Comment


                            • #15
                              l finally finished implement CRUD Controler in SWF , it has a simple look. l want to ask , If it possible to post my codes here for discussion ( 8,9 classes , l guess ) ? because l would like to know it is OK or not , but afraid "fooding" the forum ...hihi
                              To be honest. I really think you should be doing message resolution from the views
                              Yes . Keith , l saw u emphasized twice , if l do it in view , how can l display message like

                              "The Publisher Keith Publisher have been added ...

                              in my successView ? why it is important to do message resolution from the views ? hope u don't feel annoying to explain ..

                              moon

                              Comment

                              Working...
                              X