blob: d3a90cd2afb3b84171f629b84f164284b3115438 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.openejb.resource.quartz;
import org.apache.openejb.util.LogCategory;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
import javax.resource.ResourceException;
import javax.resource.spi.ActivationSpec;
import javax.resource.spi.BootstrapContext;
import javax.resource.spi.ResourceAdapterInternalException;
import javax.resource.spi.endpoint.MessageEndpoint;
import javax.resource.spi.endpoint.MessageEndpointFactory;
import javax.transaction.xa.XAResource;
import java.lang.reflect.Method;
/**
* @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 void start(BootstrapContext bootstrapContext) throws ResourceAdapterInternalException {
this.bootstrapContext = bootstrapContext;
startThread = new Thread("Quartz Scheduler Start") {
@Override
public void run() {
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
synchronized (QuartzResourceAdapter.this) {
try {
scheduler = StdSchedulerFactory.getDefaultScheduler();
} catch (Exception e) {
ex = e;
return;
}
}
try {
scheduler.start();
} catch (Exception e) {
ex = e;
}
}
};
startThread.setDaemon(true);
startThread.start();
try {
startThread.join(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
if (null != ex) {
throw new ResourceAdapterInternalException("Failed to create Quartz Scheduler", ex);
}
org.apache.openejb.util.Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources").info("Started Quartz Scheduler");
}
public Scheduler getScheduler() {
return scheduler;
}
public BootstrapContext getBootstrapContext() {
return bootstrapContext;
}
public void stop() {
synchronized (this) {
if (null != scheduler) {
if (startThread.isAlive()) {
startThread.interrupt();
}
Thread stopThread = new Thread("Quartz Scheduler Requested Stop") {
@Override
public void run() {
try {
scheduler.shutdown(true);
} catch (Exception e) {
ex = e;
}
}
};
stopThread.setDaemon(true);
stopThread.start();
try {
//Block for a maximum of 5 seconds waiting for this thread to die.
stopThread.join(5000);
} catch (InterruptedException ie) {
//Ignore
}
try {
if (!scheduler.isShutdown()) {
stopThread = new Thread("Quartz Scheduler Forced Stop") {
@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;
}
}
};
stopThread.setDaemon(true);
stopThread.start();
}
} catch (Exception e) {
ex = e;
}
}
}
this.bootstrapContext = null;
if (null != ex) {
org.apache.openejb.util.Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources").warning("Error stopping Quartz Scheduler", ex);
return;
}
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 {
if (null == scheduler) {
throw new ResourceException("Quartz Scheduler is not available");
}
try {
JobSpec spec = (JobSpec) activationSpec;
MessageEndpoint endpoint = messageEndpointFactory.createEndpoint(null);
spec.setEndpoint(endpoint);
Job job = (Job) endpoint;
JobDataMap jobDataMap = spec.getDetail().getJobDataMap();
jobDataMap.put(Data.class.getName(), new Data(job));
scheduler.scheduleJob(spec.getDetail(), spec.getTrigger());
} catch (SchedulerException e) {
throw new ResourceException("Failed to schedule job", e);
}
}
public void endpointDeactivation(MessageEndpointFactory messageEndpointFactory, ActivationSpec activationSpec) {
if (null == scheduler) {
throw new IllegalStateException("Quartz Scheduler is not available");
}
JobSpec spec = null;
try {
spec = (JobSpec) activationSpec;
scheduler.deleteJob(spec.jobKey());
} catch (SchedulerException e) {
throw new IllegalStateException("Failed to delete job", e);
} finally {
if (null != spec) {
spec.getEndpoint().release();
}
}
}
public static class JobEndpoint implements Job {
private static Method method = null;
public void execute(JobExecutionContext execution) throws JobExecutionException {
MessageEndpoint endpoint = null;
try {
JobDataMap jobDataMap = execution.getJobDetail().getJobDataMap();
Data data = Data.class.cast(jobDataMap.get(Data.class.getName()));
Job job = data.job;
endpoint = (MessageEndpoint) job;
if (null == method) {
method = Job.class.getMethod("execute", JobExecutionContext.class);
}
endpoint.beforeDelivery(method);
job.execute(execution);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(e);
} catch (ResourceException e) {
throw new JobExecutionException(e);
} catch (Throwable t) {
throw new JobExecutionException(new Exception(t));
} finally {
if (null != endpoint) {
try {
endpoint.afterDelivery();
} catch (ResourceException e) {
throw new JobExecutionException(e);
}
}
}
}
}
/**
* A private inner class is used so the key and value are not publicly visible.
* This is standard OpenEJB practice for all "public storage" maps as it prevents
* outside code from becoming dependent on or tampering with the private data.
*/
private static class Data {
private final Job job;
private Data(Job job) {
this.job = job;
}
}
public XAResource[] getXAResources(ActivationSpec[] activationSpecs) throws ResourceException {
return new XAResource[0];
}
}