Problems
Summary
- 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)
- 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:
- The application server does not recognize unmanaged threads.
- Unmanaged threads do not have access to Java EE contextual information.
- Unmanaged threads can use resources without being monitored by the application server.
- Unmanaged threads can adversely affect application server functions such as shutting down gracefully or recovering resources from failure.
- 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
- Create a task (That implements the runnable interface)
- Add the Websphere timer Manager reference in the web.xml and ibm-web-bnd.xmi
- 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
- http://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=/com.ibm.websphere.nd.multiplatform.doc/info/ae/ae/cspr_design.html
- 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)
- http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/scheduling/commonj/package-summary.html
Download/Browse
the Sample Code
- To download sample code war file (https://sourceforge.net/projects/blog-sivavaka-com-code-samples/files/WebSphere%20Compliant%20Spring%20Task%20Scheduler/)
- To browser the sample code (https://code.google.com/p/blog-sivavaka-com-code-samples/source/browse/#svn%2Ftrunk%2FSivaTestSpringScheduler)
This is a great post.
ReplyDeleteI 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
XML code is not displayed in previous reply.
Delete1. 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
When trying option 1 I got this
Deletejavax.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
I cant see the xmls properly in your comments , do the html encode and paste in the comments
DeleteThanks Siva.
DeleteThis 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"/>
application.xml
Delete<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"/>
Sorry for the late response, When you mentioned that you have deployed the back end application as EAR file . You have two options
ReplyDelete1. 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
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!
ReplyDeleteThanks A LOT!!! Saved my day...
ReplyDeleteSimon
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");
ReplyDeleteAny ideas on how to accomplish your solution using only JavaConfig/Annotations instead of XML?
ReplyDelete