I'm using Spring 3 with JPA and I'm seeing an intermittent issue in my web application.
I have wrapper for the JPA EntityManager that calls the underlying EntityManager crud methods. I see a NPE sometimes when I call entityManager.persist(object);
It looks the the database connection is lost but I'm not 100% what is the cause. Anyone have any details on what may have caused the below exception?
Spring Version: 3.0.6.RELEASE
Spring 3 LazyConnectionDataSourceProxy.java line 416:
if (this.autoCommit != null && this.autoCommit != this.target.getAutoCommit()) {
this.target.setAutoCommit(this.autoCommit);
}
Exception:
Caused by: java.lang.NullPointerException
at org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy$LazyConnectionInvocationHandler.getTargetConnection(LazyConnectionDataSourceProxy.java:416)
at org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy$LazyConnectionInvocationHandler.invoke(LazyConnectionDataSourceProxy.java:376)
at $Proxy64.prepareStatement(Unknown Source)
at org.hibernate.jdbc.AbstractBatcher.getPreparedStatement(AbstractBatcher.java:534)
at org.hibernate.jdbc.AbstractBatcher.prepareSelectStatement(AbstractBatcher.java:145)
at org.hibernate.id.SequenceGenerator.generate(SequenceGenerator.java:96)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:122)
at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:154)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:110)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:646)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:620)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:624)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:220)
at sun.reflect.GeneratedMethodAccessor101.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:592)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
at $Proxy79.persist(Unknown Source)
at myapp.api.dao.impl.GenericDAOImpl.save(GenericDAOImpl.java:50)
at sun.reflect.GeneratedMethodAccessor100.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:592)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy119.save(Unknown Source)
at myapp.api.service.impl.backoffice.StoringServiceImpl.store(StoringServiceImpl.java:89)
at myapp.api.service.impl.backoffice.StoringServiceImpl.storeIncludedFeatureMessage(StoringServiceImpl.java:68)
at sun.reflect.GeneratedMethodAccessor124.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:592)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy168.storeIncludedFeatureMessage(Unknown Source)
at myapp.api.listener.backoffice.StorableMessageListener.processNew(StorableMessageListener.java:136)
at myapp.api.listener.backoffice.StorableMessageListener.onMessage(StorableMessageListener.java:187)
... 34 more
Spring Configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:hz="http://www.hazelcast.com/schema/spring"
xsi:schemaLocation="
http://www.hazelcast.com/schema/spring
http://www.hazelcast.com/schema/spring/hazelcast-spring-2.5.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- Generic -->
<context:annotation-config />
<context:component-scan base-package="myapp.api" />
<aop:aspectj-autoproxy/>
<!-- JPA -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<tx:annotation-driven />
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="persistenceUnitName" value="MyApp" />
<property name="jpaProperties">
<props>
<prop key="hibernate.use_sql_comments">true</prop>
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.archive.autodetection">class</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.provider_class">com.hazelcast.hibernate.provider.HazelcastCacheProvider</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.use_minimal_puts">true</prop>
</props>
</property>
</bean>
<hz:hazelcast id="instance">
<hz:config>
//rest of Hazelast config here
</hz:config>
</hz:hazelcast>
<hz:hibernate-region-factory id="regionFactory" instance-ref="instance"/>
<!-- Define JPA Provider Adapter -->
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.OracleDialect" />
</bean>
<bean id="dataSourceTarget" class="oracle.jdbc.pool.OracleDataSource" destroy-method="close">
<property name="URL" value="jdbc:oracle:thin:@server:1525:name" />
<property name="user" value="test" />
<property name="password" value="123" />
<property name="connectionCachingEnabled" value="true" />
<property name="connectionCacheProperties">
<props merge="default">
<prop key="MinLimit">5</prop>
<prop key="MaxLimit">50</prop>
</props>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
<property name="targetDataSource" ref="dataSourceTarget"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="false"/>
<bean id="genericDAO" class="myapp.api.dao.impl.GenericDAOImpl">
<constructor-arg>
<value>java.io.Serializable</value>
</constructor-arg>
</bean>
<bean id="springContextHolder" class="myapp.api.util.SpringContextHolder" factory-method="getInstance" />
<bean id="executionInterceptor" class="myapp.api.listener.backoffice.ExecutionInterceptor" />
</beans>
AOP Error Handler:
package myapp.api.listener.backoffice;
import javax.jms.Message;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.util.StopWatch;
@Aspect
public class ExecutionInterceptor extends BaseListener{
//protected Log log = LogFactory.getLog(this.getClass());
private String errorDestination="ErrorQ";
@Autowired
@Qualifier("jmsTemplate")
JmsTemplate jmsTemplate;
@Around("execution(* onMessage(javax.jms.Message))")
public Object aroundOnMessage(ProceedingJoinPoint pjp) throws Throwable{
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object retVal = pjp.proceed();
stopWatch.stop();
log.trace( pjp.getSignature().getName() + " Execution Time: " + stopWatch.getTotalTimeMillis()+" ms" );
return retVal;
}
@AfterReturning("execution(* onMessage(javax.jms.Message))")
public void afterOnMessage(){
// logic to capture time
log.debug("*****************************EXIT ONMESSAGE*******************************");
}
@Around("execution(* commit(..))")
public Object aroundCommit(ProceedingJoinPoint pjp) throws Throwable{
try{
return pjp.proceed();
}catch(Throwable ex){
log.error( "Unexpected Error occured during database commit routing message to ErrorQ", ex);
jmsTemplate.convertAndSend(errorDestination, ex);
throw ex;
}finally{
log.trace( "Commiting Transaction...");
}
}
@AfterThrowing(value="execution(* onMessage(javax.jms.Message) throws java.lang.RuntimeException)",throwing="ex")
public void afterThrowingOnMessage(JoinPoint jp,RuntimeException ex) throws RuntimeException{
log.trace("Unexpected Error occured during onMessage processing routing to ErrorQ");
log.error("{{ERROR}}", ex);
Object[] args = jp.getArgs();
if (args!=null && args[0] instanceof javax.jms.Message){
Message msg = (Message)args[0];
jmsTemplate.convertAndSend(errorDestination, msg);
log.info("Unexpected Error occured successfully routed to ErrorQ");
}else
log.info("Unexpected Error occured failed to route to ErrorQ");
}
}
It seems that the NPE is thrown because the connection is null (variable this.target).
The connection is obtained from the DataSource a few lines before (LazyConnectionDataSourceProxy.java):
// Fetch physical Connection from DataSource.
this.target = (this.username != null) ? getTargetDataSource().getConnection(this.username, this.password) : getTargetDataSource().getConnection();
Googling I have found that Oracle JDBC driver may return a null connection in the following situation (link):
ConnectionWaitTimeout
Specifies cache behavior when a connection is requested and there are already MaxLimit connections active. If ConnectionWaitTimeout is greater than zero, then each connection request waits for the specified number of seconds or until a connection is returned to the cache. If no connection is returned to the cache before the timeout elapses, then the connection request returns null.
Default: 0 (no timeout)
So, I guess, a connection timeout could explain your inconsistent problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With