Page 1 of 2 12 LastLast
Results 1 to 10 of 16

Thread: Continuations, DAO, and the Singleton

  1. #1

    Question Continuations, DAO, and the Singleton

    I just tried to switch to continuations and received an error because of the method I am using to access my business objects. Essentially what I am doing is as follows:

    OrderActions.java - the action object (extends FormAction) for the Web Flow which contains setters to receive business object managers, and then places the reference to these managers into the flow scope
    Country.java - a (serializable) business object that would contain a single country
    CountryManager.java - a business object manager which retrieves lists of Country(s), can insert a Country, delete a Country, and update a Country using a DAO
    CountryManagerDao.java - an (interface) definition for what a CountryManagerDao implementation is allowed to do
    CountryManagerSqlMap.java - an implementation of CountryManagerDao for iBatis SQL Maps

    Three beans are then created:

    orderActions - a bean for OrderActions.java for use in the Web Flow which is passed a reference to countryMan (which is then placed into the flow scope)
    countryMan - a bean for CountryManager.java which is passed a reference to countryManDao
    countryManDao - a bean for CountryManagerDaoSqlMap.java

    This allowed me (prior to using Continuations) to retrieve a list of countries to populate form fields from within my .jsp view using the flow scoped countryMan (placed there by orderActions). However, since only Country.java is Serializable, I get the following error:

    Tomcat 5.5.17 with JRE 1.5.0_07, SWF 1.0 RC3, and Spring 2.0 RC2
    Code:
    org.springframework.webflow.execution.repository.continuation.ContinuationCreationException: Could not serialize flow execution; make sure all objects stored in flow scope are serializable; nested exception is java.io.NotSerializableException: com.company.db.PaymentMethodTypeManagerDaoSqlMap
    	org.springframework.webflow.execution.repository.continuation.SerializedFlowExecutionContinuationFactory.createContinuation(SerializedFlowExecutionContinuationFactory.java:71)
    	org.springframework.webflow.execution.repository.continuation.ContinuationFlowExecutionRepository.putFlowExecution(ContinuationFlowExecutionRepository.java:188)
    	org.springframework.webflow.execution.repository.support.RebindingFlowExecutionRepository.putFlowExecution(RebindingFlowExecutionRepository.java:69)
    	org.springframework.webflow.executor.FlowExecutorImpl.launch(FlowExecutorImpl.java:211)
    	org.springframework.webflow.executor.support.FlowRequestHandler.handleFlowRequest(FlowRequestHandler.java:129)
    	org.springframework.webflow.executor.mvc.FlowController.handleRequestInternal(FlowController.java:199)
    	org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:153)
    	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:45)
    	org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:800)
    	org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:730)
    	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:396)
    	org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:350)
    	javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
    	javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
    This is obviously expected, but it raises the question on how is one supposed to get a list of countries if I don't want to make my DAO objects and managers Serializable? Should I be using a different design to store/access my data? I don't want the view to have too much control over the objects, but I also don't want to generate too much code. I used to use ASP.NET with C# and I could simply call the class as a static class (you can access static methods directly if you don't use an instance) in the following way, assuming I have a Country object:

    ASP.NET C# Example
    Code:
    ArrayList alCountries = Country.getAllCountries();
    This does not create an instance of Country, rather it accesses the static getAllCountries() method in the Country class directly, which then returns an ArrayList. This seems like a much cleaner method than having to create a second layer and then inject that into the form controller for IoC. I really want to understand how to do this cleanly in IoC, and would love to hear any suggestions or corrections. I'm having trouble wrapping my head around this.

    Below is the code I am using for all objects referred to above:

    orderActions bean
    Code:
    <bean id="orderActions" class="com.company.web.OrderActions">
    	<property name="countryMan" ref="countryMan"/>
    	<property name="orderformMan" ref="orderformMan"/>
    	<property name="paymentMethodTypeMan" ref="paymentMethodTypeMan"/>
    	<property name="stateMan" ref="stateMan"/>
    	<property name="userMan" ref="userMan"/>
    	<property name="validator">
    		<bean class="com.company.web.OrderValidator">
    			<property name="userMan" ref="userMan"/>
    		</bean>
    	</property>
    </bean>
    countryMan bean
    Code:
    <bean id="countryMan" class="com.company.bus.entity.CountryManager">
    	<property name="countryManagerDao" ref="countryManDao"/>
    </bean>
    countryManDao bean
    Code:
    <bean id="countryManDao" class="com.company.db.CountryManagerDaoSqlMap">
    	<property name="dataSource" ref="dataSource"/>
    	<property name="sqlMapClient" ref="sqlMapClient"/>
    </bean>
    com.company.bus.entity.Country.java (Serializable)
    Code:
    package com.company.bus.entity;
    
    import java.io.Serializable;
    import java.lang.Short;
    import java.lang.String;
    
    public class Country implements Serializable
    {
    
    	private Short	id;
    	private String	name;
    
    	public Country ()
    	{
    		this(null,"");
    	}
    
    	public Country (Short id, String name)
    	{
    		this.id = id;
    		this.name = name;
    	}
    
    	//private Short id;
    	public void setId (Short id)
    	{
    		this.id = id;
    	}
    
    	public Short getId ()
    	{
    		return id;
    	}
    
    	//private String name;
    	public void setName (String name)
    	{
    		this.name = name;
    	}
    
    	public String getName ()
    	{
    		return name;
    	}
    
    }
    com.company.bus.entity.CountryManager
    Code:
    package com.company.bus.entity;
    
    import java.lang.Short;
    import java.util.List;
    
    import com.company.bus.entity.Country;
    import com.company.db.CountryManagerDao;
    
    public class CountryManager
    {
    
    	private CountryManagerDao dao;
    
    	public void setCountryManagerDao (CountryManagerDao dao)
    	{
    		this.dao = dao;
    	}
    
    	public List<Country> getCountries ()
    	{
    		return dao.getCountries();
    	}
    
    	public Country getCountryById (Short id)
    	{
    		return dao.getCountryById(id);
    	}
    
    	public void insertCountry (Country country)
    	{
    		dao.insertCountry(country);
    	}
    
    	public void updateCountry (Country country)
    	{
    		dao.updateCountry(country);
    	}
    
    }
    com.company.db.CountryManagerDao.java (interface)
    Code:
    package com.company.db;
    
    import java.lang.Short;
    import java.util.List;
    
    import com.company.bus.entity.Country;
    
    public interface CountryManagerDao
    {
    
    	public List<Country> getCountries ();
    	public Country getCountryById (Short id);
    	public void insertCountry (Country country);
    	public void updateCountry (Country country);
    
    }
    com.company.db.CountryManagerDaoSqlMap.java (CountryManagerDao.java implementation)
    Code:
    package com.company.db;
    
    import java.lang.Short;
    import java.util.List;
    
    import org.springframework.dao.DataAccessException;
    import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
    
    import com.company.bus.entity.Country;
    
    public class CountryManagerDaoSqlMap extends SqlMapClientDaoSupport implements CountryManagerDao
    {
    
    	private Country country;
    
    	public List<Country> getCountries ()
    	{
    		return getSqlMapClientTemplate().queryForList("getCountries",null);
    	}
    
    	public Country getCountryById (Short id)
    	{
    		country = new Country();
    		country.setId(id);
    		country = (Country)getSqlMapClientTemplate().queryForObject("getCountryById",country);
    		return country;
    	}
    
    	public void insertCountry (Country country)
    	{
    		getSqlMapClientTemplate().insert("insertCountry",country);
    	}
    
    	public void updateCountry (Country country)
    	{
    		getSqlMapClientTemplate().update("updateCountry",country);
    	}
    
    }

  2. #2
    Join Date
    Aug 2004
    Location
    Melbourne, FL
    Posts
    2,794

    Default

    Why does your Country entity object depend on its DAO? I would expect it to be serializable with no dependency on the repository that persisted it.

    Unrelated, your CountryManager seems completely overkill: it's not adding any value, I'd collapse a layer and make CountryManager a transaction script with a direct iBatis data access dependency.

    Keith
    Keith Donald
    Core Spring Development Team

  3. #3

    Default

    Quote Originally Posted by Keith Donald
    Why does your Country entity object depend on its DAO? I would expect it to be serializable with no dependency on the repository that persisted it.
    I'm not really sure what you mean by this. Country (that I can tell) doesn't depend on anything, and is Serializable. The CountryManager singleton bean (countryMan) is used to retrieve a list of Country entities from within the .jsp like so:

    Code:
    <tr>
    	<td align="right">Country: </td>
    	<td align="left">
    		<form:select path="billingAddress.countryId">
    			<form:option value="" label="" />
    			<form:options items="${countryMan.countries}" itemValue="id" itemLabel="name" />
    		</form:select></td>
    	<td align="left"><form:errors path="billingAddress.countryId" /></td>
    </tr>
    Did I misunderstand what you meant?

    Quote Originally Posted by Keith Donald
    Unrelated, your CountryManager seems completely overkill: it's not adding any value, I'd collapse a layer and make CountryManager a transaction script with a direct iBatis data access dependency.
    The only reason I had the CountryManager was if I ever needed to switch to a different ORM (such as Hibernate) I could just swap in a new .xml which definied countryManDao which countryMan used. That way I could have a Hibernate implementation class called CountryManDaoHibernate and also my iBatis SQL Map implementation CountryManDaoSqlMap, but only refer inside all of my flow actions to countryMan of type CountryManager, which delegates to my DAO implementation (either CountryManDaoHibernate or CountryManDaoSqlMap depending on what bean .xml I use). Is that ridiculous?
    Last edited by tortuga; Aug 9th, 2006 at 04:36 PM.

  4. #4
    Join Date
    Aug 2004
    Location
    Melbourne, FL
    Posts
    2,794

    Default

    I'm just trying to figure out why the PaymentMethodTypeManagerDaoSqlMap is being serialized at all--is it in flow scope? If so, why? I would only expect the country object to be in flow scope, and the only object eligible for serialization.

    Yes, it's ridiculous. :-) Your service interface already provides the separation from implementation. When your data access logic is the business logic, there is no beneift gained by introducing a separate data access layer...

    Keith
    Last edited by Keith Donald; Aug 9th, 2006 at 07:37 PM.
    Keith Donald
    Core Spring Development Team

  5. #5

    Default

    Quote Originally Posted by Keith Donald
    I'm just trying to figure out why the PaymentMethodTypeManagerDaoSqlMap is being serialized at all--is it in flow scope? If so, why? I would only expect the country object to be in flow scope, and the only object eligible for serialization.
    PaymentMethodTypeManagerDaoSqlMap is being serialized because it is another bean I was injecting into my flow action. The reason CountryManagerDaoSqlMap didn't throw the error is because of some arbitrary order that objects were being serialized. Here is the error when I only try to inject countryMan (com.company.db.CountryManagerDaoSqlMap) only:

    Code:
    org.springframework.webflow.execution.repository.continuation.ContinuationCreationException: Could not serialize flow execution; make sure all objects stored in flow scope are serializable; nested exception is java.io.NotSerializableException: com.company.db.CountryManagerDaoSqlMap
    	org.springframework.webflow.execution.repository.continuation.SerializedFlowExecutionContinuationFactory.createContinuation(SerializedFlowExecutionContinuationFactory.java:71)
    	org.springframework.webflow.execution.repository.continuation.ContinuationFlowExecutionRepository.putFlowExecution(ContinuationFlowExecutionRepository.java:188)
    	org.springframework.webflow.execution.repository.support.RebindingFlowExecutionRepository.putFlowExecution(RebindingFlowExecutionRepository.java:69)
    	org.springframework.webflow.executor.FlowExecutorImpl.launch(FlowExecutorImpl.java:211)
    	org.springframework.webflow.executor.support.FlowRequestHandler.handleFlowRequest(FlowRequestHandler.java:129)
    	org.springframework.webflow.executor.mvc.FlowController.handleRequestInternal(FlowController.java:199)
    	org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:153)
    	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:45)
    	org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:800)
    	org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:730)
    	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:396)
    	org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:350)
    	javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
    	javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
    The reason I was doing this was because I couldn't get the context in my setter. Here are the relevent snippets from my form action:

    Code:
    public class OrderActions extends FormAction
    {
    
    	protected void initBinder (RequestContext context, DataBinder binder)
    	{
    		binder.registerCustomEditor(Integer.class, new CustomNumberEditor(Integer.class, true));
    		binder.registerCustomEditor(Short.class, new CustomNumberEditor(Short.class, true));
    	}
    
    	private CountryManagerDao countryMan;
    
    	/* setupForm action */
    	public OrderActions ()
    	{
    		logger.info("01 - OrderActions.java - Entered setupForm Event!");
    	}
    
    	public Event populateVariables (RequestContext context) throws Exception
    	{
    		logger.info("02 - OrderActions.java - Entered populateVariables Event!");
    		context.getFlowScope().put("countryMan", countryMan);
    		return success();
    	}
    
    	public void setCountryMan (CountryManagerDao countryMan)
    	{
    		this.countryMan = countryMan;
    	}
    
    }
    I want to be able to do the following in my .jsp:
    Code:
    <tr>
    	<td align="right">Country: </td>
    	<td align="left">
    		<form:select path="billingAddress.countryId">
    			<form:option value="" label="" />
    			<form:options items="${countryMan.countries}" itemValue="id" itemLabel="name" />
    		</form:select></td>
    	<td align="left"><form:errors path="billingAddress.countryId" /></td>
    </tr>
    I'm trying not to store too much information in memory (such as a list of countries in every session.. too much). I'm caching what is retrieved from the database on a lower level, so asking the countryMan bean for a list of the countries will retrieve the same cached list over and over. However, if I don't inject the bean into the flow in the way I showed in my flow action above, I can't access it in my JSP file. How else should I do this?

    Quote Originally Posted by Keith Donald
    Yes, it's ridiculous. :-) Your service interface already provides the separation from implementation. When your data access logic is the business logic, there is no beneift gained by introducing a separate data access layer...
    Wow, I didn't realize that an interface could be used directly. I've removed that extra layer, and it seems to work great. Thanks!

  6. #6

    Default

    If anyone has any suggestions, that would be great. Coming from a .NET framework where you have code-behind pages that pre-compile makes this structure very confusing for me.

    -Tortuga

  7. #7

    Default

    Keith, this is driving me nuts. I've tried all kinds of EL syntax and <jsp:useBean> tags. What is the correct way to access fairly static values from the database? Obviously any data lists/maps that are flow/request dependent should be stored in the flow, but a list of countries? That is fairly constant and I want to pull it straight from the database as I showed.

    Thanks

  8. #8
    Join Date
    Aug 2004
    Location
    Melbourne, FL
    Posts
    2,794

    Default

    What we generally recommend is to have your templates "logicless" -- in that they would not invoke the backing service directly. So in this case for every view that needs a country list, you'd simply execute an action that invokes your country service and put the country list in request scope before rendering the view. I believe the shipping-rate sample in SWF shows exactly this.

    Keith
    Keith Donald
    Core Spring Development Team

  9. #9

    Default

    Keith, thanks for getting back to me. That makes a lot of sense. I guess one wouldn't like the view state to be able to access services since database writing could be done in the wrong layer. It's too bad that so much extra data needs to be stored in the continuation repository, but the only other alternative is to hard-code it into the .jsp page, which I don't want to do.

    Thanks!

  10. #10
    Join Date
    Aug 2004
    Location
    Melbourne, FL
    Posts
    2,794

    Default

    If you put the data in requestScope it doesn't get associated with persistent state in the repository...there is no storage overhead there beyond more than one request.

    Keith
    Keith Donald
    Core Spring Development Team

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •