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.