Unmanaged Thread Problem - WebSphere Compliant Spring Task Schedulers - (JNDI Context problems)

Problems Summary
  1. Spring creates a separate threads to handle schedulers , but most of J2EE container vendors like websphere , weblogic ..etc doesn't support the threads created outside of containers. (These are threads called unmanaged threads)
  2. Following exception was thrown when we try to lookup the objects directly using javax.naming.initialContext . (We have JAX-RPC webservices client code that is generated by RAD using WSDL , which is auto generated code has the direct initial context lookups)

Exception Occured
  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 "com/env/url/testrouce"

Explanation and solution

IBM clearly states that don't create unmanaged threads (this is true with most of J2EE container vendors), for the following reasons:
  1. The application server does not recognize unmanaged threads.
  2. Unmanaged threads do not have access to Java EE contextual information.
  3. Unmanaged threads can use resources without being monitored by the application server.
  4. Unmanaged threads can adversely affect application server functions such as shutting down gracefully or recovering resources from failure.
  5. An administrator cannot control the number of unmanaged threads or their use of resources.



Spring Framework Support
Spring framework does provide a package with scheduling classes based on CommonJ WorkManager/TimerManager that supported by IBM Websphere 6.0+ and BEA weblogic 9.0+.  Spring Commonj classes link is provided in references section.


Steps to create websphere compliant task scheduler
  1. Create a task (That implements the runnable interface)
  2. Add the Websphere timer Manager reference in the web.xml and ibm-web-bnd.xmi
  3. Configure task at specific intervals in the SpringContext.xml


Step1: Create task
public class TestTask implements Runnable {
public void run() {
//your work to be executed        
}
}

Step 2: add websphere timer manager references in project

In Web.xml
<resource-ref>
<description>TimerManager</description >
<res-ref-name>timerManager</res-ref-name >
<res-type>commonj.timers.TimerManager</res-type >
<res-auth>Container</res-auth >
<res-sharing-scope>Unshareable</res-sharing-scope >
</resource-ref>

in ibm-web-bnd.xml
<resource-ref name="timerManager" binding-name="tm/default" ></resource-ref>

Step 3: configure scheduler

<bean id="testTask" class="com.sivavaka.test.spring.scheduler.TestTask">
</bean>        

<bean id= "timerScheduler" class="org.springframework.scheduling.commonj.TimerManagerTaskScheduler" >
      <property name="resourceRef" value="true"/>
      <property name="timerManagerName" value="timerManager"/>
</bean>

<!-- To schedule job for every one hour then cron will be "0 0 0/1 * * ?" -->
<!-- Websphere Compliant Thread :: Runs for every 5 mins -->         
<task:scheduled-tasks scheduler="timerScheduler" >
      <task:scheduled ref="testTask" method="run" cron="0 0/5 * * * ?" />
</task:scheduled-tasks >


If you create the scheduler like above , you can see the WebSphere Runtime available to thread that executes this task , basically it is WebSphere managed thread
  

But if you use the Spring's task scheduler directly (I mean without using the websphere timemanager), thread that executes this task doesn't have access the Websphere Runtime causing the J2EE container services are not available.




References
  1. http://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=/com.ibm.websphere.nd.multiplatform.doc/info/ae/ae/cspr_design.html
  2. Workmanagers (http://pic.dhe.ibm.com/infocenter/wasinfo/v8r0/index.jsp?topic=/com.ibm.websphere.nd.multiplatform.doc/info/ae/asyncbns/concepts/casb_workmgr.html)
  3. http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/scheduling/commonj/package-summary.html


Download/Browse the Sample Code

  1. To download sample code war file  (https://sourceforge.net/projects/blog-sivavaka-com-code-samples/files/WebSphere%20Compliant%20Spring%20Task%20Scheduler/)
  2. To browser the sample code (https://code.google.com/p/blog-sivavaka-com-code-samples/source/browse/#svn%2Ftrunk%2FSivaTestSpringScheduler)

11 comments:

  1. This is a great post.
    I am having the same problem but not in a WEB application but rather in a back end application deployed as a EAR on Webshere. Any suggestion for how to configure TimerManagerTaskSchedule. It complains that

    ASYN0069E: TimerManager tm/default is being looked up without using resource reference

    Which is true as I do not have the resource-ref web.xml equivalent set anywhere.

    Thank you in advance,
    Julian

    ReplyDelete
    Replies
    1. XML code is not displayed in previous reply.

      1. You can point the timer manager JNDI name directly without using resource reference like below
      <bean id= "timerScheduler" class="org.springframework.scheduling.commonj.TimerManagerTaskScheduler" >
      <property name="resourceRef" value="false"/>
      <property name="timerManagerName" value="java:comp/env/tm/default"/>
      </bean>
      2. if it is EJB applicaiton , you can include the resource ref in ejb-jar.xml file

      Delete
    2. When trying option 1 I got this
      javax.naming.NameNotFoundException: Name comp/env/tm not found in context "java:".

      as for option 2 I tried to add the resource ref in application.xml like below:


      Webshere DefaultTimerManager
      timerManager
      commonj.timers.TimerManager
      Container
      Unshareable


      and my ibm-application-bnd.xml entry is like below:


      Any attempts to reference timerManager in the spring file failed>

      I am out of any ideas. Could you please spot what I am doing wrong.

      Thanks

      Delete
    3. I cant see the xmls properly in your comments , do the html encode and paste in the comments

      Delete
    4. Thanks Siva.
      This is from my application.xml:

      <resource-ref>
      <description>Webshere DefaultTimerManager</description>
      <res-ref-name>timerManager</res-ref-name>
      <res-type>commonj.timers.TimerManager</res-type>
      <res-auth>Container</res-auth>
      <res-sharing-scope>Unshareable</res-sharing-scope>
      </resource-ref>

      And this is from my ibm-application-bnd.xml

      <resource-ref name="tm/default" binding-name="timerManager"/>

      Delete
    5. application.xml
      <resource-ref>
      <description>Webshere DefaultTimerManager</description>
      <res-ref-name>timerManager</res-ref-name>
      <res-type>commonj.timers.TimerManager</res-type>
      <res-auth>Container</res-auth>
      <res-sharing-scope>Unshareable</res-sharing-scope>
      </resource-ref>

      ibm-application-bnd.xml
      <resource-ref name="tm/default" binding-name="timerManager"/>

      Delete
  2. Sorry for the late response, When you mentioned that you have deployed the back end application as EAR file . You have two options

    1. You can point the timer manager JNDI name directly without using resource reference like below






    2. if it is EJB applicaiton , you can include the resource ref in ejb-jar.xml file

    ReplyDelete
  3. Using "java:comp/env/tm/default" without resource reference did not work for me either (it raised a JNDI exception), but with the resource reference in web.xml it works perfectly. Thank you very much for this helpful article which saved me a lot of time!

    ReplyDelete
  4. Thanks A LOT!!! Saved my day...

    Simon

    ReplyDelete
  5. Thanks this worked like a charm with Spring 4 and WebSphere 7.5. To work around Hibernate JNDI errors, my job method class had to extend org.springframework.context.ApplicationContextAware and inject a org.springframework.context.ApplicationContext property. Instead of getting the session factory from the initial context as I could everywhere else in my application, I had to look it up with a call similar to SessionFactory sf = (SessionFactory)context.getBean("myHibernateSessionFactoryBean");

    ReplyDelete
  6. Any ideas on how to accomplish your solution using only JavaConfig/Annotations instead of XML?

    ReplyDelete