You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by an...@apache.org on 2013/02/06 12:23:14 UTC
svn commit: r1442913 - in
/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/quartz:
JobSpec.java QuartzResourceAdapter.java
Author: andygumbrecht
Date: Wed Feb 6 11:23:13 2013
New Revision: 1442913
URL: http://svn.apache.org/viewvc?rev=1442913&view=rev
Log:
Fix https://issues.apache.org/jira/browse/OPENEJB-1996
Fix https://issues.apache.org/jira/browse/OPENEJB-1995
Modified:
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/quartz/JobSpec.java
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/quartz/QuartzResourceAdapter.java
Modified: tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/quartz/JobSpec.java
URL: http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/quartz/JobSpec.java?rev=1442913&r1=1442912&r2=1442913&view=diff
==============================================================================
--- tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/quartz/JobSpec.java (original)
+++ tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/quartz/JobSpec.java Wed Feb 6 11:23:13 2013
@@ -38,7 +38,7 @@ import java.util.TimeZone;
/**
* @version $Rev$ $Date$
-*/
+ */
public final class JobSpec implements ActivationSpec {
private MessageEndpoint endpoint;
@@ -67,7 +67,7 @@ public final class JobSpec implements Ac
return triggerName;
}
- public void setTriggerName(String s) {
+ public void setTriggerName(final String s) {
triggerName = s;
}
@@ -75,7 +75,7 @@ public final class JobSpec implements Ac
return triggerGroup;
}
- public void setTriggerGroup(String s) {
+ public void setTriggerGroup(final String s) {
triggerGroup = s;
}
@@ -85,13 +85,13 @@ public final class JobSpec implements Ac
return jobName;
}
- public void setJobName(String s) {
+ public void setJobName(final String s) {
jobName = s;
}
// -- Job Group
- public void setJobGroup(String s) {
+ public void setJobGroup(final String s) {
jobGroup = s;
}
@@ -105,13 +105,13 @@ public final class JobSpec implements Ac
return description;
}
- public void setDescription(String s) {
+ public void setDescription(final String s) {
description = s;
}
// -- Recoverable
- public void setRequestsRecovery(boolean b) {
+ public void setRequestsRecovery(final boolean b) {
recoverable = b;
}
@@ -125,13 +125,13 @@ public final class JobSpec implements Ac
return durable;
}
- public void setDurable(boolean b) {
+ public void setDurable(final boolean b) {
durable = b;
}
// -- Calendar name
- public void setCalendarName(String s) {
+ public void setCalendarName(final String s) {
calendarName = s;
}
@@ -141,7 +141,7 @@ public final class JobSpec implements Ac
// -- Expression
- public void setCronExpression(String s) {
+ public void setCronExpression(final String s) {
cronExpression = s;
}
@@ -151,60 +151,60 @@ public final class JobSpec implements Ac
/**
* An alias for CronExpression
+ * See http://quartz-scheduler.org/api/2.0.0/org/quartz/CronExpression.html
*
- * @param s
+ * @param s Valid cron expression
*/
- public void setCronTrigger(String s) {
+ public void setCronTrigger(final String s) {
setCronExpression(s);
}
// --
- public void setTimeZone(String timeZone) {
+ public void setTimeZone(final String timeZone) {
this.timeZone = timeZone;
}
// --
- public void setStartTime(String startTime) {
- Date date = parse(startTime);
+ public void setStartTime(final String startTime) {
+ final Date date = parse(startTime);
if (date != null) {
this.startTime = startTime;
}
}
- public void setEndTime(String endTime) {
- Date date = parse(endTime);
+ public void setEndTime(final String endTime) {
+ final Date date = parse(endTime);
if (date != null) {
this.endTime = endTime;
}
}
+ private Date parse(final String value) {
- private Date parse(String value) {
-
- String[] formats = {
- "EEE MMM d HH:mm:ss z yyyy",
- "EEE, d MMM yyyy HH:mm:ss Z",
- "yyyy-MM-dd HH:mm:ss.S",
- "yyyy-MM-dd HH:mm:ss.SZ",
- "yyyy-MM-dd HH:mm:ss.S",
- "yyyy-MM-dd HH:mm:ssZ",
- "yyyy-MM-dd HH:mm:ss",
- "yyyy-MM-dd HH:mmZ",
- "yyyy-MM-dd HH:mm",
- "yyyy-MM-dd'T'HH:mm:ss.SZ",
- "yyyy-MM-dd'T'HH:mm:ss.S",
- "yyyy-MM-dd'T'HH:mm:ssZ",
- "yyyy-MM-dd'T'HH:mm:ss",
- "yyyy-MM-dd'T'HH:mmZ",
- "yyyy-MM-dd'T'HH:mm",
- "yyyy-MM-dd",
- "yyyyMMdd"
+ final String[] formats = {
+ "EEE MMM d HH:mm:ss z yyyy",
+ "EEE, d MMM yyyy HH:mm:ss Z",
+ "yyyy-MM-dd HH:mm:ss.S",
+ "yyyy-MM-dd HH:mm:ss.SZ",
+ "yyyy-MM-dd HH:mm:ss.S",
+ "yyyy-MM-dd HH:mm:ssZ",
+ "yyyy-MM-dd HH:mm:ss",
+ "yyyy-MM-dd HH:mmZ",
+ "yyyy-MM-dd HH:mm",
+ "yyyy-MM-dd'T'HH:mm:ss.SZ",
+ "yyyy-MM-dd'T'HH:mm:ss.S",
+ "yyyy-MM-dd'T'HH:mm:ssZ",
+ "yyyy-MM-dd'T'HH:mm:ss",
+ "yyyy-MM-dd'T'HH:mmZ",
+ "yyyy-MM-dd'T'HH:mm",
+ "yyyy-MM-dd",
+ "yyyyMMdd"
};
- for (String format : formats) {
- SimpleDateFormat dateFormat = new SimpleDateFormat(format);
+ for (final String format : formats) {
+ final SimpleDateFormat dateFormat = new SimpleDateFormat(format);
try {
return dateFormat.parse(value);
} catch (ParseException e) {
@@ -218,36 +218,41 @@ public final class JobSpec implements Ac
// -- ActivationSpec methods
+ @SuppressWarnings("unchecked")
+ @Override
public void validate() throws InvalidPropertyException {
- if (invalidProperty != null) throw invalidProperty;
+ if (invalidProperty != null)
+ throw invalidProperty;
- int i = hashCode();
+ final int i = hashCode();
detail = JobBuilder.newJob(QuartzResourceAdapter.JobEndpoint.class)
- .withIdentity("Job" + i, Scheduler.DEFAULT_GROUP)
- .withDescription(description)
- .requestRecovery(recoverable)
- .storeDurably(durable)
- .build();
+ .withIdentity("Job" + i, Scheduler.DEFAULT_GROUP)
+ .withDescription(description)
+ .requestRecovery(recoverable)
+ .storeDurably(durable)
+ .build();
final TriggerBuilder tb = TriggerBuilder.newTrigger()
- .forJob(detail)
- .withIdentity("Trigger" + i, Scheduler.DEFAULT_GROUP)
- .withDescription(description);
+ .forJob(detail)
+ .withIdentity("Trigger" + i, Scheduler.DEFAULT_GROUP)
+ .withDescription(description);
if (startTime != null) {
- tb.startAt(parse(startTime));
+ tb.startAt(parse(startTime));
}
+
if (endTime != null) {
- tb.endAt(parse(endTime));
+ tb.endAt(parse(endTime));
}
+
if (calendarName != null) {
- tb.modifiedByCalendar(calendarName);
+ tb.modifiedByCalendar(calendarName);
}
+
final CronScheduleBuilder csb = CronScheduleBuilder.cronSchedule(getCronExpression());
if (timeZone != null) {
csb.inTimeZone(TimeZone.getTimeZone(timeZone));
}
- tb.withSchedule(CronScheduleBuilder.cronSchedule(getCronExpression()));
- trigger = tb.build();
-
+
+ trigger = tb.withSchedule(csb).build();
try {
((CronTriggerImpl) trigger).validate();
@@ -256,11 +261,13 @@ public final class JobSpec implements Ac
}
}
+ @Override
public ResourceAdapter getResourceAdapter() {
return resourceAdapter;
}
- public void setResourceAdapter(ResourceAdapter resourceAdapter) {
+ @Override
+ public void setResourceAdapter(final ResourceAdapter resourceAdapter) {
this.resourceAdapter = resourceAdapter;
}
@@ -268,7 +275,7 @@ public final class JobSpec implements Ac
return endpoint;
}
- void setEndpoint(MessageEndpoint endpoint) {
+ void setEndpoint(final MessageEndpoint endpoint) {
this.endpoint = endpoint;
}
Modified: tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/quartz/QuartzResourceAdapter.java
URL: http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/quartz/QuartzResourceAdapter.java?rev=1442913&r1=1442912&r2=1442913&view=diff
==============================================================================
--- tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/quartz/QuartzResourceAdapter.java (original)
+++ tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/quartz/QuartzResourceAdapter.java Wed Feb 6 11:23:13 2013
@@ -16,6 +16,7 @@
*/
package org.apache.openejb.resource.quartz;
+import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.util.LogCategory;
import org.quartz.Job;
import org.quartz.JobDataMap;
@@ -24,6 +25,7 @@ import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
+import org.quartz.listeners.SchedulerListenerSupport;
import javax.resource.ResourceException;
import javax.resource.spi.ActivationSpec;
@@ -33,166 +35,236 @@ import javax.resource.spi.endpoint.Messa
import javax.resource.spi.endpoint.MessageEndpointFactory;
import javax.transaction.xa.XAResource;
import java.lang.reflect.Method;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
/**
* @version $Rev$ $Date$
*/
public class QuartzResourceAdapter implements javax.resource.spi.ResourceAdapter {
- private static Exception ex = null;
- private Scheduler scheduler;
- private BootstrapContext bootstrapContext;
- private Thread startThread;
+ public static final String OPENEJB_QUARTZ_TIMEOUT = "openejb.quartz.timeout";
- public void start(BootstrapContext bootstrapContext) throws ResourceAdapterInternalException {
+ //Start and stop may be called from different threads so use atomics
+ private final AtomicReference<Exception> ex = new AtomicReference<Exception>();
+ private final AtomicReference<Scheduler> scheduler = new AtomicReference<Scheduler>();
+ private final AtomicReference<BootstrapContext> bootstrapContext = new AtomicReference<BootstrapContext>();
+ private final AtomicReference<Thread> startThread = new AtomicReference<Thread>();
- this.bootstrapContext = bootstrapContext;
+ @Override
+ public void start(final BootstrapContext bootstrapContext) throws ResourceAdapterInternalException {
- startThread = new Thread("Quartz Scheduler Start") {
+ if (null != this.bootstrapContext.getAndSet(bootstrapContext)) {
+ org.apache.openejb.util.Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources").warning("QuartzResourceAdapter is already starting");
+ return;
+ }
- @Override
- public void run() {
+ final CountDownLatch signal = new CountDownLatch(1);
+ long timeout = SystemInstance.get().getOptions().get(QuartzResourceAdapter.OPENEJB_QUARTZ_TIMEOUT, 10000L);
- Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+ if (timeout < 1000L) {
+ timeout = 1000L;
+ }
- synchronized (QuartzResourceAdapter.this) {
+ if (timeout > 60000L) {
+ timeout = 60000L;
+ }
- try {
- scheduler = StdSchedulerFactory.getDefaultScheduler();
- } catch (Exception e) {
- ex = e;
- return;
- }
+ //Allow org.quartz.InterruptableJob implementors to be interrupted on shutdown
+ System.setProperty(StdSchedulerFactory.PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN
+ , System.getProperty(StdSchedulerFactory.PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN, "true"));
+ System.setProperty(StdSchedulerFactory.PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN_WITH_WAIT
+ , System.getProperty(StdSchedulerFactory.PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN_WITH_WAIT, "true"));
+
+ startThread.set(new Thread("Quartz Scheduler Start") {
+
+ @Override
+ public void run() {
+
+ try {
+ QuartzResourceAdapter.this.scheduler.set(StdSchedulerFactory.getDefaultScheduler());
+ } catch (Exception e) {
+ QuartzResourceAdapter.this.ex.set(e);
+ return;
}
try {
- scheduler.start();
+ QuartzResourceAdapter.this.scheduler.get().getListenerManager().addSchedulerListener(new SchedulerListenerSupport() {
+ @Override
+ public void schedulerStarted() {
+ signal.countDown();
+ }
+ });
+
+ QuartzResourceAdapter.this.scheduler.get().start();
+
} catch (Exception e) {
- ex = e;
+ QuartzResourceAdapter.this.ex.set(e);
+ signal.countDown();
}
}
- };
+ });
- startThread.setDaemon(true);
- startThread.start();
+ startThread.get().setDaemon(true);
+ startThread.get().start();
+ boolean started = false;
try {
- startThread.join(5000);
+ started = signal.await(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
- Thread.currentThread().interrupt();
+ //Ignore
}
-
- if (null != ex) {
- throw new ResourceAdapterInternalException("Failed to create Quartz Scheduler", ex);
+ final Exception exception = ex.get();
+ if (null != exception) {
+ final String err = "Error creating Quartz Scheduler";
+ org.apache.openejb.util.Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources").error(err, exception);
+ throw new ResourceAdapterInternalException(err, exception);
}
- org.apache.openejb.util.Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources").info("Started Quartz Scheduler");
+ if (started) {
+ org.apache.openejb.util.Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources").info("Started Quartz Scheduler");
+ } else {
+ org.apache.openejb.util.Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources").warning("Failed to start Quartz Scheduler within defined timeout, status unknown");
+ }
}
public Scheduler getScheduler() {
- return scheduler;
+ return scheduler.get();
}
public BootstrapContext getBootstrapContext() {
- return bootstrapContext;
+ return bootstrapContext.get();
}
+ @Override
public void stop() {
- synchronized (this) {
+ final Scheduler s = scheduler.getAndSet(null);
- if (null != scheduler) {
+ if (null != s) {
- if (startThread.isAlive()) {
- startThread.interrupt();
- }
+ if (null != startThread.get()) {
+ startThread.get().interrupt();
+ }
- Thread stopThread = new Thread("Quartz Scheduler Requested Stop") {
+ long timeout = SystemInstance.get().getOptions().get(QuartzResourceAdapter.OPENEJB_QUARTZ_TIMEOUT, 10000L);
- @Override
- public void run() {
- try {
- scheduler.shutdown(true);
- } catch (Exception e) {
- ex = e;
- }
- }
- };
+ if (timeout < 1000L) {
+ timeout = 1000L;
+ }
- stopThread.setDaemon(true);
- stopThread.start();
+ if (timeout > 60000L) {
+ timeout = 60000L;
+ }
- try {
- //Block for a maximum of 5 seconds waiting for this thread to die.
- stopThread.join(5000);
- } catch (InterruptedException ie) {
- //Ignore
- }
+ final CountDownLatch shutdownWait = new CountDownLatch(1);
- try {
- if (!scheduler.isShutdown()) {
+ Thread stopThread = new Thread("Quartz Scheduler Requested Stop") {
- stopThread = new Thread("Quartz Scheduler Forced Stop") {
+ @Override
+ public void run() {
+ try {
+ s.getListenerManager().addSchedulerListener(new SchedulerListenerSupport() {
@Override
- public void run() {
- try {
- //Try to force a shutdown
- scheduler.shutdown(false);
- org.apache.openejb.util.Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources").warning("Forced Quartz stop - Jobs may be incomplete");
- } catch (Exception e) {
- ex = e;
- }
+ public void schedulerShutdown() {
+ shutdownWait.countDown();
}
- };
+ });
- stopThread.setDaemon(true);
- stopThread.start();
+ //Shutdown, but give running jobs a chance to complete.
+ //User scheduled jobs should really implement InterruptableJob
+ s.shutdown(true);
+ } catch (Exception e) {
+ QuartzResourceAdapter.this.ex.set(e);
}
- } catch (Exception e) {
- ex = e;
}
+ };
+
+ stopThread.setDaemon(true);
+ stopThread.start();
+
+ boolean stopped = false;
+ try {
+ stopped = shutdownWait.await(timeout, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ //Ignore
}
- }
- this.bootstrapContext = null;
+ try {
+ if (!stopped || !s.isShutdown()) {
- if (null != ex) {
- org.apache.openejb.util.Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources").warning("Error stopping Quartz Scheduler", ex);
- return;
+ stopThread = new Thread("Quartz Scheduler Forced Stop") {
+
+ @Override
+ public void run() {
+ try {
+ //Force a shutdown without waiting for jobs to complete.
+ s.shutdown(false);
+ org.apache.openejb.util.Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources").warning("Forced Quartz stop - Jobs may be incomplete");
+ } catch (Exception e) {
+ QuartzResourceAdapter.this.ex.set(e);
+ }
+ }
+ };
+
+ stopThread.setDaemon(true);
+ stopThread.start();
+
+ try {
+ //Give the forced shutdown a chance to complete
+ stopThread.join(timeout);
+ } catch (InterruptedException e) {
+ //Ignore
+ }
+ }
+ } catch (Exception e) {
+ ex.set(e);
+ }
}
- org.apache.openejb.util.Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources").info("Stopped Quartz Scheduler");
+ this.bootstrapContext.set(null);
+
+ if (null != ex.get()) {
+ org.apache.openejb.util.Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources").warning("Error stopping Quartz Scheduler", ex.get());
+ } else {
+ org.apache.openejb.util.Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources").info("Stopped Quartz Scheduler");
+ }
}
- public void endpointActivation(MessageEndpointFactory messageEndpointFactory, ActivationSpec activationSpec) throws ResourceException {
+ @Override
+ public void endpointActivation(final MessageEndpointFactory messageEndpointFactory, final ActivationSpec activationSpec) throws ResourceException {
- if (null == scheduler) {
+ final Scheduler s = scheduler.get();
+ if (null == s) {
throw new ResourceException("Quartz Scheduler is not available");
}
try {
- JobSpec spec = (JobSpec) activationSpec;
+ final JobSpec spec = (JobSpec) activationSpec;
- MessageEndpoint endpoint = messageEndpointFactory.createEndpoint(null);
+ final MessageEndpoint endpoint = messageEndpointFactory.createEndpoint(null);
spec.setEndpoint(endpoint);
- Job job = (Job) endpoint;
+ final Job job = (Job) endpoint;
- JobDataMap jobDataMap = spec.getDetail().getJobDataMap();
+ final JobDataMap jobDataMap = spec.getDetail().getJobDataMap();
jobDataMap.put(Data.class.getName(), new Data(job));
- scheduler.scheduleJob(spec.getDetail(), spec.getTrigger());
+ s.scheduleJob(spec.getDetail(), spec.getTrigger());
} catch (SchedulerException e) {
throw new ResourceException("Failed to schedule job", e);
}
}
- public void endpointDeactivation(MessageEndpointFactory messageEndpointFactory, ActivationSpec activationSpec) {
+ @Override
+ public void endpointDeactivation(final MessageEndpointFactory messageEndpointFactory, final ActivationSpec activationSpec) {
- if (null == scheduler) {
+ final Scheduler s = scheduler.get();
+ if (null == s) {
throw new IllegalStateException("Quartz Scheduler is not available");
}
@@ -200,7 +272,7 @@ public class QuartzResourceAdapter imple
try {
spec = (JobSpec) activationSpec;
- scheduler.deleteJob(spec.jobKey());
+ s.deleteJob(spec.jobKey());
} catch (SchedulerException e) {
throw new IllegalStateException("Failed to delete job", e);
@@ -215,24 +287,25 @@ public class QuartzResourceAdapter imple
private static Method method = null;
- public void execute(JobExecutionContext execution) throws JobExecutionException {
+ @Override
+ public void execute(final JobExecutionContext execution) throws JobExecutionException {
MessageEndpoint endpoint = null;
+ JobExecutionException ex = null;
try {
- JobDataMap jobDataMap = execution.getJobDetail().getJobDataMap();
-
- Data data = Data.class.cast(jobDataMap.get(Data.class.getName()));
+ final JobDataMap jobDataMap = execution.getJobDetail().getJobDataMap();
- Job job = data.job;
+ final Data data = Data.class.cast(jobDataMap.get(Data.class.getName()));
- endpoint = (MessageEndpoint) job;
+ final Job job = data.job;
if (null == method) {
- method = Job.class.getMethod("execute", JobExecutionContext.class);
+ method = job.getClass().getMethod("execute", JobExecutionContext.class);
}
+ endpoint = (MessageEndpoint) job;
endpoint.beforeDelivery(method);
job.execute(execution);
@@ -240,19 +313,23 @@ public class QuartzResourceAdapter imple
} catch (NoSuchMethodException e) {
throw new IllegalStateException(e);
} catch (ResourceException e) {
- throw new JobExecutionException(e);
+ ex = new JobExecutionException(e);
} catch (Throwable t) {
- throw new JobExecutionException(new Exception(t));
+ ex = new JobExecutionException(new Exception(t));
} finally {
if (null != endpoint) {
try {
endpoint.afterDelivery();
} catch (ResourceException e) {
- throw new JobExecutionException(e);
+ ex = new JobExecutionException(e);
}
}
}
+
+ if (null != ex) {
+ throw ex;
+ }
}
}
@@ -265,12 +342,13 @@ public class QuartzResourceAdapter imple
private final Job job;
- private Data(Job job) {
+ private Data(final Job job) {
this.job = job;
}
}
- public XAResource[] getXAResources(ActivationSpec[] activationSpecs) throws ResourceException {
+ @Override
+ public XAResource[] getXAResources(final ActivationSpec[] activationSpecs) throws ResourceException {
return new XAResource[0];
}
}