Help

Controls

PermLinkWikiLink

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

Forum: Seam Users Forum ListTopic List
30. Jul 2008, 03:52 CET | Link

I've been using the @Asynchrounous/@Expiration/@IntervalDuration annotations to schedule repeatable Quartz jobs, and that has worked pretty well. However, the jobs/triggers have auto-generated names like -6a5e40e8:11b717f63c3:-7f7d and that is not very readable.

I need to reschedule jobs if some parameters are changed in my app, hence the need to now a job's associated trigger name. QuartzTriggerHandle has a 'triggerName' field, but it is private, and QuartzTriggerHandle offers only cancel/pause/resume methods. I guess a solution would be to make QuartzTriggerHandle's triggerName publicly accessible (as already requested in JBSEAM-2695), but that would still give random job names. I would prefer to be able to specify the job/trigger names. To that end being able to add a @JobName/@TriggerName annotation to @Asynchronous methods would be nice.

In case it helps someone with a similar problem, I currently use the following code as a workaround. It schedules jobs, executed in a Seam context, with the specified job/trigger name (mimics what QuartzDispatcher.java does sans the auto-generated names).


QuartzDispatcher dispatcher = QuartzDispatcher.instance();
Scheduler scheduler = dispatcher.getScheduler();

Method method = clazz.getMethod(methodName, (Class[]) null);
Asynchronous async = new AsynchronousInvocation(method, componentName, null);
JobDetail job = new JobDetail(jobName, JOB_GROUP, QuartzDispatcher.QuartzJob.class);
job.setRequestsRecovery(true);
job.getJobDataMap().put("async", async);
SimpleTrigger trigger = new SimpleTrigger(triggerName, TRIGGER_GROUP, when, null,
                                          SimpleTrigger.REPEAT_INDEFINITELY, interval);
scheduler.scheduleJob(job, trigger);

BTW, using a job/trigger group other than DEFAULT (say 'seam') or being able to specify one would also be nice.

5 Replies:
30. Jul 2008, 11:27 CET | Link

I felt like Seam did not quite cover cron-type jobs, so I just used a completely separate Quartz scheduler for those jobs. This requires some stanza to hook the job-running thread into Seam, but is quite easy. If you'd like I can paste some code here.

30. Jul 2008, 13:38 CET | Link
Alexander T wrote on Jul 30, 2008 11:27:
I felt like Seam did not quite cover cron-type jobs, so I just used a completely separate Quartz scheduler for those jobs. This requires some stanza to hook the job-running thread into Seam, but is quite easy. If you'd like I can paste some code here.

I have a working solution (for now), but I'd like to see how you hooked the job-running thread into Seam. So, yes, please post some code.

30. Jul 2008, 14:50 CET | Link

Ok! I created my scheduler in an MBean to get a cluster singleton, and I'm using EL expressions saved in the jobs to express what is to be done. Here is the worker class. It is slightly incomplete, but you'll get the idea

public class TaskWorker implements Job {

	private Log log;

	/**
	 * @see RootInterceptor#invoke() 
	 */
	public void execute(JobExecutionContext jec) throws JobExecutionException {
		// More about this in the org.jboss.seam.Seam sourcecode
		// and http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4126662#4126662
		boolean createContexts = !Contexts.isEventContextActive() && !Contexts.isApplicationContextActive();
		if (createContexts) {
			Lifecycle.beginCall();
		}
		try {

			log = Logging.getLog(getClass());

			JobDetail jobDetail = jec.getJobDetail();
			CronTrigger cronTrigger = (CronTrigger) jec.getTrigger();

			String methodBindingExpression = (String) jobDetail.getJobDataMap().get(
					TaskDataTypes.METHOD_BINDING_EXPRESSION.name());

			// Taken from org.jboss.seam.Event 

			MethodExpression<Object> invocationTarget = Expressions.instance().createMethodExpression(
					methodBindingExpression);

			log.debug("Executing job '#0', calling '#1', scheduled at '#2'", jobDetail.getName(),
					invocationTarget.getExpressionString(), cronTrigger.getCronExpression());

			invocationTarget.invoke();
		} finally {
			if (createContexts) {
				Lifecycle.endCall();
			}
		}
	}
}
30. Jul 2008, 11:33 CET | Link

File a feature request in JIRA with a patch.

 

Read about how to report a bug.

30. Jul 2008, 15:46 CET | Link

I had the same problem as well.

According to the seam doco you can persist the QuartzTrigerHandler to the datbase. But i didn't like that either.

So i thought of some wrappers ... in the end i just used reflections to get the private field ... and i save that to the DB .... and then call the schedulers automatically.

here's the code i used to get the private field


public static Object getPrivateField(Object o, String fieldName) {
		// Check we have valid arguments...
		assert o != null;
		assert fieldName != null;

		// Go and find the private field...
		final Field fields[] = o.getClass().getDeclaredFields();
		for (int i = 0; i < fields.length; ++i) {
			if (fieldName.equals(fields[i].getName())) {
				try {
					fields[i].setAccessible(true);
					return fields[i].get(o);
				} catch (IllegalAccessException ex) {
					return null;
				}
			}
		}
		return null;
	}