3

I am developing a web app where batch programs need to run for specific times. I used Quartz library to schedule the jobs. The web app is deployed on Websphere 8.5.5 and its working fine, accessing the tables through datasources (Datasource given in code is java:comp/env/jdbc/db_datasource). The job is also triggered at the mentioned times.

I am getting an error when the scheduled job makes a DB connection through the datasource and the error is:

javax.naming.ConfigurationException: A JNDI operation on a "java:" name cannot be completed because the server runtime is not able to associate the operation's thread with any J2EE application component.  This condition can occur when the JNDI client using the "java:" name is not executed on the thread of a server application request.  Make sure that a J2EE application does not execute JNDI operations on "java:" names within static code blocks or in threads created by that J2EE application.  Such code does not necessarily run on the thread of a server application request and therefore is not supported by JNDI operations on "java:" names. [Root exception is javax.naming.NameNotFoundException: Name comp/env/jdbc not found in context "java:".]
at com.ibm.ws.naming.java.javaURLContextImpl.throwExceptionIfDefaultJavaNS(javaURLContextImpl.java:522)
at com.ibm.ws.naming.java.javaURLContextImpl.throwConfigurationExceptionWithDefaultJavaNS(javaURLContextImpl.java:552)
at com.ibm.ws.naming.java.javaURLContextImpl.lookupExt(javaURLContextImpl.java:481)
at com.ibm.ws.naming.java.javaURLContextRoot.lookupExt(javaURLContextRoot.java:485)
at com.ibm.ws.naming.java.javaURLContextRoot.lookup(javaURLContextRoot.java:370)

I understand from the error message is that the job is running outside the J2ee container and so the datasource is not available for the Job to make the connection, which I cannot agree as the Quartz is implemented as the ServletContextListener and the same is mentioned in web.xml.

Web.xml

<listener>
    <listener-class>com.ehacampaign.helper.EHAJobSchedulerListener</listener-class>
</listener>

EHAJobSchedulerListener.java

public class EHAJobSchedulerListener implements ServletContextListener {..}

As you can see the code, the class is registered in the web and I do not understand why it cannot use the datasource in the J2EE container.

Questions are:

  1. Why servlet registered class cannot access the datasource in J2EE container?
  2. If datasource in container cannot be used, then how to make a connection to the DB while executing the job?

NOTE: I have the same setup in JBoss AS 7.1 and the jobs are running smoothly accessing the datasource configured in JBoss AS 7.1. I have to develop this in Websphere as the customer demands it.

UPDATED

I have attached the modified quartz property file. Even after adding the workmanagerthread, I am getting the same error.

org.quartz.threadPool.threadCount=1
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool

org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore
org.quartz.threadExecutor.class=org.quartz.commonj.WorkManagerThreadExecutor
org.quartz.threadExecutor.workManagerName=wm/default
Anand
  • 727
  • 3
  • 14
  • 39
  • 2
    Are you using the WorkManagerThreadExecutor? I'm assuming the lookup is done on a quartz thread. http://www.quartz-scheduler.org/api/2.2.1/org/quartz/commonj/WorkManagerThreadExecutor.html – Alasdair Jun 17 '16 at 00:54
  • what's the full stack trace? That will tell us if you're actually on a managed thread or not, which I suspect is the case (and is consistent with what the error message indicates). I tested this locally and I was able to lookup a data source successfully from a ServletContextListener without using Quartz, so I highly suspect that Quartz is running your code on a non-managed thread. – Andy Guibert Jun 17 '16 at 01:03
  • @Alasdair, Yes, the lookup is done on a quartz thread. Even after changing my property file, its not working. Do I need to create the work manager in Websphere? – Anand Jun 17 '16 at 07:07
  • 2
    Check this page http://gybas.com/2014/quartz-websphere-spring/ Try to remove *.theadPool.* properties, as quartz should use threads from the workManager. – Gas Jun 17 '16 at 08:21
  • @Gas, Thanks. Also, we need to mention the thread count. – Anand Jun 20 '16 at 04:54

2 Answers2

2

In order to perform JNDI lookups in WebSpehre, your code must be running on a managed thread. In order to have Quartz run on one of WebSphere's managed threads, you must set the following 2 properties in your quartz.properties (as Alasdair mentioned in the comments):

org.quartz.threadExecutor.class=org.quartz.commonj.WorkManagerThreadExecutor
org.quartz.threadExecutor.workManagerName=wm/default

The name for org.quartz.threadExecutor.workManagerName can be the JNDI name of any Work Manager that you have configured in WebSphere. I recommend simply using wm/default because it is in your configuration by default.

Andy Guibert
  • 41,446
  • 8
  • 38
  • 61
  • Thanks for your reply. I got an error ClassNotFoundException for WorkManagerThreadExecutor. Then found out that the class actually sits in the package org.quartz.commonj.* I am getting an error even after changing the property file. Please see the content of my property file in my post (updated). – Anand Jun 17 '16 at 07:04
  • 1
    the JNDI look up should inside the constructor. It will not work if its outside the constructor – Anand Jun 20 '16 at 04:42
  • Thanks for pointing that out. I've updated my answer. Did you get this to work yet? Gas made a good suggestion to remove the .threadPool properties from your quartz.properties file – Andy Guibert Jun 20 '16 at 04:49
  • 1
    Yeah, you need to include the number of threadcount without which it will not work. To make it clear for other users, I posted it as a separate answer. Thanks for your help mate!!! – Anand Jun 20 '16 at 04:52
1

With all the help provided by aguibert and Alasdair and reference from here, I am able to fix the issue.

The Quartz property file is:

org.quartz.threadPool.threadCount=1
org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore
org.quartz.threadExecutor.class=org.quartz.commonj.WorkManagerThreadExecutor
org.quartz.threadExecutor.workManagerName=wm/default

The database connection or JNDI lookup should happen within the empty constructor of the JOB Implemented class. For ex,

public class ContractIdFromPartyServiceJob implements Job {

private DataSource ds;

public ContractIdFromPartyServiceJob() {
    try {
        Logger.info("Gets the data source");
        Context context = new InitialContext();
        ds = (DataSource) context.lookup(ApplicationConstants.RESOURCE_REFERENCE_JDBC);
    } catch (RException e) {
        e.printStackTrace();
    }
}

@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException
{
    EHAMarsDAO marsDao = new EHAMarsDAO();
    Connection con = getConnection();
    try {
      marsDao.callDBMethod(con);
    } finally {
      con.close();
    }
 }

public Connection getConnection() throws RACVException
{
    Connection con = null;

    try {
        con = ds.getConnection();
        con.setAutoCommit(false);
        con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
    } catch (SQLException e) {
        throw new RException(Constants.ERROR_CODE_002, Constants.E012_DB_CONNECTION_ERROR, e);
    }
    return con;
}
}
Community
  • 1
  • 1
Anand
  • 727
  • 3
  • 14
  • 39
  • Note that references to open connections should not be stored (especially when jobs are being run on different threads). DataSource objects are considered thread-safe and can be stored for long periods of time, but Connections are not and should follow the get/use/close pattern. Hanging onto an open connection indefinitely will lead to serious performance/scalability issues. – Andy Guibert Jun 20 '16 at 13:36
  • @aguibert, Thanks for your suggestions. Actually, I am closing the connection in the DAO layer once the DB operation is complete. I have not mentioned the code to close the connection here, but I have handled it. Connection for each thread is closed after the job completion. – Anand Jun 21 '16 at 00:08