Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* 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.activemq.store.kahadb.scheduler;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.jms.MessageFormatException;
import org.apache.activemq.broker.scheduler.CronParser;
import org.apache.activemq.broker.scheduler.Job;
import org.apache.activemq.broker.scheduler.JobListener;
import org.apache.activemq.broker.scheduler.JobScheduler;
import org.apache.activemq.store.kahadb.disk.index.BTreeIndex;
import org.apache.activemq.store.kahadb.disk.journal.Location;
import org.apache.activemq.store.kahadb.disk.page.Transaction;
import org.apache.activemq.store.kahadb.disk.util.LongMarshaller;
import org.apache.activemq.store.kahadb.disk.util.VariableMarshaller;
import org.apache.activemq.util.ByteSequence;
import org.apache.activemq.util.IdGenerator;
import org.apache.activemq.util.ServiceStopper;
import org.apache.activemq.util.ServiceSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class JobSchedulerImpl extends ServiceSupport implements Runnable, JobScheduler {
private static final Logger LOG = LoggerFactory.getLogger(JobSchedulerImpl.class);
final JobSchedulerStoreImpl store;
private final AtomicBoolean running = new AtomicBoolean();
private String name;
BTreeIndex> index;
private Thread thread;
private final AtomicBoolean started = new AtomicBoolean(false);
private final List jobListeners = new CopyOnWriteArrayList();
private static final IdGenerator ID_GENERATOR = new IdGenerator();
private final ScheduleTime scheduleTime = new ScheduleTime();
JobSchedulerImpl(JobSchedulerStoreImpl store) {
this.store = store;
}
public void setName(String name) {
this.name = name;
}
/*
* (non-Javadoc)
*
* @see org.apache.activemq.beanstalk.JobScheduler#getName()
*/
@Override
public String getName() {
return this.name;
}
/*
* (non-Javadoc)
*
* @see org.apache.activemq.beanstalk.JobScheduler#addListener(org.apache.activemq .beanstalk.JobListener)
*/
@Override
public void addListener(JobListener l) {
this.jobListeners.add(l);
}
/*
* (non-Javadoc)
*
* @see org.apache.activemq.beanstalk.JobScheduler#removeListener(org.apache. activemq.beanstalk.JobListener)
*/
@Override
public void removeListener(JobListener l) {
this.jobListeners.remove(l);
}
@Override
public synchronized void schedule(final String jobId, final ByteSequence payload, final long delay) throws IOException {
this.store.getPageFile().tx().execute(new Transaction.Closure() {
@Override
public void execute(Transaction tx) throws IOException {
schedule(tx, jobId, payload, "", 0, delay, 0);
}
});
}
@Override
public synchronized void schedule(final String jobId, final ByteSequence payload, final String cronEntry) throws Exception {
this.store.getPageFile().tx().execute(new Transaction.Closure() {
@Override
public void execute(Transaction tx) throws IOException {
schedule(tx, jobId, payload, cronEntry, 0, 0, 0);
}
});
}
@Override
public synchronized void schedule(final String jobId, final ByteSequence payload, final String cronEntry, final long delay, final long period,
final int repeat) throws IOException {
this.store.getPageFile().tx().execute(new Transaction.Closure() {
@Override
public void execute(Transaction tx) throws IOException {
schedule(tx, jobId, payload, cronEntry, delay, period, repeat);
}
});
}
/*
* (non-Javadoc)
*
* @see org.apache.activemq.beanstalk.JobScheduler#remove(long)
*/
@Override
public synchronized void remove(final long time) throws IOException {
this.store.getPageFile().tx().execute(new Transaction.Closure() {
@Override
public void execute(Transaction tx) throws IOException {
remove(tx, time);
}
});
}
synchronized void removeFromIndex(final long time, final String jobId) throws IOException {
this.store.getPageFile().tx().execute(new Transaction.Closure() {
@Override
public void execute(Transaction tx) throws IOException {
removeFromIndex(tx, time, jobId);
}
});
}
/*
* (non-Javadoc)
*
* @see org.apache.activemq.beanstalk.JobScheduler#remove(long, java.lang.String)
*/
public synchronized void remove(final long time, final String jobId) throws IOException {
this.store.getPageFile().tx().execute(new Transaction.Closure() {
@Override
public void execute(Transaction tx) throws IOException {
remove(tx, time, jobId);
}
});
}
synchronized void remove(final long time, final List jobIds) throws IOException {
this.store.getPageFile().tx().execute(new Transaction.Closure() {
@Override
public void execute(Transaction tx) throws IOException {
remove(tx, time, jobIds);
}
});
}
/*
* (non-Javadoc)
*
* @see org.apache.activemq.beanstalk.JobScheduler#remove(java.lang.String)
*/
@Override
public synchronized void remove(final String jobId) throws IOException {
this.store.getPageFile().tx().execute(new Transaction.Closure() {
@Override
public void execute(Transaction tx) throws IOException {
remove(tx, jobId);
}
});
}
@Override
public synchronized long getNextScheduleTime() throws IOException {
Map.Entry> first = this.index.getFirst(this.store.getPageFile().tx());
return first != null ? first.getKey() : -1l;
}
/*
* (non-Javadoc)
*
* @see org.apache.activemq.beanstalk.JobScheduler#getNextScheduleJobs()
*/
@Override
public synchronized List getNextScheduleJobs() throws IOException {
final List result = new ArrayList();
this.store.getPageFile().tx().execute(new Transaction.Closure() {
@Override
public void execute(Transaction tx) throws IOException {
Map.Entry> first = index.getFirst(store.getPageFile().tx());
if (first != null) {
for (JobLocation jl : first.getValue()) {
ByteSequence bs = getPayload(jl.getLocation());
Job job = new JobImpl(jl, bs);
result.add(job);
}
}
}
});
return result;
}
@Override
public synchronized List getAllJobs() throws IOException {
final List result = new ArrayList();
this.store.getPageFile().tx().execute(new Transaction.Closure() {
@Override
public void execute(Transaction tx) throws IOException {
Iterator>> iter = index.iterator(store.getPageFile().tx());
while (iter.hasNext()) {
Map.Entry> next = iter.next();
if (next != null) {
for (JobLocation jl : next.getValue()) {
ByteSequence bs = getPayload(jl.getLocation());
Job job = new JobImpl(jl, bs);
result.add(job);
}
} else {
break;
}
}
}
});
return result;
}
@Override
public synchronized List getAllJobs(final long start, final long finish) throws IOException {
final List result = new ArrayList();
this.store.getPageFile().tx().execute(new Transaction.Closure() {
@Override
public void execute(Transaction tx) throws IOException {
Iterator>> iter = index.iterator(store.getPageFile().tx(), start);
while (iter.hasNext()) {
Map.Entry> next = iter.next();
if (next != null && next.getKey().longValue() <= finish) {
for (JobLocation jl : next.getValue()) {
ByteSequence bs = getPayload(jl.getLocation());
Job job = new JobImpl(jl, bs);
result.add(job);
}
} else {
break;
}
}
}
});
return result;
}
@Override
public synchronized void removeAllJobs() throws IOException {
this.store.getPageFile().tx().execute(new Transaction.Closure() {
@Override
public void execute(Transaction tx) throws IOException {
destroy(tx);
}
});
}
@Override
public synchronized void removeAllJobs(final long start, final long finish) throws IOException {
this.store.getPageFile().tx().execute(new Transaction.Closure() {
@Override
public void execute(Transaction tx) throws IOException {
destroy(tx, start, finish);
}
});
}
ByteSequence getPayload(Location location) throws IllegalStateException, IOException {
return this.store.getPayload(location);
}
void schedule(Transaction tx, String jobId, ByteSequence payload, String cronEntry, long delay, long period, int repeat) throws IOException {
long startTime = System.currentTimeMillis();
// round startTime - so we can schedule more jobs
// at the same time
startTime = (startTime / 1000) * 1000;
long time = 0;
if (cronEntry != null && cronEntry.length() > 0) {
try {
time = CronParser.getNextScheduledTime(cronEntry, startTime);
} catch (MessageFormatException e) {
throw new IOException(e.getMessage());
}
}
if (time == 0) {
// start time not set by CRON - so it it to the current time
time = startTime;
}
if (delay > 0) {
time += delay;
} else {
time += period;
}
Location location = this.store.write(payload, false);
JobLocation jobLocation = new JobLocation(location);
this.store.incrementJournalCount(tx, location);
jobLocation.setJobId(jobId);
jobLocation.setStartTime(startTime);
jobLocation.setCronEntry(cronEntry);
jobLocation.setDelay(delay);
jobLocation.setPeriod(period);
jobLocation.setRepeat(repeat);
if (LOG.isDebugEnabled()) {
LOG.debug("Scheduling " + jobLocation);
}
storeJob(tx, jobLocation, time);
this.scheduleTime.newJob();
}
synchronized void storeJob(final JobLocation jobLocation, final long nextExecutionTime) throws IOException {
this.store.getPageFile().tx().execute(new Transaction.Closure() {
@Override
public void execute(Transaction tx) throws IOException {
storeJob(tx, jobLocation, nextExecutionTime);
}
});
}
void storeJob(final Transaction tx, final JobLocation jobLocation, final long nextExecutionTime) throws IOException {
List values = null;
jobLocation.setNextTime(nextExecutionTime);
if (this.index.containsKey(tx, nextExecutionTime)) {
values = this.index.remove(tx, nextExecutionTime);
}
if (values == null) {
values = new ArrayList();
}
values.add(jobLocation);
this.index.put(tx, nextExecutionTime, values);
}
void remove(Transaction tx, long time, String jobId) throws IOException {
JobLocation result = removeFromIndex(tx, time, jobId);
if (result != null) {
this.store.decrementJournalCount(tx, result.getLocation());
}
}
JobLocation removeFromIndex(Transaction tx, long time, String jobId) throws IOException {
JobLocation result = null;
List values = this.index.remove(tx, time);
if (values != null) {
for (int i = 0; i < values.size(); i++) {
JobLocation jl = values.get(i);
if (jl.getJobId().equals(jobId)) {
values.remove(i);
if (!values.isEmpty()) {
this.index.put(tx, time, values);
}
result = jl;
break;
}
}
}
return result;
}
private void remove(Transaction tx, long time, List jobIds) throws IOException {
List result = removeFromIndex(tx, time, jobIds);
if (result != null) {
for (JobLocation jl : result) {
this.store.decrementJournalCount(tx, jl.getLocation());
}
}
}
private List removeFromIndex(Transaction tx, long time, List Jobs) throws IOException {
List result = null;
List values = this.index.remove(tx, time);
if (values != null) {
result = new ArrayList(values.size());
for (JobLocation job : Jobs) {
if (values.remove(job)) {
result.add(job);
}
}
if (!values.isEmpty()) {
this.index.put(tx, time, values);
}
}
return result;
}
void remove(Transaction tx, long time) throws IOException {
List values = this.index.remove(tx, time);
if (values != null) {
for (JobLocation jl : values) {
this.store.decrementJournalCount(tx, jl.getLocation());
}
}
}
void remove(Transaction tx, String id) throws IOException {
for (Iterator>> i = this.index.iterator(tx); i.hasNext();) {
Map.Entry> entry = i.next();
List values = entry.getValue();
if (values != null) {
for (JobLocation jl : values) {
if (jl.getJobId().equals(id)) {
remove(tx, entry.getKey(), id);
return;
}
}
}
}
}
synchronized void destroy(Transaction tx) throws IOException {
List keys = new ArrayList();
for (Iterator>> i = this.index.iterator(tx); i.hasNext();) {
Map.Entry> entry = i.next();
keys.add(entry.getKey());
List values = entry.getValue();
if (values != null) {
for (JobLocation jl : values) {
this.store.decrementJournalCount(tx, jl.getLocation());
}
}
}
for (Long l : keys) {
this.index.remove(tx, l);
}
}
synchronized void destroy(Transaction tx, long start, long finish) throws IOException {
List keys = new ArrayList();
for (Iterator>> i = this.index.iterator(tx, start); i.hasNext();) {
Map.Entry> entry = i.next();
if (entry.getKey().longValue() <= finish) {
keys.add(entry.getKey());
List values = entry.getValue();
if (values != null) {
for (JobLocation jl : values) {
this.store.decrementJournalCount(tx, jl.getLocation());
}
}
} else {
break;
}
}
for (Long l : keys) {
this.index.remove(tx, l);
}
}
private synchronized Map.Entry> getNextToSchedule() throws IOException {
if (!this.store.isStopped() && !this.store.isStopping()) {
Map.Entry> first = this.index.getFirst(this.store.getPageFile().tx());
return first;
}
return null;
}
void fireJob(JobLocation job) throws IllegalStateException, IOException {
if (LOG.isDebugEnabled()) {
LOG.debug("Firing " + job);
}
ByteSequence bs = this.store.getPayload(job.getLocation());
for (JobListener l : jobListeners) {
l.scheduledJob(job.getJobId(), bs);
}
}
@Override
public void run() {
try {
mainLoop();
} catch (Throwable e) {
if (this.running.get() && isStarted()) {
LOG.error(this + " Caught exception in mainloop", e);
}
} finally {
if (running.get()) {
try {
stop();
} catch (Exception e) {
LOG.error("Failed to stop " + this);
}
}
}
}
@Override
public String toString() {
return "JobScheduler:" + this.name;
}
protected void mainLoop() {
while (this.running.get()) {
this.scheduleTime.clearNewJob();
try {
// peek the next job
long currentTime = System.currentTimeMillis();
// Read the list of scheduled events and fire the jobs. Once done with the batch
// remove all that were fired, and reschedule as needed.
Map.Entry> first = getNextToSchedule();
if (first != null) {
List list = new ArrayList(first.getValue());
List fired = new ArrayList(list.size());
final long executionTime = first.getKey();
long nextExecutionTime = 0;
if (executionTime <= currentTime) {
for (final JobLocation job : list) {
int repeat = job.getRepeat();
nextExecutionTime = calculateNextExecutionTime(job, currentTime, repeat);
long waitTime = nextExecutionTime - currentTime;
this.scheduleTime.setWaitTime(waitTime);
if (job.isCron() == false) {
fireJob(job);
if (repeat != 0) {
repeat--;
job.setRepeat(repeat);
// remove this job from the index so it doesn't get destroyed
removeFromIndex(executionTime, job.getJobId());
// and re-store it
storeJob(job, nextExecutionTime);
} else {
fired.add(job);
}
} else {
// cron job will have a repeat time.
if (repeat == 0) {
// we haven't got a separate scheduler to execute at
// this time - just a cron job - so fire it
fireJob(job);
}
if (nextExecutionTime > currentTime) {
// we will run again ...
// remove this job from the index - so it doesn't get destroyed
removeFromIndex(executionTime, job.getJobId());
// and re-store it
storeJob(job, nextExecutionTime);
if (repeat != 0) {
// we have a separate schedule to run at this time
// so the cron job is used to set of a separate schedule
// hence we won't fire the original cron job to the
// listeners but we do need to start a separate schedule
String jobId = ID_GENERATOR.generateId();
ByteSequence payload = getPayload(job.getLocation());
schedule(jobId, payload, "", job.getDelay(), job.getPeriod(), job.getRepeat());
waitTime = job.getDelay() != 0 ? job.getDelay() : job.getPeriod();
this.scheduleTime.setWaitTime(waitTime);
}
} else {
fired.add(job);
}
}
}
// now remove all jobs that have not been rescheduled from this execution
// time, if there are no more entries in that time it will be removed.
remove(executionTime, fired);
// If there is a job that should fire before the currently set wait time
// we need to reset wait time otherwise we'll miss it.
Map.Entry> nextUp = getNextToSchedule();
if (nextUp != null) {
final long timeUntilNextScheduled = nextUp.getKey() - currentTime;
if (timeUntilNextScheduled < this.scheduleTime.getWaitTime()) {
this.scheduleTime.setWaitTime(timeUntilNextScheduled);
}
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Not yet time to execute the job, waiting " + (executionTime - currentTime) + " ms");
}
this.scheduleTime.setWaitTime(executionTime - currentTime);
}
}
this.scheduleTime.pause();
} catch (Exception ioe) {
LOG.error(this.name + " Failed to schedule job", ioe);
try {
this.store.stop();
} catch (Exception e) {
LOG.error(this.name + " Failed to shutdown JobSchedulerStore", e);
}
}
}
}
@Override
public void startDispatching() throws Exception {
if (!this.running.get()) {
return;
}
if (started.compareAndSet(false, true)) {
this.thread = new Thread(this, "JobScheduler:" + this.name);
this.thread.setDaemon(true);
this.thread.start();
}
}
@Override
public void stopDispatching() throws Exception {
if (started.compareAndSet(true, false)) {
this.scheduleTime.wakeup();
Thread t = this.thread;
this.thread = null;
if (t != null) {
t.join(1000);
}
}
}
@Override
protected void doStart() throws Exception {
this.running.set(true);
}
@Override
protected void doStop(ServiceStopper stopper) throws Exception {
this.running.set(false);
stopDispatching();
}
long calculateNextExecutionTime(final JobLocation job, long currentTime, int repeat) throws MessageFormatException {
long result = currentTime;
String cron = job.getCronEntry();
if (cron != null && cron.length() > 0) {
result = CronParser.getNextScheduledTime(cron, result);
} else if (job.getRepeat() != 0) {
result += job.getPeriod();
}
return result;
}
void createIndexes(Transaction tx) throws IOException {
this.index = new BTreeIndex>(this.store.getPageFile(), tx.allocate().getPageId());
}
void load(Transaction tx) throws IOException {
this.index.setKeyMarshaller(LongMarshaller.INSTANCE);
this.index.setValueMarshaller(ValueMarshaller.INSTANCE);
this.index.load(tx);
}
void read(DataInput in) throws IOException {
this.name = in.readUTF();
this.index = new BTreeIndex>(this.store.getPageFile(), in.readLong());
this.index.setKeyMarshaller(LongMarshaller.INSTANCE);
this.index.setValueMarshaller(ValueMarshaller.INSTANCE);
}
public void write(DataOutput out) throws IOException {
out.writeUTF(name);
out.writeLong(this.index.getPageId());
}
static class ValueMarshaller extends VariableMarshaller> {
static ValueMarshaller INSTANCE = new ValueMarshaller();
@Override
public List readPayload(DataInput dataIn) throws IOException {
List result = new ArrayList();
int size = dataIn.readInt();
for (int i = 0; i < size; i++) {
JobLocation jobLocation = new JobLocation();
jobLocation.readExternal(dataIn);
result.add(jobLocation);
}
return result;
}
@Override
public void writePayload(List value, DataOutput dataOut) throws IOException {
dataOut.writeInt(value.size());
for (JobLocation jobLocation : value) {
jobLocation.writeExternal(dataOut);
}
}
}
static class ScheduleTime {
private final int DEFAULT_WAIT = 500;
private final int DEFAULT_NEW_JOB_WAIT = 100;
private boolean newJob;
private long waitTime = DEFAULT_WAIT;
private final Object mutex = new Object();
/**
* @return the waitTime
*/
long getWaitTime() {
return this.waitTime;
}
/**
* @param waitTime
* the waitTime to set
*/
void setWaitTime(long waitTime) {
if (!this.newJob) {
this.waitTime = waitTime > 0 ? waitTime : DEFAULT_WAIT;
}
}
void pause() {
synchronized (mutex) {
try {
mutex.wait(this.waitTime);
} catch (InterruptedException e) {
}
}
}
void newJob() {
this.newJob = true;
this.waitTime = DEFAULT_NEW_JOB_WAIT;
wakeup();
}
void clearNewJob() {
this.newJob = false;
}
void wakeup() {
synchronized (this.mutex) {
mutex.notifyAll();
}
}
}
}