I am developing a web application with AngularJs as front-end and Spring+Hibernate as the service layer. Service Layer has a set of REST API's that are consumed by AngularJs. The front end of the application has no major issues. But in the service layer, I am facing some concurrency issues. For some scenarios, I have to make parallel Asychronous calls to same REST API's from the same http session. When I do that, there are times when I get the above error. Actually, the error keeps of changing everytime. Sometimes I also get NULLPointerException on Query.List() operation. Sometimes I get TransactionResourceAlreadyClosed exception. Various configurations and code snippets are attached below.
Web.xml
<context-param><param-name>contextConfigLocation</param-name><param-value>WEB-INF/config/fs-cxf-serverContext.xml,WEB-INF/config/fs-spring-jpa-config.xml,WEB-INF/config/security-beans.xml</param-value></context-param><listener><listener-class>org.springframework.web.context.request.RequestContextListener</listener-class></listener><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><filter><filter-name>CorsFilter</filter-name><filter-class>org.apache.catalina.filters.CorsFilter</filter-class><init-param><param-name>cors.allowed.origins</param-name><param-value>*</param-value></init-param><init-param><param-name>cors.allowed.headers</param-name><param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Access-Control-Allow-Origin</param-value></init-param><init-param><param-name>cors.exposed.headers</param-name><param-value>Access-Control-Allow-Origin</param-value></init-param><init-param><param-name>cors.allowed.methods</param-name><param-value>GET, POST, PUT, DELETE, OPTIONS, HEAD</param-value></init-param></filter><filter-mapping><filter-name>CorsFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><filter><filter-name>springSecurityFilterChain</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class></filter><filter-mapping><filter-name>springSecurityFilterChain</filter-name><url-pattern>/*</url-pattern></filter-mapping><servlet><description>Servlet to Initialize and shutdown the Scheduler</description><servlet-name>QuartzInitializer</servlet-name><servlet-class>org.quartz.ee.servlet.QuartzInitializerServlet</servlet-class><init-param><param-name>config-file</param-name><param-value>quartz.properties</param-value></init-param><init-param><param-name>shutdown-on-unload</param-name><param-value>true</param-value></init-param><init-param><param-name>start-scheduler-on-load</param-name><param-value>true</param-value></init-param><load-on-startup>2</load-on-startup></servlet><servlet><servlet-name>CXFServlet</servlet-name><servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>CXFServlet</servlet-name><url-pattern>/services/*</url-pattern></servlet-mapping><resource-ref><res-ref-name>jdbc/foodsafetyDS</res-ref-name><res-type>javax.sql.DataSource</res-type><res-auth>Container</res-auth><res-sharing-scope>Shareable</res-sharing-scope></resource-ref>
JPA Configuration
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:task="http://www.springframework.org/schema/task"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"><!-- Hibernate session factory --><bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"><property name="configLocation" value="classpath:hibernate.cfg.xml" /><property name="entityInterceptor"><bean class="com.cts.foodSafety.interceptor.AuditLogInterceptor"/></property></bean><bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"><property name="sessionFactory" ref="sessionFactory" /></bean><bean id="persistenceAnnotation" class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /><tx:annotation-driven transaction-manager="transactionManager" /><task:annotation-driven/>
Hibernate.cfg.xml
<hibernate-configuration><session-factory><property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property><property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property><property name="hibernate.connection.datasource">java:comp/env/jdbc/foodsafetyDS</property><property name="hbm2ddl.auto">update</property><property name="show_sql">false</property><!-- Mapping with model class containing annotations -->
I have created a simple rest controller using CXF. REST end point is as below
@Path("/units")@Produces(MediaType.APPLICATION_JSON)public interface UnitService extends Serializable {@GET@Path("/getGraphData/{type}")@Produces(MediaType.APPLICATION_JSON)public UnitServiceBean getGraphData(@PathParam("type") String type) throws FoodSafetyException;
}
Implementation of the controller is as below
xxxServiceImpl.java
@Service("unit")@Transactionalpublic class xxxServiceImpl implements xxxService {private static final long serialVersionUID = 1L;@AutowiredxxxDelegate xxxDelegate;@Overridepublic UnitServiceBean getGraphData(String type) throws FoodSafetyException { UnitServiceBean bean = new UnitServiceBean(); List<String[]> data = unitDelegate.getGraphData(type); .... .... return bean;}
Delegate calls the DAO. (We haven't introduced the Business object as of now). Implementation of DAO is as below
@Componentpublic class UnitDAOImpl extends FoodSafetyDAO implements UnitDAO {@Overridepublic List<String[]> getGraphData(String type) { List<String[]> data = new ArrayList<String[]>(); Map<String, List<UnitReadingEntity>> readings = new HashMap<String, List<UnitReadingEntity>>(); SimpleDateFormat df = CommonConstants.TIME_FORMATTER; // Get all the Units Query query = getSession().getNamedQuery( QueryConstants.FIND_UNITS_BY_TYPE); query.setParameter(QueryConstants.TYPE, type); List<UnitEntity> units = (List<UnitEntity>) query.list(); if (units != null && units.size() != 0) { ..... ..... for (UnitEntity unit : units) { Calendar cal1 = Calendar.getInstance(); cal1.add(Calendar.DAY_OF_YEAR, -1); query = getSession().getNamedQuery( QueryConstants.FIND_READING_BY_UNIT_TIME).setLong( QueryConstants.UNIT_ID, unit.getUnitId()); List<UnitReadingEntity> rr = query.list(); ... ... } } return data;}
Now, when I call the REST service "getGraphData" once using POSTMAN etc. it works fine without any issue. But when I simulate concurrent call to this rest service using I get following error
Caused by: org.hibernate.AssertionFailure: possible non-threadsafe access to the sessionat org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:130)at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:1108)at org.hibernate.loader.Loader.processResultSet(Loader.java:964)at org.hibernate.loader.Loader.doQuery(Loader.java:911)at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:342)at org.hibernate.loader.Loader.doList(Loader.java:2526)at org.hibernate.loader.Loader.doList(Loader.java:2512)at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2342)at org.hibernate.loader.Loader.list(Loader.java:2337)at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:495)at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:356)at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:195)at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1269)at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)at com.cts.foodSafety.model.dao.impl.UnitDAOImpl.getGraphData(UnitDAOImpl.java:255)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)at java.lang.reflect.Method.invoke(Method.java:597)at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:51)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:42)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)at com.sun.proxy.$Proxy51.getGraphData(Unknown Source)at com.cts.foodSafety.delegate.UnitDelegate.getGraphData(UnitDelegate.java:39)at com.cts.foodSafety.delegate.UnitDelegate$$FastClassByCGLIB$$aac35d0d.invoke(<generated>)at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:51)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:42)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)at com.cts.foodSafety.delegate.UnitDelegate$$EnhancerByCGLIB$$75af66.getGraphData(<generated>)at com.cts.foodSafety.service.impl.UnitServiceImpl.getGraphData(UnitServiceImpl.java:108)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)at java.lang.reflect.Method.invoke(Method.java:597)at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:51)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:42)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:55)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96)at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)at com.sun.proxy.$Proxy72.getGraphData(Unknown Source)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)at java.lang.reflect.Method.invoke(Method.java:597)at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:180)at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96)
Client that I used to make concurrent request is as below
public class RestClient implements Runnable{private String i;public RestClient(String x) { i=x;}public static void main(String[] args) { for(int i =1;i<=2;i++) { Thread t = new Thread(new RestClient(String.valueOf(i))); t.start(); }}@Overridepublic void run() { System.out.println("started "+ i); try { URL url = new URL(" http://localhost:8080/fs/services/units/getGraphData/Freezer"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept", "application/json"); if (conn.getResponseCode() != 204) { throw new RuntimeException("Failed : HTTP error code : "+ conn.getResponseCode()); }
Any pointers/suggestions ? I have gone through a lot of material on sessions and transaction management in Spring+hibernate. But I am not able to get my head around this problem.