Announcement Announcement Module
No announcement yet.
Accessing Service Objects in Actions Page Title Module
Move Remove Collapse
Conversation Detail Module
  • Filter
  • Time
  • Show
Clear All
new posts

  • Accessing Service Objects in Actions

    I have an action class that needs to access Product and Catalog information in my database for display. What is the established practice for accessing service objects within spring webflow actions?

    So far, I have come accross one listing that said to have actions be context aware.

    public class ActionObject extends AbstractAction implements ApplicationContextAware

    Any other alternatives?


  • #2
    Look how the Phonebook sample does this.

    Do not use ApplicationContextAware to do a service lookup. Have Spring inject the reference to the service object to the action through a setter or constructor argument.



    • #3
      That is what I have done and for some strange reason whenever I call my event method in my Action class I am getting a NullPointerException. I know my wiring is fine, because I am calling this same service object in one of my spring mvc controllers (which uses setter injection) and everything works fine. Anyways, I thought perhaps it had to do with spring webflow.

      method interface:

      public Event getCategory (RequestContext context) throws Exception

      In the above method I check to see if the reference to the service object is null, and it is. However, I know in my Action class that service object reference is set when the application loads. I did a simple println inside the setter method to see if it is getting called.

      Below is the exception I am getting.

      at com.oim.commerce.web.spring.flow.action.CatalogAct ion.getCategory(
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Nativ e Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(Native
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(De
      at java.lang.reflect.Method.invoke(
      at org.springframework.util.DispatchMethodInvoker.dis patch(
      at Execute(
      at org.springframework.web.flow.action.AbstractAction .execute(
      at org.springframework.web.flow.ActionState$ActionExe cutor.execute(
      at org.springframework.web.flow.ActionState.doEnter(A
      at org.springframework.web.flow.State.enter(State.jav a:164)
      at org.springframework.web.flow.execution.impl.StateC ontextImpl.spawn(
      at org.springframework.web.flow.execution.impl.FlowEx ecutionImpl.start(
      at org.springframework.web.flow.execution.FlowExecuti onManager.onEvent(
      at org.springframework.web.flow.execution.FlowExecuti onManager.onEvent(
      at org.springframework.web.flow.execution.servlet.Ser vletFlowExecutionManager.handle(ServletFlowExecuti
      at org.springframework.web.flow.mvc.FlowController.ha ndleRequestInternal(
      at org.springframework.web.servlet.mvc.AbstractContro ller.handleRequest(
      at org.springframework.web.servlet.mvc.SimpleControll erHandlerAdapter.handle(SimpleControllerHandlerAda
      at org.springframework.web.servlet.DispatcherServlet. doDispatch(
      at org.springframework.web.servlet.DispatcherServlet. doService(
      at org.springframework.web.servlet.FrameworkServlet.s erviceWrapper(
      at org.springframework.web.servlet.FrameworkServlet.d oGet(
      at javax.servlet.http.HttpServlet.service(HttpServlet .java:113)
      at javax.servlet.http.HttpServlet.service(HttpServlet .java:90)
      at com.caucho.server.dispatch.ServletFilterChain.doFi lter(
      at sionInViewFilter.doFilterInternal(OpenSessionInVie
      at org.springframework.web.filter.OncePerRequestFilte r.doFilter(
      at com.caucho.server.dispatch.FilterFilterChain.doFil ter(
      at com.caucho.server.webapp.WebAppFilterChain.doFilte r(
      at com.caucho.server.dispatch.ServletInvocation.servi ce(
      at com.caucho.server.http.HttpRequest.handleRequest(H
      at com.caucho.util.ThreadPool.runTasks(ThreadPool.jav a:450)
      at )


      • #4
        Is Spring instantiating your action? Are you referring to it by id from your webflow definition? Is your setter method correctly coded? Let me see your action bean definition, your action setter logic, how the service is used in the getCategory method, and your corresponding action state def.



        • #5
          Yeah, I pretty much checked everything that I can think off, including what you previously listed.

          The following is what I have:

          public class CatalogAction extends MultiAction {
          private static final String SHOW_CATEGORY = "showCategory";
          private CatalogService catalogService;
          private CategoryService categoryService;

          public CatalogAction () {
          public void setCatalogService (CatalogService catalogService) {
          this.catalogService = catalogService;
          public void setCategoryService (CategoryService categoryService) {
          this.categoryService = categoryService;

          // 1. If the eventId is showCategory

          // a. Retrieve the category id from request

          // b. Retrieve the category and its' subcategories

          // c. Test whether this category has any products.

          // 1. if so store within request context the category's subcategories and products and set eventId to showCategoriesAndProducts

          // 2. else store the category's subcategories within the request context and set eventId to showOnlyCategories

          public Event getCategory (RequestContext context) throws Exception {
          if (context.getSourceEvent().getId().equals(SHOW_CATE GORY)) {
          Long id = Long.valueOf(context.getSourceEvent().getParameter ("id").toString());
          if (id != null) {
          Category category = categoryService.getCategory(id);
          return result("showOnlyCategories");

          /************** Flow Definition ***************************/
          public class BrowseCatalogFlowBuilder extends AbstractFlowBuilder {

          //flow id
          private static final String FLOW_ID = "catalog.Browse";
          // View ids
          private static final String VIEW_ONLY_CATEGORIES_ID = "viewOnlyCategoriesId";
          private static final String VIEW_CATEGORIES_AND_PRODUCTS_ID = "viewCategoriesAndProductsId";
          private static final String VIEW_PRODUCT_DETAIL_ID = "viewProductDetailId";
          //Action ids
          private static final String ACTION_SELECT_CATEGORY_ID = "actionSelectCategoryId";
          private static final String ACTION_GET_PRODUCT_ID = "getProductId";
          //View names
          private static final String VIEW_ONLY_CATEGORIES = "ViewOnlyCategories";
          private static final String VIEW_CATEGORIES_AND_PRODUCTS = "ViewCategoriesAndProducts";
          private static final String VIEW_PRODUCT_DETAIL = "ViewProductDetail";
          //Action method names
          private static final String ACTION_GET_PRODUCT_METHOD = "getProduct";

          protected String flowId() {
          return FLOW_ID;
          public void buildStates() throws FlowBuilderException {

          addActionState (ACTION_SELECT_CATEGORY_ID, method ("getCategory", action (CatalogAction.class)),
          new Transition[] { on ("showOnlyCategories", VIEW_ONLY_CATEGORIES_ID),
          on ("showCategoriesAndProducts", VIEW_CATEGORIES_AND_PRODUCTS_ID)});

          new Transition[] {on("showCategory", ACTION_SELECT_CATEGORY_ID)});

          new Transition[] {on("showCategory", ACTION_SELECT_CATEGORY_ID),
          on ("showProduct", ACTION_GET_PRODUCT_ID)});

          addActionState (ACTION_GET_PRODUCT_ID, method (ACTION_GET_PRODUCT_METHOD, action (ProductAction.class)), on("showProductDetail",VIEW_PRODUCT_DETAIL_ID));



          /********* Relevant webflow-context *******************/
          <bean id="catalogBrowseFrontController" class="org.springframework.web.flow.mvc.FlowContro ller">
          <property name="flow" ref="catalog.Browse"/>

          <bean id="catalog.Browse" class="org.springframework.web.flow.config.FlowFac toryBean">
          <property name="flowBuilder">
          <bean class="com.oim.commerce.web.spring.flow.BrowseCata logFlowBuilder" autowire="byType"/>

          <bean id="catalog.Browse.catalogMultiAction" class="com.oim.commerce.web.spring.flow.action.Cat alogAction">
          <property name="categoryService">
          <ref bean="categoryService"/>
          <property name="catalogService">
          <ref bean="catalogService"/>


          • #6
            Ok the problem is in your action state def and your use of the action factory method. The method you're using has the flow system --- not the spring bean factory -- instantiate a new action instance on each call. When you do this, the default case performs no service autowiring, and that's why your setters for your services aren't being called.

            Since you already have your services and actions managed by spring, the best thing to simply replace "action" with "actionRef"... see the javadocs for these two methods to compare what they do...

            e.g actionRef(CategoryAction.class)

            That does a by-type lookup for an existing action in the spring bean factory instead of instantiating a new one...



            • #7
              Wow, I spent the whole day debugging this, and it was just a matter of switching "action" to "actionRef"

              Thanks Keith.


              • #8
                Originally posted by Keith Donald View Post
                Look how the Phonebook sample does this.

                Do not use ApplicationContextAware to do a service lookup. Have Spring inject the reference to the service object to the action through a setter or constructor argument.


                Thanks Keith this is a good answer. I've been looking for the place where the line between a service and an action is drawn and/or the tension between the two. Maybe I missed this in the documentation, but maybe this should be under a best-practices/quick-start type area. Still I guess maybe the key question is "will this Action functionality be re-used independently from the Action, and if so, it should probably be placed into a service.". Otherwise it's really tempting to just place all logic into the Action itself... Hm.

                Also I don't believe phonebook is currently doing this; looking at the search-flow.xml file it looks like it's using a bean action within a view-state. Of course the original post was over a year ago, hehe.

                Of course I'm a newbie at both Spring and Webflow so I could be insane/reading it incorrectly
                Last edited by functionform; Dec 28th, 2006, 04:41 PM.


                • #9
                  You're pretty much correct in your analysis. And indeed, in SWF 1.0 the phonebook no longer has any real action code in it.