
org.apache.ode.bpel.engine.cron.CronScheduler Maven / Gradle / Ivy
/*
* 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.ode.bpel.engine.cron;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ode.bpel.engine.BpelServerImpl.ContextsAware;
import org.apache.ode.bpel.engine.Contexts;
import org.apache.ode.bpel.iapi.ClusterAware;
import org.apache.ode.bpel.iapi.ProcessConf;
import org.apache.ode.bpel.iapi.ProcessConf.CronJob;
import org.apache.ode.bpel.iapi.Scheduler.JobDetails;
import org.apache.ode.bpel.iapi.Scheduler.MapSerializableRunnable;
import org.apache.ode.utils.CronExpression;
import javax.xml.namespace.QName;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
public class CronScheduler {
static final Log __log = LogFactory.getLog(CronScheduler.class);
// minimum interval of the cron job(1 second)
private final long MIN_INTERVAL = 0;
// if the work is schedule too late from the due time, skip it
private final long TOLERABLE_SCHEDULE_DELAY = 0;
private ExecutorService _scheduledTaskExec;
private Contexts _contexts;
private final Timer _schedulerTimer = new Timer("CronScheduler", true);
private final Collection _systemTerminationListeners = new ArrayList();
private final Map> _terminationListenersByPid = new HashMap>();
private volatile boolean _shuttingDown = false;
public void setScheduledTaskExec(ExecutorService taskExec) {
_scheduledTaskExec = taskExec;
}
public void setContexts(Contexts _contexts) {
this._contexts = _contexts;
}
public void shutdown() {
_shuttingDown = true;
_schedulerTimer.cancel();
for (TerminationListener listener : _systemTerminationListeners) {
listener.terminate();
}
_systemTerminationListeners.clear();
for (Collection listeners : _terminationListenersByPid.values()) {
for (TerminationListener listener : listeners) {
listener.terminate();
}
}
_terminationListenersByPid.clear();
}
public void cancelProcessCronJobs(QName pid, boolean undeployed) {
assert pid != null;
if (__log.isDebugEnabled()) __log.debug("Cancelling PROCESS CRON jobs for: " + pid);
Collection listenersToTerminate = new ArrayList();
synchronized (_terminationListenersByPid) {
Collection listeners = _terminationListenersByPid.get(pid);
if (listeners != null) {
listenersToTerminate.addAll(listeners);
listeners.clear();
}
if (undeployed) {
_terminationListenersByPid.remove(pid);
}
}
// terminate existing cron jobs if there are
synchronized (pid) {
for (TerminationListener listener : listenersToTerminate) {
listener.terminate();
}
}
}
public void scheduleProcessCronJobs(QName pid, ProcessConf pconf) {
if (_shuttingDown) {
return;
}
assert pid != null;
cancelProcessCronJobs(pid, false);
Collection newListeners = new ArrayList();
synchronized (pid) {
if (__log.isDebugEnabled()) __log.debug("Scheduling PROCESS CRON jobs for: " + pid);
// start new cron jobs
for (final CronJob job : pconf.getCronJobs()) {
if (__log.isDebugEnabled())
__log.debug("Scheduling PROCESS CRON job: " + job.getCronExpression() + " for: " + pid);
// for each different scheduled time
Runnable runnable = new Runnable() {
public void run() {
if (__log.isDebugEnabled())
__log.debug("Running cron cleanup with details list size: " + job.getRunnableDetailList().size());
for (JobDetails details : job.getRunnableDetailList()) {
try {
// for each clean up for the scheduled time
RuntimeDataCleanupRunnable cleanup = new RuntimeDataCleanupRunnable();
cleanup.restoreFromDetails(details);
cleanup.setContexts(_contexts);
cleanup.run();
if (__log.isDebugEnabled())
__log.debug("Finished running runtime data cleanup from a PROCESS CRON job: " + cleanup);
} catch (Exception re) {
__log.error("Error during runtime data cleanup from a PROCESS CRON: " + details + "; check your cron settings in deploy.xml.", re);
// don't sweat.. the rest of the system and other cron jobs still should work
}
}
}
};
newListeners.add(schedule(job.getCronExpression(), runnable, null, null));
}
}
// make sure the pid does not get into the terminationListener map if no cron is setup
if (!newListeners.isEmpty()) {
synchronized (_terminationListenersByPid) {
Collection oldListeners = _terminationListenersByPid.get(pid);
if (oldListeners == null) {
_terminationListenersByPid.put(pid, newListeners);
} else {
oldListeners.addAll(newListeners);
}
}
}
}
/**
* In BPS we don't want to use refreshing functionality for system cron jobs.
* This will schedule the system cron jobs at the start-up of the ODE server.
*
* @param systemCronJobs System cron job list
*/
public void scheduleSystemCronJobs(List systemCronJobs) {
try{
for (final CronJob job : systemCronJobs) {
if (__log.isDebugEnabled()) {
__log.debug("Scheduling SYSTEM CRON job:" + job);
}
Runnable runnable = new Runnable() {
public void run() {
for (JobDetails details : job.getRunnableDetailList()) {
try {
// for now, we have only runtime data cleanup cron job defined
// for each clean up for the scheduled time
RuntimeDataCleanupRunnable cleanup = new RuntimeDataCleanupRunnable();
synchronized (_terminationListenersByPid) {
if (!_terminationListenersByPid.isEmpty()) {
details.getDetailsExt().put("pidsToExclude", _terminationListenersByPid.keySet());
}
}
cleanup.restoreFromDetails(details);
cleanup.setContexts(_contexts);
cleanup.run();
if (__log.isDebugEnabled()) {
__log.debug("Finished running runtime data cleanup from a SYSTEM CRON job:" + cleanup);
}
} catch (Exception e) {
__log.error("Error running a runtime data cleanup from a SYSTEM CRON job: " + details + "; check your system cron setup.", e);
}
}
}
};
_systemTerminationListeners.add(schedule(job.getCronExpression(), runnable, null, null));
}
}catch (Exception e){
__log.error("Error during refreshing SYSTEM CRON schedules: ", e);
}
}
public void refreshSystemCronJobs(SystemSchedulesConfig systemSchedulesConf) {
if (_shuttingDown) {
return;
}
synchronized (_systemTerminationListeners) {
if (__log.isDebugEnabled()) __log.debug("Refreshing SYSTEM CRON jobs.");
try {
// if error thrown on reading the schedules.xml, do not cancel existing cron jobs
List systemCronJobs = systemSchedulesConf.getSystemCronJobs();
// cancel cron jobs
for (TerminationListener listener : _systemTerminationListeners) {
listener.terminate();
}
_systemTerminationListeners.clear();
// start new cron jobs
for (final CronJob job : systemCronJobs) {
if (__log.isDebugEnabled()) __log.debug("Scheduling SYSTEM CRON job:" + job);
// for each different scheduled time
Runnable runnable = new Runnable() {
public void run() {
for (JobDetails details : job.getRunnableDetailList()) {
try {
// for now, we have only runtime data cleanup cron job defined
// for each clean up for the scheduled time
RuntimeDataCleanupRunnable cleanup = new RuntimeDataCleanupRunnable();
synchronized (_terminationListenersByPid) {
if (!_terminationListenersByPid.isEmpty()) {
details.getDetailsExt().put("pidsToExclude", _terminationListenersByPid.keySet());
}
}
cleanup.restoreFromDetails(details);
cleanup.setContexts(_contexts);
cleanup.run();
if (__log.isDebugEnabled())
__log.debug("Finished running runtime data cleanup from a SYSTEM CRON job:" + cleanup);
} catch (Exception e) {
__log.error("Error running a runtime data cleanup from a SYSTEM CRON job: " + details + "; check your system cron setup.", e);
}
}
}
};
_systemTerminationListeners.add(schedule(job.getCronExpression(), runnable, null, null));
}
} catch (Exception e) {
__log.error("Error during refreshing SYSTEM CRON schedules: ", e);
}
}
}
public TerminationListener schedule(final CronExpression cronExpression,
final Runnable runnable, final JobDetails runnableDetails,
TerminationListener terminationListener) {
if (_shuttingDown) {
__log.info("CRON Scheduler is being shut down. This new scheduling request is ignored.");
return new TerminationListener() {
public void terminate() {
// do nothing
}
};
}
assert cronExpression != null;
assert runnable != null;
final Date nextScheduleTime = cronExpression.getNextValidTimeAfter(new Date(
System.currentTimeMillis() + MIN_INTERVAL));
final CronScheduledJob job = new CronScheduledJob(nextScheduleTime, runnable, runnableDetails, cronExpression, terminationListener);
if (__log.isDebugEnabled())
__log.debug("CRON will run in " + (nextScheduleTime.getTime() - System.currentTimeMillis()) + "ms.");
try {
_schedulerTimer.schedule(new TimerTask() {
@Override
public void run() {
__log.debug("Cron scheduling timer kicked in: " + cronExpression);
// run only if the current node is the coordinator,
// with the SimpleScheduler, the node is always the coordinator
if (!(_contexts.scheduler instanceof ClusterAware)
|| ((ClusterAware) _contexts.scheduler).amICoordinator()) {
// do not hold the timer thread too long, submit the work to an executorService
_scheduledTaskExec.submit(job);
__log.debug("CRON job scheduled " + runnable);
}
}
}, nextScheduleTime);
} catch (IllegalStateException ise) {
if (_shuttingDown) {
__log.info("CRON Scheduler is being shut down. This new scheduling request is ignored.");
} else {
throw ise;
}
}
return job.terminationListener;
}
public interface TerminationListener {
void terminate();
}
private class CronScheduledJob implements Callable {
private volatile boolean terminated = false;
private Date nextScheduleTime;
private Runnable runnable;
private JobDetails runnableDetails;
private CronExpression cronExpression;
private TerminationListener terminationListener;
public CronScheduledJob(Date nextScheduleTime,
Runnable runnable, JobDetails runnableDetails,
CronExpression cronExpression, TerminationListener terminationListener) {
this.nextScheduleTime = nextScheduleTime;
this.runnable = runnable;
this.runnableDetails = runnableDetails;
this.cronExpression = cronExpression;
if (terminationListener == null) {
terminationListener = new TerminationListener() {
public void terminate() {
terminated = true;
}
};
}
this.terminationListener = terminationListener;
}
public TerminationListener call() throws Exception {
try {
if (TOLERABLE_SCHEDULE_DELAY == 0 ||
nextScheduleTime.getTime() < System.currentTimeMillis() + TOLERABLE_SCHEDULE_DELAY) {
if (runnableDetails != null &&
runnable instanceof MapSerializableRunnable) {
((MapSerializableRunnable) runnable).restoreFromDetails(runnableDetails);
}
if (runnable instanceof ContextsAware) {
((ContextsAware) runnable).setContexts(_contexts);
}
if (!_shuttingDown && !terminated) {
__log.debug("Running CRON job: " + runnable + " for " + nextScheduleTime.getTime());
runnable.run();
}
} else {
// ignore the scheduling.. it will be scheduled later
}
} catch (Exception e) {
if (_shuttingDown) {
__log.info("A cron job threw an Exception during ODE shutdown: " + e.getMessage() + ", you can ignore the error.");
} else if (e instanceof RuntimeException) {
throw e;
} else {
throw new RuntimeException("Exception during running cron scheduled job: " + runnable, e);
}
} finally {
if (!_shuttingDown && !terminated) {
schedule(cronExpression, runnable, runnableDetails, terminationListener);
}
}
return terminationListener;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy