Question

The Spring MVC application needs to execute an intensive computation job which takes several minutes. The client wants to run this job in asynchronous way. But after I enable the @Async annotation on the method (see code 1) and get the error (see code 2). My web.xml and web-appliaiton-context.xml are also presented below. I tried to find a workaround to fix this, but failed. Please help.

code 1:

@Service
public class AsyncServiceImpl implements AsyncServiceInt{

    protected int pertArrSize = 1000;
    @Autowired
    protected TblvDao tblvDao1 = null;

    @Override
    @Async
    public void startSlowProcess(Integer scenarioId) throws SecurityException, IllegalArgumentException, IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        batchUpdateSummaryPertSim(scenarioId);

    }

    public void batchUpdateSummaryPertSim(Integer scenarioId) throws SecurityException, IllegalArgumentException, IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException
    {
        double[] summaryArry = new double[pertArrSize];
        summaryArry = summaryPertSim_env(scenarioId, summaryArry);
    }

    @Transactional
    private double[] summaryPertSim_env(Integer  scenarioId,
    double[] summaryArry) throws IOException, ClassNotFoundException, SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
        ScenarioTblv scenario = tblvDao1.getScenarioTblv(scenarioId);
        TblvResultSaved savedResult = scenario.getTblvResultSaved();
        TblvScenarioCategory4 tblvScenarioCategory4 = new TblvScenarioCategory4();
        List<TblvScenarioCategory4> tblvScenarioCategory4List = scenario
        .getTblvScenarioCategory4List();
        Double min = 0.0, max = 0.0, ml = 0.0;
        Integer conf = 4;
        if (savedResult.getEnvDist() != null) {

            byte[] st = (byte[]) savedResult.getEnvDist();
            ByteArrayInputStream baip = new ByteArrayInputStream(st);
            ObjectInputStream ois = new ObjectInputStream(baip);

            summaryArry = (double[]) ois.readObject();
            } else {
            for (int i = 0; i < tblvScenarioCategory4List.size(); i++) {
                tblvScenarioCategory4 = tblvScenarioCategory4List.get(i);

                vTblvScenarioEffectChoose v = tblvDao1
                .getvTblvScenarioEffectChooseById(tblvScenarioCategory4
                .getId());

                if (v.getC1id() == 1) {
                    tblvScenarioCategory4.setImpactYearlyUnitsSim(pertArrSize);
                    min = tblvScenarioCategory4
                    .getTblvEffectScenarioImpactUnitValue()
                    .getLowValue();
                    max = tblvScenarioCategory4
                    .getTblvEffectScenarioImpactUnitValue()
                    .getHighValue();
                    ml = tblvScenarioCategory4
                    .getTblvEffectScenarioImpactUnitValue()
                    .getMostValue();
                    conf = tblvScenarioCategory4
                    .getTblvEffectScenarioImpactUnitValue().getConf();
                    if ((conf == null) || (conf == 0))
                    conf = 4;

                    double[] MOPertArr;

                    MOPertArr = getPertArry(min, max, ml, conf, pertArrSize);

                    double[] benefitResult = new double[pertArrSize];

                    for (int j = 0; j < pertArrSize; j++) {
                        tblvScenarioCategory4.setSimIndex(j);
                        tblvScenarioCategory4
                        .getTblvEffectScenarioImpactUnitValue()
                        .setHighValue(MOPertArr[j]);
                        benefitResult[j] = tblvScenarioCategory4.getHighPvSum();
                        summaryArry[j] = summaryArry[j] + benefitResult[j];
                    }
                    tblvScenarioCategory4
                    .getTblvEffectScenarioImpactUnitValue()
                    .setHighValue(max);
                }

            }
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(summaryArry);
            byte[] summaryArryAsBytes = baos.toByteArray();
            savedResult.setEnvDist(summaryArryAsBytes);
            tblvDao1.saveTblvResultSaved(savedResult);
        }
        return summaryArry;
    }

code 2:

 10:26:06,233 ERROR org.hibernate.LazyInitializationException:42 - failed to lazily initialize a collection of role: com.pb.prism.model.db.ScenarioTblv.tblvScenarioCategory4List, no session or session was closed
    org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.pb.prism.model.db.ScenarioTblv.tblvScenarioCategory4List, no session or session was closed
        at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
        at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
        at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119)
        at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248)
        at com.pb.prism.util.AsyncServiceImpl.summaryPertSim_env(AsyncServiceImpl.java:446)
        at com.pb.prism.util.AsyncServiceImpl.batchUpdateSummaryPertSim(AsyncServiceImpl.java:41)
        at com.pb.prism.util.AsyncServiceImpl.startSlowProcess(AsyncServiceImpl.java:34)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
        at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:80)
        at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
        at java.util.concurrent.FutureTask.run(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)

Web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">


   <context-param>
      <param-name>log4jConfigLocation</param-name>
      <param-value>/WEB-INF/log4j.properties</param-value>
   </context-param>
   <listener>
      <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
   </listener>
   <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener>
   <listener>
      <listener-class>com.pb.prism.listener.MTAServletContextListener</listener-class>
   </listener>
   <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/spring/web-application-context.xml</param-value>
   </context-param>
   <filter>
      <filter-name>openEntityManagerInViewFilter</filter-name>
      <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
   </filter>
   <filter-mapping>
      <filter-name>openEntityManagerInViewFilter</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
   <!-- Enables Spring Security -->
   <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>
   <filter>
      <filter-name>encoding-filter</filter-name>
      <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
      <init-param>
         <param-name>encoding</param-name>
         <param-value>UTF-8</param-value>
      </init-param>
   </filter>
   <filter-mapping>
      <filter-name>encoding-filter</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
   <filter>
      <filter-name>UrlRewriteFilter</filter-name>
      <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
   </filter>
   <filter-mapping>
      <filter-name>UrlRewriteFilter</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
   <!-- Handles all requests into the application -->
   <filter>
      <filter-name>OpenSessionInViewFilter</filter-name>
      <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
   </filter>
   <filter-mapping>
      <filter-name>OpenSessionInViewFilter</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
   <servlet>
      <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value />
      </init-param>
      <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
      <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
      <url-pattern>/app/*</url-pattern>
   </servlet-mapping>
</web-app>

web-appliation-context.xml

<?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:task="http://www.springframework.org/schema/task"
    xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
                         http://www.springframework.org/schema/beans/spring-beans.xsd 
                         http://www.springframework.org/schema/task
                         http://www.springframework.org/schema/task/spring-task-3.0.xsd
                         http://www.springframework.org/schema/context
                         http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.pb.prism" />

    <!-- Imports the configurations of the different infrastructure systems of the application -->
    <import resource="data-access-context.xml" />
    <import resource="security-context.xml" />
    <import resource="webmvc-context.xml" />

    <bean id="applicationContextProvider" class="com.pb.prism.context.ApplicationContextProvider"></bean>

    <!-- Configure the multipart resolver -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- one of the properties available; the maximum file size in bytes -->
        <property name="maxUploadSize" value="2000000"/>
    </bean>


        <task:annotation-driven executor="myExecutor" />    
        <task:executor id="myExecutor" pool-size="5"/>
</beans>
Was it helpful?

Solution

The problem here is, that Spring's AOP proxies don't extend but rather wrap your service instance to intercept calls. This has the effect, that any call to "this" from within your service instance is directly invoked on that instance and cannot be intercepted by the wrapping proxy (the proxy is not even aware of any such call). (as explained in Spring @Transaction method call by the method within the same class, does not work? )

A possible solution is to extract the transactional code from the service, and put it in a separate class. This way, the call to the transactional method can be intercepted, and a transaction is available.

Eg.

@Service
public class AsyncServiceImpl implements AsyncServiceInt{

@Autowired private SlowProcess slowProcess;

@Override
@Async
public void startSlowProcess(Integer scenarioId) {
    slowProcess.execute(param);
}

..

public class SlowProcess {

   @Transactional
   public double[] execute() { .. }

}

OTHER TIPS

You can try following

@Async
@Transactional(propagation = Propagation.REQUIRES_NEW)

There is no issue with your @Async it is due to your your entity class, when you declare mapping from one to many, try specifying the fetchType to EAGER. Some thing like this:

@OneToMany(fetch=FetchType.EAGER)
public Collection<Role> getRoleSet(){
...
 }

You can try

@Proxy (lazy = false)

On top of both entity class. It works in my case.

use Hibernate.initialize(obj) in your DAO class logic to avoid lazy loading exceptions.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top