Introduction
When building a batch component for a Spring application running on IBM WebSphere I came across a few problems that I found worth sharing. Mostly due to lack of proper and consistent documentation I spent some time researching something that proved to be pretty trivial. Hence this short article to explain how to set up an application utilizing the Spring Scheduling integration on WebSphere.
The Spring Scheduling documentation mentions the Quartz Scheduler and the JDK Timer as the two preferred ways to obtain scheduling in a Spring application. However, both of these approaches will, according to this article, lead to unmanaged threads in WebSphere and should be avoided. Fortunately Spring also has some support for the CommonJ API (specification jointly created by BEA and IBM), which is the only recommended solution for the WebSphere platform.
Note! The Quartz scheduler can be set up to use the CommonJ API as engine. I have added a small section at the bottom of the article explaining this.
The documentation on how to utilize the Spring CommonJ integration on WebSphere was pretty sparse. The rest of this article will try to fill the gaps in this documentation.
Requirements for scheduling
For our application we had two separate needs for scheduling support:
- Run task at regular time-intervals: Utilizing scheduling to regurlarly update a list of connected clients. Since updating this list was a time consuming operation we wanted to cache the result and just update the list every 30 minutes or so (configurable time interval).
- Run task at a given time (HH:mm:ss) and then repeat at regular time-intervals: We have a batch process which need to be run once on a daily basis. We also needed to be able to control exactly when the batch started each day.
Covering the first requirement was easy. The second was also pretty easy, but involved a bit more work mostly due to lack of consistent documentation.
Initial setup – “Wait 20 seconds then run every 30 seconds”
The following examples show how to setup a component running on a regular basis after the server starts up. The CommonJ API only requires your class to implement java.lang.Runnable. So for this example we create a simple class SampleRunnable, adding a counter to show it’s a single instance running every time:
package no.bekk.boss;
/**
* Simple example runnable.
*/
public class SampleRunnable implements Runnable {
private int count = 0;
public void run() {
count++;
System.out.println("SampleRunnable running. Run number: " + count);
}
}
We also need to setup a reference to the TimerManager in web.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4"
id="web-app_1">
<display-name>Spring Scheduling Example</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:ApplicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<resource-ref id="ResourceRef_1">
<res-ref-name>TimerManagerRef</res-ref-name>
<res-type>commonj.timers.TimerManager</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Unshareable</res-sharing-scope>
</resource-ref>
</web-app>
And corresponding ibm-web-bnd.xmi (using default resource here – can be configured in the WebSphere Admin Console):
<webappbnd:WebAppBinding xmi:version="2.0"
xmlns:xmi="http://www.omg.org/XMI"
xmlns:webappbnd="webappbnd.xmi"
xmlns:webapplication="webapplication.xmi"
xmlns:commonbnd="commonbnd.xmi"
xmlns:common="common.xmi"
xmi:id="WebApp_ID_Bnd"
virtualHostName="default_host"
>
<webapp href="WEB-INF/web.xml#web-app_1"/>
<resRefBindings xmi:id="ResourceRefBinding_1" jndiName="tm/default">
<bindingResourceRef href="WEB-INF/web.xml#ResourceRef_1"/>
</resRefBindings>
</webappbnd:WebAppBinding>
We can now configure our SampleRunnable directly in the Spring configuration. Notice how we’re simply using the built-in classes provided by Spring to wire it all up:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="sampleRunnable" class="no.bekk.boss.SampleRunnable"/>
<bean id="runnableListener" class="org.springframework.scheduling.commonj.ScheduledTimerListener">
<!-- Wait 20 seconds before starting first execution -->
<property name="delay" value="20000"/>
<!-- Run every 30 seconds -->
<property name="period" value="30000"/>
<property name="runnable" ref="sampleRunnable"/>
</bean>
<bean id="timerFactory" class="org.springframework.scheduling.commonj.TimerManagerFactoryBean">
<property name="resourceRef" value="true"/>
<property name="timerManagerName" value="TimerManagerRef"/>
<property name="scheduledTimerListeners">
<list>
<ref bean="runnableListener"/>
</list>
</property>
</bean>
</beans>
Ok. That should be about it. Pack it all up in a WAR and deploy to WebSphere. After 20 seconds you should see the first print out in your SystemOut.log file, and thereafter every 30 seconds. With the counter increasing. Something similar to this:
[10.01.08 08:55:49:765 CET] 00000064 SystemOut O SampleRunnable running. Run number: 1 [10.01.08 08:56:19:530 CET] 0000003c SystemOut O SampleRunnable running. Run number: 2 [10.01.08 08:56:49:827 CET] 00000039 SystemOut O SampleRunnable running. Run number: 3
Next step – “Run every day at 19:00″
The next thing we want to do is set up our Batch process, running at a specific time every day. The CommonJ API should support this with the schedule(…) methods. However, the Spring integration does not seem to have any equivalent implementation.
The support for the missing functions is quickly added extending the Spring classes. First we need a configurable listener that can convert the specified time-string to a Date object:
package no.bekk.boss.commonj;
import org.springframework.scheduling.commonj.ScheduledTimerListener;
import java.util.Calendar;
import java.util.Date;
/**
* Special timer listener with support for date scheduling.
*/
public class ScheduledDateTimerListener extends ScheduledTimerListener {
private Date date;
/**
* Get the date object wrapped by this listener.
*
* @return The date object.
*/
public Date getDate() {
return date;
}
/**
* Set the wrapped date object, given a string representing a time of day.
* If the specified time has passed in todays date, it will wrap to tomorrow.
* <p/>
* Examples:
* <ul>
* <li>"14" - 2 pm.</li>
* <li>"14:15" - 15 mins past 2 pm.</li>
* <li>"14:15:30" - 15 mins 30 secs past 2 pm.</li>
* </ul>
*
* @param timeStr The time string, with elements separated by ':'.
*/
public void setTimeStr(String timeStr) {
Calendar cal = Calendar.getInstance();
String[] times = timeStr.split(":");
int hour = Integer.parseInt(times[0]);
int minute = times.length > 1 ? Integer.parseInt(times[1]) : 0;
int second = times.length > 2 ? Integer.parseInt(times[2]) : 0;
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, minute);
cal.set(Calendar.SECOND, second);
if (cal.before(Calendar.getInstance())) {
cal.add(Calendar.DATE, 1);
}
this.date = cal.getTime();
}
}
Secondly we extend the Factory class to support this new type of listener by calling the appropriate methods on the CommonJ TimerManager. We call the super() methods to insure that existing functionality in the Factory remains intact:
package no.bekk.boss.commonj;
import commonj.timers.TimerManager;
import org.springframework.scheduling.commonj.TimerManagerFactoryBean;
import javax.naming.NamingException;
/**
* Extension of the default TimerManagerFactoryBean, to enable support for date
* configured schedules.
*/
public class DateTimerManagerFactoryBean extends TimerManagerFactoryBean {
private ScheduledDateTimerListener[] scheduledDateTimerListeners;
private String timerManagerName;
private TimerManager timerManager;
/**
* Set the array of date time listeners.
*
* @param scheduledDateTimerListeners The array of date time listeners.
*/
public void setScheduledDateTimerListeners(ScheduledDateTimerListener[] scheduledDateTimerListeners) {
this.scheduledDateTimerListeners = scheduledDateTimerListeners;
}
public void setTimerManagerName(String timerManagerName) {
super.setTimerManagerName(timerManagerName);
this.timerManagerName = timerManagerName;
}
public void afterPropertiesSet() throws NamingException {
super.afterPropertiesSet();
if (timerManager == null) {
timerManager = (TimerManager) lookup(this.timerManagerName, TimerManager.class);
}
if (scheduledDateTimerListeners != null) {
for (ScheduledDateTimerListener scheduledTask : scheduledDateTimerListeners) {
if (scheduledTask.getPeriod() > 0) {
if (scheduledTask.isFixedRate()) {
timerManager.scheduleAtFixedRate(
scheduledTask.getTimerListener(),
scheduledTask.getDate(),
scheduledTask.getPeriod());
} else {
timerManager.schedule(
scheduledTask.getTimerListener(),
scheduledTask.getDate(),
scheduledTask.getPeriod());
}
} else {
timerManager.schedule(
scheduledTask.getTimerListener(),
scheduledTask.getDate());
}
}
}
}
}
With this in place we create a new Runnable representing the Batch job. For the example we could of course have used the one we already created. So this is just to make a point of the difference between the two:
package no.bekk.boss;
/**
* Simple example batch runnable.
*/
public class SampleBatchRunnable implements Runnable {
public void run() {
System.out.println("SampleBatchRunnable running...");
}
}
We can then update the Spring configuration with the new Batch Runnable and wire up with the new factory and listener. Notice how we’ve changed the class name for the factory, having the new factory class support both the new and the built-in listeners:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="sampleRunnable" class="no.bekk.boss.SampleRunnable"/>
<bean id="sampleBatchRunnable" class="no.bekk.boss.SampleBatchRunnable"/>
<bean id="runnableListener" class="org.springframework.scheduling.commonj.ScheduledTimerListener">
<!-- Wait 20 seconds before starting first execution -->
<property name="delay" value="20000"/>
<!-- Run every 30 seconds -->
<property name="period" value="30000"/>
<property name="runnable" ref="sampleRunnable"/>
</bean>
<bean id="batchListener" class="no.bekk.boss.commonj.ScheduledDateTimerListener">
<!-- Run at next 19:00 -->
<property name="timeStr" value="19:00"/>
<!-- Run daily 86400000 -->
<property name="period" value="86400000"/>
<property name="fixedRate" value="true"/>
<property name="runnable" ref="sampleBatchRunnable"/>
</bean>
<bean id="timerFactory" class="no.bekk.boss.commonj.DateTimerManagerFactoryBean">
<property name="resourceRef" value="true"/>
<property name="timerManagerName" value="TimerManagerRef"/>
<property name="scheduledTimerListeners">
<list>
<ref bean="runnableListener"/>
</list>
</property>
<property name="scheduledDateTimerListeners">
<list>
<ref bean="batchListener"/>
</list>
</property>
</bean>
</beans>
Pack everything up and redeploy. You will now have two scheduled components running:
- sampleRunnable – running after 20 seconds, and then every 30 seconds.
- sampleBatchRunnable – running at 19:00 every day.
Other aspects and tips
Working directly on the TimerManager
The new listener and factory classes above can be created in a myriad of ways. By extending the Spring class we don’t need more than one entry in the Spring config file. This does however mean some code is run twice (like the lookup of the TimerManager). Since the TimerManagerFactoryBean is a proxy for the TimerManager we could have written a separate class just to add the extra listener. The code given above is just one way to achieve what we wanted.
To start things off manually we can obtain the reference to the TimerManager and call methods directly on this. For instance we use the following code to manually trigger the batch, which is very useful for testing purposes. This is just an extract of some JSP-code we created as a quick-and-dirty way to manually start the batch job. A cleaner way would of course be to have the references dependecy injected is a separate component:
TimerManager timerManager = (TimerManager) ctx.getBean("timerFactory");
ScheduledTimerListener task = (ScheduledTimerListener) ctx.getBean("batchListener");
timerManager.schedule(task.getTimerListener(), 1000L);
Warnings in WebSphere
Our batch job takes quite a long time to complete. It performs some work, then waits for a given amount of time to check for a reply. This leads us to getting warnings like the following in the WebSphere log:
[07.01.08 17:12:24:045 CET] 0000000f ThreadMonitor W WSVR0605W: Thread “DefaultTimerManager.Alarm Pool : 0″ (0000002b) has been active for 743348 milliseconds and may be hung. There is/are 1 thread(s) in total in the server that may be hung.
We’ve chosen to ignore these warnings as they seem to be just warnings. Have not investigated how to avoid getting these warnings printed out.
Waiting for reply
As mentioned in the above section we have our batch waiting for replies. We’ve used Thread.sleep(timeout) for this. This does not seem to pose any problems, except for the warning shown above. Here’s some example code showing how we implemented this, having the batch check every timeout amount of time until maxTimeout or a reply has been obtained:
while (replyFileName == null) {
try {
if (totalTimeout < maxTimeout) {
totalTimeout += timeout;
Thread.sleep(timeout);
} else {
throw new BatchTimeoutException(
"Batch stopping due to Max timeout: " + totalTimeout / 1000 + "s (max: " + maxTimeout / 1000 + "s)");
}
}
catch (InterruptedException e) {
LOG.error("Could not wait for reply", e);
}
finally {
replyFileName = checkBatchReply(batch);
}
}
Quartz with CommonJ
The Quartz scheduler can actually be used in WebSphere. The key point here is to set it up to use the CommonJ WorkManager API.
A small snippet of Spring XML to show how to initialize the Quartz SchedulerFactoryBean with a WorkManagerTaskExecutor from WebSphere:
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="synchronizerTrigger" />
</list>
</property>
<property name="taskExecutor" ref="taskExecutor"></property>
</bean>
<bean id="taskExecutor" class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
<property name="workManagerName" value="wm/default"/>
<property name="resourceRef" value="false"/>
</bean>
Conclusion
Scheduling is easy to setup and use in WebSphere utilizing Spring and the CommonJ API. Make sure you use this approach over standard Quartz or JDK Timer to avoid unmanaged threads in WebSphere. If you want to use Quartz, make sure you configure it to use the CommonJ WorkManager in WebSphere.
About the author
Eivind B. Waaler has been developing J2EE applications on WebSphere and other application servers for many years. He has experience working with and contributing to a wide range of open source projects. Eivind works as a manager and technical group leader in Bekk Consulting, Oslo – Norway.
References
Comments
Anonymous says:In this context, it’s interesting to know that you *can* also use Quartz with WebSphere in a supported manner through Spring: you can configure Quartz in Spring to use a CommonJ WorkManager (not a TimerManager), also simply obtained through JNDI, as its thread pool. By default Quartz uses its own simple thread pool implementation, which will indeed cause unmanaged threads to be spawned and is therefore not supported on WAS, but by configuring it to obtain its threads from a WorkManager this is easily fixed. I’ve asked IBM to update the article you’re refering to accordingly a while ago, but they haven’t changed it. Joris Kuipers Feb 14, 2008 13:33 |
Eivind Waaler says:Thanks Joris. Very useful information, seeing that the CommonJ API is quite limited compared to Quartz. I will update the article to reflect this. Feb 15, 2008 09:19 |
Anonymous says:Is this Spring scheduling approach in WebSphere “cluster aware”? We’ve decided to use the WebSphere scheduling capability directly because it is cluster aware… Feb 19, 2008 17:40 |
Anonymous says:Hi Elvind, Thanks for the nice article. At the same time, I am not sure why we need commonj API or quartz for this. IBM has its own implementation of commonj kind of stuff – called Work Manager. Please look at the article:- http://www.javaworld.com/javaworld/jw-01-2007/jw-01-workmgr.html You can use this directly, without much configuration… Best regards, Ravion Feb 23, 2008 23:18 |
Anonymous says:The commonj API is a proposed standard that is implemented by both WebSphere and Weblogic (and it would be nice if others did as well). The WorkManager you are referring to is a similar beast that is WebSphere specific and most likely predates the commonj proposal. I would suspect that IBM’s commonj implementation most likely uses this code base. I would use the commonj API for any new code. It also has the benefit that it may be adopted by other JEE servers, thus making your code more portable. Mar 06, 2008 20:36 |
VK says:Great article. Based on your code samples, I was able to get the TimerManager and WorkManager with Spring running under Websphere 6.1. However in my batch runnable job, I need to access both TimerManager and WorkManager as the job needs to cancel or resume the timer when a piece of work is completed. I am able to access WorkManager in the job by injecting it into the bean as follows inthe spring config file: <bean id=“testRunnable”class=“org.frb.ny.mg.nacs.scheduler.jobs.SampleRunnable” > <property name=“taskManager” ref=“taskExecutor” /></bean> However I am not able to do the same for the timerManager when I add the following in the config file: <bean id=“testRunnable” class=“org.frb.ny.mg.nacs.scheduler.jobs.SampleRunnable” > <property name=“taskManager” ref=“taskExecutor” /><property name=“dateTimerManager” ref=“timerFactory” /></bean> I get the following error: Error 500: javax.faces.FacesException: javax.faces.el.EvaluationException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘testRunnable’ defined in ServletContext resource [/WEB-INF/resources/scheduler-context.xml]: Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type [com.ibm.ws.asynchbeans.timer.TimerManagerImpl] to required type [org.frb.ny.mg.nacs.scheduler.timer.DateTimerManagerFactoryBean] for property ‘dateTimerManager’; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [com.ibm.ws.asynchbeans.timer.TimerManagerImpl] to required type [org.frb.ny.mg.nacs.scheduler.timer.DateTimerManagerFactoryBean] for property ‘dateTimerManager’: no matching editors or conversion strategy found. Not sure why the asyncbeans package from ibm is invoked, maybe the commonJ api’s call it internally. Alternately, if there is a way to retrieve the application context in workerManager or the runnable job, it may be possible to retrieve the timerManager bean and then invoke – cancel the timer – in the job. Any help /suggestions immediately would be greatly appreciated. Apr 02, 2008 19:19 |
Eivind Waaler says:VK: Sounds to me like you’re using wrong type on your dateTimerManager property in the SampleRunnable class. Think it should be of type commonj.timers.TimerManager, which is implemented by the com.ibm.ws.asynchbeans.timer.TimerManagerImpl referenced in your error.. Apr 04, 2008 09:52 |
Anonymous says:nice article about using open source technologies to build a batch system. I think there are two important points to make here: first, that java as a language for batch processing has value in terms of agility and development options; second, that assembling a batch environment via various open source components is worthwhile for specific problem sets, but is the effort of building your own solution worth it if you are an enterprise? For enterprise customers, defined as those who demand higher qualities of service and integration with existing batch assets like enterprise schedulers, auditing/archiving systems, workload management, z/OS support, and so on, pre-packaged solutions may be more appealing. I suggest you take a look at Websphere XD Compute Grid, you can find an overview article at: http://www.ibm.com/developerworks/websphere/techjournal/0804_antani/0804_antani.html One important design decision was to hide the complexities of thread management from the developer. So in this forum for example, there are questions about commonj versus workmanager. My question is, why should the average developer care? Sure, it’s “cool” to solve more advanced problems with your own mult-threading solutions (i’m working on that myself right now, it’s a nice challenge), but this effort perhaps isn’t in the best interest of the business. When building Compute Grid applications, the developer writes code that is threadsafe and runs on 1 thread. They can then define parallelization properties and have the infrastructure manage starting multiple instances of the batch job, where each job processes a different partition of the data. You can read more about that in the HPC section of the article. There are also links at the bottom that go into more details about developing CG applications and the tooling that’s available. Thanks, Snehal Apr 04, 2008 11:10 |
Eivind Waaler says:Snehal: I don’t really see the relevance of your IBM batch product in this context. My point was to show how we implemented a simple batch-like functionality using the CommonJ API. Using a separate batch product would be way more than we need, and is NOT something I would recommend to anyone looking for a simple scheduling mechanism to run in WebSphere. For more batch functionality I’d recommend looking at Spring Batch. I’m sure it can be used for enterprise development (as my example actually also is), and will feel familiar for those used to Spring (which is practically everyone these days Apr 09, 2008 13:28 |
Anonymous says:Hi Elvind, Time and again I’m meeting with developers and customers that have been struggling to build their own “maverick batch” systems, dealing with issues around writing their own batch container essentially. I think the article is a nice way to point out that this isn’t necessarily quite so simple. Also, for Spring Batch, it’s a very interesting batch programming model technology, and compliments Compute Grid. There are many synergies among the two technologies, where Spring Batch delivers a programming model but no underlying runtime infrastructure for cross-cluster parallel processing, operational control, external scheduling integration, so on and so forth; whereas Compute Grid delivers both a programming model and more importantly the underlying runtime infrastructure. Nothing precludes you from leveraging both technologies, in fact, we encourage customers and developers to build their applications using whatever underlying programming model they feel most comfortable with, but in terms of operational/infrastructure management and integrations expected by the operations teams, to view Compute Grid as the infrastructure in which your batch apps will run. One of our most aggressive Compute Grid customers for example uses the Batch Data Stream Framework (an article coming out on that soon) as the mechanism that connects their Spring-based batch applications (note, *not* Spring-batch based, but instead using regular Spring as the assembly mechanism) to their batch processing infrastructure. I think this is a very nice pattern, look for an article on this too in the near-future. Thanks and good luck… Snehal Apr 12, 2008 02:30 |
Anonymous says:Hi Eivind, First of all Nice article! However we found a little problem with the DateTimerManagerFactoryBean in your example. In your example, you override the afterPropertiesSet method, which do not keep track any of the timers scheduled. Jason Apr 30, 2008 08:55 |
Anonymous says:Hi, Let me thank you for the excellent article. We are facing one issue with this configuration on Websphere cluster. 1. Our standalone processes are file based as well as database and currently the files are local and we are planning to make it as shared filed system across nodes of cluster. Query 1. Would this process be running on both nodes? If it is yes then it would create bottleneck on the resource because this will be processing the same rows / files. Typically batch processes will not have any redundant system or active / active configuration but implementing batch processes using commonJ leads to create bottle neck on the resources since it is running on cluster. We cannot switch off timer manager because the startup of server is looking for resource reference to create jndi reference. We do not have option of having different configurations at node level in a cluster also. Please do the needful.. Regards Ramesh Jun 11, 2008 21:07 |
Anonymous says:Hi Eivind, Thanks for the cool article. I have implemented the scheduling the job successfully.But have one issue, I cannot stop the thread even after undeployment. Could you please suggest How do I have control over the scheduled thread? Thanks Rod Jul 11, 2008 17:42 |
Anonymous says:In this context, it’s interesting to know that you *can* also use Quartz with WebSphere in a supported manner through Spring: you can configure Quartz in Spring to use a CommonJ WorkManager (not a TimerManager), also simply obtained through JNDI, as its thread pool. By default Quartz uses its own simple thread pool implementation, which will indeed cause unmanaged threads to be spawned and is therefore not supported on WAS, but by configuring it to obtain its threads from a WorkManager this is easily fixed. I’ve asked IBM to update the article you’re refering to accordingly a while ago, but they haven’t changed it. Joris Kuipers Thanks Jul 15, 2008 13:43 |
Anonymous says:I downloaded the source and tried to build it with maven. But the build failed with compilation errors because some imports, the classes from the commonj packages, were not be found. I uncommented the <dependancy> element for commonj in pom.xml but the commonj jar was still not found. The message was: Missing: I have looked and could not find the commonj jars at the maven repo at: http://repo1.maven.org/maven2/\\ I’d appreciate very much if someone could point me to the commonj pom.xml thanks, Jul 16, 2008 18:15 |
Anonymous says:For those using maven, I found the commonj pom at the maven repo under ‘bea’. The URL is: http://repo1.maven.org/maven2/com/bea/wlplatform/commonj-twm/1.1/\\ Jul 17, 2008 17:49 |
Anonymous says:I don’t have access to java:comp jndi namespace through the use of spring/quartz/commonj integration. When I try to resolve java:comp/UserTransaction i get an error from websphere (6.1.0.13) : NMSV0310E: 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. … Have any of you really gained access to the j2ee context from within a spring/quartz/commonj job??? Sep 08, 2008 12:27 |
Faizal Abdoelrahman says:If you go with the commonj/quartz spring integration you will have managed worker threads, but without j2ee context which is swallowed during contruction of org.springframework.scheduling.quartz.SchedulerFactoryBean . This happens since it creates an unmanaged thread during construction of QuartzSchedulerThread inside quartz core package. Hopefully the spring / quartz guys can comeup with a better integration. Cheers. Sep 12, 2008 10:10 |
Eivind Waaler says:Thank you all for good informative comments and suggestions. I forgot to inform about the commonj dependency in the maven2 project. As far as I can remember I installed the dependency manually. The jar-file should be found in your WebSphere installation. Do something like this with the name of the commonj jar file at the end: mvn install:install-file -DgroupId=com.ibm.websphere -DartifactId=commonj-twm -Dversion=6.0 -Dpackaging=jar -Dfile=<your-jar-file-here> Or use the bea commonj module or similar instead. It’s only needed for compiling, so any jar containing the commonj api would work I guess.. Sep 26, 2008 14:25 |
Anonymous says:Eivind, I found this ariticle very helpful. could you please expand on the following as I would like to implement a manual trigger, perhaps providing a code sample. I achieved this with a servlet but I would especially like to know you can implement “references dependecy injected is a separate component:” Thank you. To start things off manually we can obtain the reference to the TimerManager and call methods directly on this. For instance we use the following code to manually trigger the batch, which is very useful for testing purposes. This is just an extract of some JSP-code we created as a quick-and-dirty way to manually start the batch job. A cleaner way would of course be to have the references dependecy injected is a separate component:TimerManager timerManager = (TimerManager) ctx.getBean(“timerFactory”); Dec 01, 2008 21:25 |