package org.bbaw.wsp.cms.scheduler;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.logging.Logger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobListener;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import de.mpg.mpiwg.berlin.mpdl.exception.ApplicationException;
public class CmsChainScheduler {
private static CmsChainScheduler instance;
private static String CRUD_JOB = "MPDL_CRUD_JOB";
private static String CRUD_TRIGGER = "MPDL_CRUD_TRIGGER";
private static String CRUD_GROUP = "MPDL_CRUD_GROUP";
private static Logger LOGGER = Logger.getLogger(CmsDocJob.class.getName());
private org.quartz.Scheduler scheduler;
private JobListener jobListener;
private Queue<CmsDocOperation> docOperationQueue = new PriorityQueue<CmsDocOperation>();
private HashMap<Integer, CmsDocOperation> finishedDocOperations = new HashMap<Integer, CmsDocOperation>();
private boolean operationInProgress = false;
private int jobOrderId = 0;
public static CmsChainScheduler getInstance() throws ApplicationException {
if (instance == null) {
instance = new CmsChainScheduler();
instance.init();
}
return instance;
}
public CmsDocOperation doOperation(CmsDocOperation docOperation) throws ApplicationException {
jobOrderId++;
docOperation.setOrderId(jobOrderId);
queueOperation(docOperation);
scheduleNextOperation();
return docOperation;
}
public void finishOperation(CmsDocOperation docOperation) throws ApplicationException {
operationInProgress = false;
Date now = new Date();
docOperation.setEnd(now);
docOperation.setStatus("finished");
int jobId = new Integer(docOperation.getOrderId());
finishedDocOperations.put(jobId, docOperation);
log(docOperation);
// schedule next job if there is one
scheduleNextOperation();
}
private void log(CmsDocOperation docOperation) {
Date startTime = docOperation.getStart();
Date endTime = docOperation.getEnd();
long executionTime = -1;
if (startTime != null && endTime != null)
executionTime = (endTime.getTime() - startTime.getTime());
String jobInfo = "Document operation " + docOperation.toString() + ": started at: " + startTime +
" and ended at: " + endTime + " (needed time: " + executionTime + " ms)";
LOGGER.info(jobInfo);
}
public synchronized void scheduleNextOperation() throws ApplicationException {
if (isOperationInProgress()) {
// nothing, operation has to wait
} else {
CmsDocOperation docOperation = docOperationQueue.poll();
if (docOperation == null) {
// if queue is empty then do nothing (there are no more operations to execute)
} else {
Date now = new Date();
operationInProgress = true;
docOperation.setStart(now);
scheduleJob(docOperation, now);
}
}
}
public ArrayList<CmsDocOperation> getDocOperations() throws ApplicationException {
ArrayList<CmsDocOperation> docOperations = new ArrayList<CmsDocOperation>();
try {
// first: all finished jobs
Collection<CmsDocOperation> finiDocOperations = finishedDocOperations.values();
docOperations.addAll(finiDocOperations);
// second: all currently executed jobs
if (operationInProgress) {
List<JobExecutionContext> currentJobs = (List<JobExecutionContext>) scheduler.getCurrentlyExecutingJobs();
Iterator<JobExecutionContext> iter = currentJobs.iterator();
while (iter.hasNext()) {
JobExecutionContext jobExecutionContext = iter.next();
CmsDocOperation docOperation = getDocOperation(jobExecutionContext);
if (docOperation != null) {
docOperations.add(docOperation);
}
}
}
// third: all queued jobs
Iterator<CmsDocOperation> iter = docOperationQueue.iterator();
while (iter.hasNext()) {
CmsDocOperation docOperation = iter.next();
docOperations.add(docOperation);
}
} catch (SchedulerException e) {
LOGGER.severe(e.getMessage());
throw new ApplicationException(e);
}
return docOperations;
}
public CmsDocOperation getDocOperation(int jobId) throws ApplicationException {
CmsDocOperation docOperation = null;
try {
// first try: looks into currently executing jobs
if (operationInProgress) {
List<JobExecutionContext> currentJobs = (List<JobExecutionContext>) scheduler.getCurrentlyExecutingJobs();
Iterator<JobExecutionContext> iter = currentJobs.iterator();
while (iter.hasNext()) {
JobExecutionContext jobExecutionContext = iter.next();
docOperation = getDocOperation(jobExecutionContext);
if (docOperation != null) {
int dopOpJobId = docOperation.getOrderId();
if (jobId == dopOpJobId)
return docOperation;
}
}
}
// second try: look into finished jobs
docOperation = finishedDocOperations.get(new Integer(jobId));
if (docOperation != null) {
return docOperation;
}
// third try: look into queued jobs
Iterator<CmsDocOperation> iter = docOperationQueue.iterator();
while (iter.hasNext()) {
docOperation = iter.next();
if (docOperation.getOrderId() == jobId)
return docOperation;
}
} catch (SchedulerException e) {
LOGGER.severe(e.getMessage());
throw new ApplicationException(e);
}
// if not found return null
return null;
}
public CmsDocOperation getDocOperation(JobExecutionContext jobExecutionContext) {
CmsDocOperation docOperation = null;
if (jobExecutionContext != null) {
JobDetail job = jobExecutionContext.getJobDetail();
JobDataMap parameters = job.getJobDataMap();
docOperation = (CmsDocOperation) parameters.get("operation");
}
return docOperation;
}
private void queueOperation(CmsDocOperation docOperation) {
int operationsBefore = docOperationQueue.size();
if (operationsBefore == 0)
docOperation.setStatus("waiting in operation queue");
else
docOperation.setStatus("waiting in operation queue: " + operationsBefore + " operations heve to be executed before this operation");
docOperationQueue.offer(docOperation);
}
private synchronized boolean isOperationInProgress() {
return operationInProgress;
}
private void scheduleJob(CmsDocOperation docOperation, Date fireTime) throws ApplicationException {
try {
int jobId = docOperation.getOrderId();
String jobName = CRUD_JOB + "-id-" + jobId + "-timeId-" + fireTime;
JobDetail job = new JobDetail(jobName, CRUD_GROUP, CmsDocJob.class);
JobDataMap parameters = new JobDataMap();
parameters.put("operation", docOperation);
job.setJobDataMap(parameters);
job.addJobListener(jobListener.getName());
String triggerName = CRUD_TRIGGER + "-id-" + jobId + "-timeId-" + fireTime;
Trigger trigger = new SimpleTrigger(triggerName, CRUD_GROUP, fireTime);
scheduler.scheduleJob(job, trigger);
String jobInfo = "Schedule document operation: " + docOperation.toString() + ": done at: " + fireTime.toString();
LOGGER.info(jobInfo);
} catch (SchedulerException e) {
LOGGER.severe(e.getMessage());
throw new ApplicationException(e);
}
}
private void init() throws ApplicationException {
try {
if (scheduler == null) {
String quartzPath = getQuartzPath();
StdSchedulerFactory schedulerFactory = new StdSchedulerFactory(quartzPath);
scheduler = schedulerFactory.getScheduler();
jobListener = new CmsChainSchedulerListener();
scheduler.addJobListener(jobListener);
scheduler.start();
LOGGER.info("Started Quartz scheduler factory: " + quartzPath);
}
} catch (SchedulerException e) {
LOGGER.severe(e.getMessage());
throw new ApplicationException(e);
}
}
public void end() throws ApplicationException {
try {
if (scheduler != null) {
scheduler.shutdown();
}
String quartzPath = getQuartzPath();
LOGGER.info("Ended Quartz scheduler factory: " + quartzPath);
} catch (SchedulerException e) {
LOGGER.severe(e.getMessage());
throw new ApplicationException(e);
}
}
private String getQuartzPath() {
URL quartzUrl = CmsChainScheduler.class.getResource("quartz.properties");
String quartzPath = quartzUrl.getPath();
if (quartzPath.indexOf(".jar!") != -1) {
int beginIndex = quartzPath.indexOf(".jar!") + 6;
quartzPath = quartzPath.substring(beginIndex);
}
return quartzPath;
}
}