1
votes

I am having a Grails 2.2.4 project which is running fine. Recently i got an issue. The application prints a report and it is downloaded as PDF. A customer has a huge amount of records in DB for that report and thus the report is taking a lot of time to generate. Therefore i need to add this as a new thread to run in background and generate and mail the report to user.

So i have created a new Class which implements Runnable and calls the same service which was called from controller.

public class JasperReportProcessor implements Runnable {

  private static final Logger LOG =     LoggerFactory.getLogger(JasperReportProcessor.class);

  private ReportProcessor reportProcessor;
  private Parties party;
  private String bObject;
  private String contextUrl;
  private String shipmentIds;
  private Map<String, String> parameters;
  private Locale locale;


  public JasperReportProcessor() {}

  public JasperReportProcessor(final ReportProcessor reportProcessor, final Parties party,
      final String bObject, final String contextUrl, final String     shipmentIds, final Map<String, String> parameters) {
    this.reportProcessor = reportProcessor;
    this.party = party;
    this.bObject = bObject;
    this.contextUrl = contextUrl;
    this.shipmentIds = shipmentIds;
    this.parameters = parameters;
    this.locale = locale;
  }

  @Override
  public void run() {
    SessionFactory sessionFactory = DataSQLSupport.getSessionFactory();
    Session currentSession = null;
    Transaction tx =null;
    try {
      currentSession = sessionFactory.openSession();
      tx = currentSession.beginTransaction();
      LOG.info("THREAD STARTED for REPORT....................");
      //Below code was earlier called directly from Controller.
      Object[] reportData = this.reportProcessor.process(this.party,     this.bObject, this.contextUrl, this.shipmentIds, this.parameters,     this.locale);
      LOG.info("REPORT generated....{}", reportData[0]);
      LOG.info("THREAD ENDED...........");
      tx.commit();
    } catch (Exception ex) {
      LOG.error("OMG ERROR in THREAD....", ex);
      tx.rollback();
    } finally {
      currentSession.flush();
      currentSession.close();
    }

  }

}

The controller code is as below.

if(!shippingReportProcessor.isReportLarge(params.selecteditems, params.level)){
                    Object[] reportData = shippingReportProcessor.process(session["usercompany"], bObject, url, params.selecteditems, params, request.getLocale())

                    response.setContentType(new MimetypesFileTypeMap().getContentType(reportData[0]))
                    response.setHeader("Content-disposition",   "attachment; filename=\""+reportData[0]+"\"")
                    response.outputStream << reportData[1]
                  } else {
                    Thread reportThread = new Thread(new JasperReportProcessor(tpsynergyUtilsService, shippingReportProcessor, session["usercompany"], bObject, url, params.selecteditems, params, request.getLocale()));
                    reportThread.start();
                  }

I also have another java class in src/java in my grails project which is marked as @component, SessionFactory is autowired. Below method works fine when the report generation service is called from controller.

@Component
class DataSQLSupport{
private static SESSION_FACTORY;

public DataSQLSupport() {}

@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
  SESSION_FACTORY = sessionFactory;
}

public static SessionFactory getSessionFactory(){
  return SESSION_FACTORY;
}

public static List<Object[]> getDataList(final String dataQuery) {
    Session currentSession = SESSION_FACTORY.getCurrentSession();
    Query query = currentSession.createSQLQuery(dataQuery);
    return (List<Object[]>) query.list();
}
}

But getCurrentSession method is throwing below exception when called from new Thread created in controller method above.

Message: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
   Line | Method
->> 238 | getDataList in tpsynergy.DataSQLSupport
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|    51 | process     in tpsynergy.reports.ShippingReportProcessor
|    58 | run . . . . in tpsynergy.reports.JasperReportProcessor
^   745 | run         in java.lang.Thread

I have tried adding the below in datasource.groovy

hibernate.current_session_context_class = 'thread'

I have also tried invoking the thread from service instead of controller but that didn't work either. I also opened transaction in thread.

I have searched for the solution but mostly i found were configuration issues. But since my project is working fins when report generation service is directly called from controller and the issue occurrs only when new Thread is created. So it doesn't seem to be configuration issue.

I have added code to create new session in the new thread as well but that doen't seem to be working.

Also the issue is only with the src/java class code(Which i have added above). DB interactions in other services from new Thread are working fine. The issue is only with getCurrentSession. openSession is working perfectly fine in src.java class in grails.

Please suggest what might be the issue.

1

1 Answers

0
votes

Here is what I have been doing to have a session in an async processing thread pool based on what the Quartz plugin does :

// Inject persistence interceptor
PersistenceContextInterceptor persistenceInterceptor

void myMethod() {
    persistenceInterceptor.init()

    // insert code here. Transactions are handled by service classes.

    persistenceInterceptor.flush()
    persistenceInterceptor.clear()
    persistenceInterceptor.destroy()
}

I hope that helps you.