1

I'm having trouble setting up Spring with Hibernate under GWT framework. I am fairly new to GWT. I have the application context set up and loading without output errors, but my main issue at the moment is that the my Service layer implementation (PobaseServiceImpl) requires a DAO that I set up in the appcontext but its not wrapping the DAO. Naturally my RPC is attempting to call the dao methods resulting in a NullPointerException. The pobaseDao is not being set by the TransactionProxyFactoryBean when I initialize it.

In summary: The DAO should be created by (that is, configured into) Spring just like the rest of my services. Then injected to the services via Spring. Then with the DAO, wrap it in a Spring transaction proxy (org.springframework.transaction.interceptor.TransactionProxyFactoryBean) and give it a Hibernate SessionFactory (org.springframework.orm.hibernate4.LocalSessionFactoryBean). But for some reason its not setting the dao

So my application context is being loaded through a ServletContextListener. Here is my application context:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

  <!--
    Spring Framework application context definition for the POBASE Website.
  -->

<beans>

  <!-- Configurer that replaces ${...} placeholders with values from a properties file -->
  <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
      <list>
         <value>classpath:/pobase.properties</value>
         <value>file:${user.home}/pobase.properties</value>
      </list>
    </property>
    <property name="ignoreResourceNotFound" value="no"/>
  </bean>

  <!-- Hibernate Data Source -->
  <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${pobase.database.driver}" />
    <property name="url" value="${pobase.database.url}" />
    <property name="username" value="${pobase.database.user}" />
    <property name="password" value="${pobase.database.password}" />
  </bean>

  <!-- Hibernate SessionFactory -->
  <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource">
      <ref bean="dataSource" />
    </property>
    <property name="packagesToScan" value="nz.co.doltech.pobase.client.entity"/>
    <property name="hibernateProperties">
      <props>
        <prop key="hibernate.dialect">${pobase.hibernate.dialect}</prop>
        <prop key="hibernate.show_sql">${pobase.hibernate.show_sql}</prop>
        <prop key="javax.persistence.validation.mode">none</prop>
      </props>
    </property>
  </bean>

  <!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
  <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
  </bean>

  <!-- Default transaction proxy, defining the transactional behaviour for
    a typical Dao configuration -->
  <bean id="baseDaoTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
    abstract="true">
    <property name="transactionManager" ref="transactionManager" />
    <property name="transactionAttributes">
      <value>*=PROPAGATION_MANDATORY</value>
    </property>
  </bean>

  <!-- Default transaction proxy, defining the transactional behaviour for
    a typical Service configuration -->
  <bean id="baseServiceTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
    abstract="true">
    <property name="transactionManager" ref="transactionManager" />
    <property name="transactionAttributes">
      <value>*=PROPAGATION_REQUIRED</value>
    </property>
  </bean>

  <!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->

  <bean id="pobaseDao" parent="baseDaoTransactionProxy">
    <property name="target" ref="pobaseDaoTarget" />
  </bean>
  <bean id="pobaseDaoTarget" class="nz.co.doltech.pobase.server.dao.PobaseHibernateDao">
    <property name="sessionFactory" ref="sessionFactory" />
  </bean>

  <bean id="pobaseService" parent="baseServiceTransactionProxy">
    <property name="target" ref="pobaseServiceTarget" />
  </bean>
  <bean id="pobaseServiceTarget" class="nz.co.doltech.pobase.server.service.PobaseServiceImpl">
    <property name="pobaseDao" ref="pobaseDao" />
    <!-- property name="accessControlService" ref="accessControlService" />
    <property name="lookupService" ref="lookupService"/>
    <property name="notificationService" ref="notificationService"/ -->
  </bean>

</beans>

and here is my RPC servlet implementation:

package nz.co.doltech.pobase.server.service;

/**
 * Extends RSS and implements the PobaseService
 * @author Ben
 */
@SuppressWarnings("serial")
public class PobaseServiceImpl extends RemoteServiceServlet implements PobaseService {

    @SuppressWarnings("unused")
    private static final Logger logger = Logger.getLogger(PobaseServiceImpl.class.getName());

    private PobaseDao pobaseDao;
    private final HashMap<Integer, PobaseEntity> pobaseEntities = new HashMap<Integer, PobaseEntity>();

    private void fetchExternPobase()
    {
        pobaseEntities.clear();
        List<PobaseEntity> pobaseList = pobaseDao.getAllPobase();
        for (int i = 0; i < pobaseList.size(); i++)
        {
            PobaseEntity en = pobaseList.get(i);
            if(en != null) {
                pobaseEntities.put(en.getId(), en);
            }
        }
    }

    public void setPobaseDao(PobaseDao dao)
    {
        this.pobaseDao = dao;
    }

    public PobaseDao getPobaseDao()
    {
        return this.pobaseDao;
    }

    public PobaseData addLocalPobase(PobaseData pobase)
    {
        PobaseEntity entity = new PobaseEntity();
        entity.mirrorObjectData(pobase);

        pobase.setId(pobaseEntities.size());
        pobaseEntities.put(pobase.getId(), entity); 

        return entity.getDataObject();
    }

    public PobaseData updateLocalPobase(PobaseData pobase)
    {
        PobaseEntity entity = new PobaseEntity();
        entity.mirrorObjectData(pobase);

        pobaseEntities.remove(entity.getId());
        pobaseEntities.put(entity.getId(), entity);

        return entity.getDataObject();
    }

    public Boolean deleteLocalPobase(int id)
    {
        pobaseEntities.remove(id);
        return true;
    }

    public ArrayList<PobaseData> deleteLocalPobases(ArrayList<Integer> ids)
    {
        for (int i = 0; i < ids.size(); ++i) {
            deleteLocalPobase(ids.get(i));
        }

        return getLocalPobaseData();
    }

    public ArrayList<PobaseData> getLocalPobaseData()
    {
        ArrayList<PobaseData> pobaseList = new ArrayList<PobaseData>();
        Iterator<Integer> it = pobaseEntities.keySet().iterator();
        while(it.hasNext())
        {
            PobaseData pobase = pobaseEntities.get(it.next()).getDataObject();
            pobaseList.add(pobase);
        }
        return pobaseList;
    }

    public PobaseData getLocalPobase(int id)
    {
        return pobaseEntities.get(id).getDataObject();
    }

    public ArrayList<PobaseData> resyncExternPobase()
    {
        fetchExternPobase();
        return getLocalPobaseData();
    }

}

Also here is the start-up log for the web application:

ServletContextListener started
Nov 12, 2012 8:20:33 PM nz.co.doltech.pobase.SpringInitialiser initSpringContext
INFO: Creating new Spring context. Configs are [/nz/co/doltech/pobase/appcontext.xml]
Nov 12, 2012 8:20:33 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@8423321: startup date [Mon Nov 12 20:20:33 NZDT 2012]; root of context hierarchy
Nov 12, 2012 8:20:33 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [nz/co/doltech/pobase/appcontext.xml]
Nov 12, 2012 8:20:33 PM org.springframework.core.io.support.PropertiesLoaderSupport loadProperties
INFO: Loading properties file from class path resource [pobase.properties]
Nov 12, 2012 8:20:33 PM org.springframework.core.io.support.PropertiesLoaderSupport loadProperties
INFO: Loading properties file from URL [file:/home/ben/pobase.properties]
Nov 12, 2012 8:20:33 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@4c56666d: defining beans [org.springframework.beans.factory.config.PropertyPlaceholderConfigurer#0,dataSource,sessionFactory,transactionManager,baseDaoTransactionProxy,baseServiceTransactionProxy,pobaseDao,pobaseDaoTarget,pobaseService,pobaseServiceTarget]; root of factory hierarchy
Nov 12, 2012 8:20:33 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
INFO: Loaded JDBC driver: org.postgresql.Driver
Nov 12, 2012 8:20:33 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Nov 12, 2012 8:20:33 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.7.Final}
Nov 12, 2012 8:20:33 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Nov 12, 2012 8:20:33 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Nov 12, 2012 8:20:34 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
Nov 12, 2012 8:20:34 PM org.hibernate.engine.jdbc.internal.LobCreatorBuilder useContextualLobCreation
INFO: HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException
Nov 12, 2012 8:20:34 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000399: Using default transaction strategy (direct JDBC transactions)
Nov 12, 2012 8:20:34 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
Nov 12, 2012 8:20:34 PM org.springframework.orm.hibernate4.HibernateTransactionManager afterPropertiesSet
INFO: Using DataSource [org.springframework.jdbc.datasource.DriverManagerDataSource@55acc0d4] of Hibernate SessionFactory for HibernateTransactionManager
Starting Jetty on port 8888

Anyone have any ideas as to why its not working? Some specs here:

  • Spring 3.2.0
  • Hibernate4
  • GWT SDK 2.4.0

Appreciate any help I can get!

Cheers, Ben

Ben Dol
  • 427
  • 6
  • 18
  • please add the stack-trace, and additionally are you sure you wanna load data and hold them within your service impl? – Francisco Spaeth Nov 12 '12 at 10:00
  • Is there a more conventional way to be storing the data? I am fairly new to GWT so not 100% sure what the best practises are. (Will add stack trace to main post) The null exception is due to the pobaseDao being called on before being set. But by my understanding the transaction proxy and service bean configured in the appcontext.xml should be taking care of this? – Ben Dol Nov 12 '12 at 10:11
  • Well, it depends on your approach but storing data within service implementation is reasonable when you wanna perform something like a cache, otherwise it isn't the way to go. – Francisco Spaeth Nov 12 '12 at 10:17
  • Ah ok, I will keep that in mind, thanks. This application is a test application to build up my knowledge in GWT with Spring, and Hibernate :) – Ben Dol Nov 12 '12 at 10:24
  • Instead of try and see how it goes. – Sajan Chandran Nov 12 '12 at 12:08
  • @SajanChandran thanks for the reply, unfortunately it did not work. I believe I am missing something to do with the service implementation perhaps. The post I have marked as the answer is the best answer so far because it is one method of doing it, but based on what I have been told there is another way to get it hooked in without overriding the RemoteServiceServlet init method, etc. – Ben Dol Nov 12 '12 at 22:55

1 Answers1

1

Your PobaseServiceImpl class is not thread-safe. In Java any remote called service(and of course the same goes the singletons in the container too)should be prepared that its methods will be called from the different threads and the same method can be executed simultaneously in the different threads.

The entire class needs a detailed check.

First of all, you should use ConcurrentHashMap instead of HashMap. And all methods need to be carefully rewritten - for example the method that return ArrayList ( public ArrayList<PobaseData> resyncExternPobase()) should make a defensive copy of the data.

As an alternative you should consider making the public methods your service methods synchronized, however, that will force the methods to be executed sequentially and that will undermine performance if the service is expected to be actively used from the different threads.

For the reference, consider reading Brian Goetz Java Concurrency In Practice, chapter 5. Building Blocks and especially 5.1 Synchronized collections and 5.2. Concurrent collections

update: Your PobaseServiceImpl seems to be a servlet that is instantiated by the servlet container - to populate its fields with Spring beans you're you should use Spring utility methods - for example WebApplicationContextUtils - there are many examples in the internet, like http://pgt.de/2009/07/17/non-invasive-gwt-and-spring-integration-reloaded/

Boris Treukhov
  • 17,493
  • 9
  • 70
  • 91
  • Thank you for your reply, I will certainly look into what you have mentioned. I am fairly new to this type of development so learning the better practises in remote services, etc, is really good. Basically this is a test application for me to learn more about GWT and large web application development. So anyway, is what you mention causing my DAO wrapping issue? – Ben Dol Nov 12 '12 at 10:51
  • @BenDol At first replace the `HashMap` with `ConcurrentHashMap`, add comment if something changed. – Boris Treukhov Nov 12 '12 at 10:52
  • No change in behaviour. I didn't really articulated very well what my issue was in the main post. Basically it seems that the Spring DAO is not being wrapped when I initialize the appcontext. By my understanding, in my appcontext.xml I have defined the a bean with id pobaseService which points to the target bean(pobaseServiceTarget) which has class PobaseServiceImpl and property which should wrap the DAO into the service. But I am obviously missing something, not too sure what. – Ben Dol Nov 12 '12 at 11:00
  • Have you tried to run the application in the debug mode and step into the `fetchExternPobase` method to find the actual line where the exception occurs? – Boris Treukhov Nov 12 '12 at 11:03
  • It is when I call the pobaseDao that is not initialized properly in the service. 'List pobaseList = pobaseDao.getAllPobase();' – Ben Dol Nov 12 '12 at 11:07
  • @BenDol But the Dao reference is not null? – Boris Treukhov Nov 12 '12 at 11:10
  • It is null, that is the issue. The spring context should be initializing the DAO as I have configured it that way in the appcontext.xml. Unless I have done it wrong. – Ben Dol Nov 12 '12 at 11:15
  • The DAO should be created by (that is, configured into) Spring just like the rest of my services. Then injected to the services via Spring. What I do for the DAO is wrap it in a Spring transaction proxy (org.springframework.transaction.interceptor.TransactionProxyFactoryBean) and give it a Hibernate SessionFactory (org.springframework.orm.hibernate4.LocalSessionFactoryBean). But for some reason its not setting the dao. – Ben Dol Nov 12 '12 at 11:57
  • @BenDol i don't yet have experience with GWT but aren't `RemoteServiceServlet` instances are initialized outside of the Spring container - as they are external to Spring. http://mitosome.blogspot.ru/2011/01/injecting-spring-beans-into-gwt.html Also if it's a servlet - servlets should be attached to container manually as they are initialized by the web server. – Boris Treukhov Nov 12 '12 at 12:03
  • Yes, but Spring is initializing it into the service with the proxy I believe. Unless I am too optimistic in thinking that it will be that automated? Perhaps I am missing something to do with @autowired annotation... not sure will keep researching, and hopefully someone will be able to shed some light on this. – Ben Dol Nov 12 '12 at 12:11
  • In this case Spring just creates another Servlet instance(because it's a java class declared in context xml configuration) - how would change the lifecycle of the web application? :) – Boris Treukhov Nov 12 '12 at 12:16
  • 1
    @BenDol there's no magic here - Servlets are instantiated by the servlet container - to access the Spring root web app context from there you should use Spring utility classes, for example like it's done in milan answer : http://stackoverflow.com/questions/8933778/why-doesnt-just-autowiring-a-field-in-a-gwt-servlet-in-spring-work - there some classes on the net to simplify GWT integration - that merely populate the Servlet fields by calling utility methods or provide a façade to Spring with a getBean(beanName) method which is also implemented via Spring utility classes. – Boris Treukhov Nov 12 '12 at 12:36
  • Thanks heaps for the help, that makes a lot more sense now. Most appreciated. – Ben Dol Nov 12 '12 at 21:42