package org.apache.slide.projector.engine;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.slide.projector.Context;
import org.apache.slide.projector.Processor;
import org.apache.slide.projector.Projector;
import org.apache.slide.projector.SystemContext;
import org.apache.slide.projector.URI;
import org.apache.slide.projector.application.Application;
import org.apache.slide.projector.application.ApplicationListener;
import org.apache.slide.projector.processor.process.Process;
import org.apache.slide.projector.processor.process.Step;
import org.apache.slide.projector.value.DocumentValue;
import org.apache.slide.projector.value.StreamableValue;
import org.apache.slide.projector.value.StringValue;
import org.apache.slide.projector.value.URIValue;
import org.apache.webdav.lib.Subscriber;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.xpath.XPath;
import de.zeigermann.xml.XMLStringWriter;
import de.zeigermann.xml.XMLWriter;
public class Scheduler implements ApplicationListener {
private final static Logger logger = Logger.getLogger(Scheduler.class.getName());
public final static String JOBS = "jobs.xml";
public final static String JOB_DEFINITIONS = "jobDefinitions.xml";
private final static String STARTUP = "startup";
private final static String SESSION = "session";
private final static String REQUEST = "request";
private final static String STARTUP_IDENTIFIER = "startup:";
private final Context context = new SystemContext();
private static Scheduler scheduler = new Scheduler();
private static ThreadLocal threadContext = new ThreadLocal();
private static Timer timer = new Timer();
private List jobs = new ArrayList();
private List sessionJobs = new ArrayList();
private List requestJobs = new ArrayList();
private List restoredJobs = new ArrayList();
private List installedJobNames = new ArrayList();
private Map installedJobs = new HashMap();
private Scheduler() {
}
public static Scheduler getInstance() {
return scheduler;
}
public void install(String type, URI applicationUri, URI configurationUri) {
if ( type == Application.JOBS ) {
install(configurationUri, false);
}
}
public void uninstall(String type, URI applicationUri, URI configurationUri) {
if ( type == Application.JOBS ) {
uninstall(configurationUri);
}
}
public void update(String type, URI applicationUri, URI configurationUri) {
if ( type == Application.JOBS ) {
update(configurationUri);
}
}
public void install(URI jobsUri, boolean restoreRunningJobs) {
logger.log(Level.FINE, "Installing scheduled jobs of application '"+jobsUri+"'");
try {
List applicationJobs = new ArrayList();
StreamableValue jobsResource = (StreamableValue)Projector.getRepository().getResource(jobsUri, Projector.getCredentials());
// FIXME: Beautify the code...
if ( jobsResource != null ) {
DocumentValue documentResource = new DocumentValue(jobsResource);
Document document = documentResource.getDocument();
Element rootElement = document.getRootElement();
List jobElements = XPath.newInstance("/jobs/job").selectNodes(rootElement);
List startupJobs = new ArrayList();
for ( Iterator i = jobElements.iterator(); i.hasNext(); ) {
Element jobElement = (Element)i.next();
Step job = new Step();
job.configure(jobElement);
String trigger = jobElement.getAttributeValue("trigger");
if ( trigger == null || trigger.equals(STARTUP) ) {
if ( !installedJobNames.contains(STARTUP_IDENTIFIER+job.getName()) ) {
startupJobs.add(job);
installedJobNames.add(job.getName());
}
} else if ( trigger.equals(REQUEST) ) {
requestJobs.add(job);
} else if ( trigger.equals(SESSION) ) {
sessionJobs.add(job);
}
}
// Note: starting jobs after reading all, to avoid writing to configuration files while reading...
for ( Iterator i = startupJobs.iterator(); i.hasNext(); ) {
Step job = (Step)i.next();
if ( job.getName().startsWith(STARTUP_IDENTIFIER) ) {
context.setStep(job.getName());
} else {
context.setStep(STARTUP_IDENTIFIER+job.getName());
}
launchJob(job, context);
if ( !restoreRunningJobs ) {
Projector.getRepository().subscribe("Update", jobsUri, 0,
new Subscriber() {
public void notify(String uri, Map information) {
update(new URIValue(uri));
}
}, Projector.getCredentials());
}
}
} else {
logger.log(Level.FINE, "Configured jobs resource '"+jobsUri+"' not found!");
}
// FIXME: How to handle deinstallation of running jobs belonging to an application
installedJobs.put(jobsUri, applicationJobs);
} catch (Exception exception) {
logger.log(Level.SEVERE, "Error while parsing messages", exception);
}
}
public void uninstall(URI jobsUri) {
logger.log(Level.FINE, "Uninstalling jobs '"+jobsUri+"'");
Collection jobKeys = (Collection)installedJobs.get(jobs);
for ( Iterator i = jobKeys.iterator(); i.hasNext(); ) {
String jobKey = (String)i.next();
jobs.remove(jobKey);
logger.log(Level.FINE, "Removing job '"+jobKey+"'");
}
installedJobs.remove(jobsUri);
}
public void update(URI jobsUri) {
uninstall(jobsUri);
install(jobsUri, true);
}
public void launchSessionJobs(Context context) throws Exception {
for ( Iterator i = sessionJobs.iterator(); i.hasNext(); ) {
Step job = (Step)i.next();
launchJob(job, context);
}
}
public void launchRequestJobs(Context context) throws Exception {
for ( Iterator i = requestJobs.iterator(); i.hasNext(); ) {
Step job = (Step)i.next();
launchJob(job, context);
}
}
private void launchJob(Step job, Context context) throws Exception {
logger.log(Level.FINE, "Launching job: '"+job.getName()+"'");
Processor processor = ProcessorManager.getInstance().getProcessor(job.getProcessorURI());
Map processorParameters = Process.loadParameters(job, processor, context);
ProcessorManager.process(processor, processorParameters, context);
}
/**
* @return Returns the timer.
*/
public static Timer getTimer() {
return timer;
}
public void notify(final Job job) {
if ( job.getRemainingCondition().evaluate() ) {
job.launch();
jobs.remove(job);
if ( job.repeat() ) {
job.restart();
registerJob(job);
}
}
if ( job.isPersistent() ) {
saveJobs();
}
}
public void registerJob(Job job) {
synchronized ( jobs ) {
jobs.add(job);
job.activate();
}
}
public static void setContext(Context context) {
threadContext.set(context);
}
public static Context getContext() {
if ( threadContext.get() == null ) {
threadContext.set(new SystemContext());
}
return (Context)threadContext.get();
}
public void saveJobs() {
XMLStringWriter writer = XMLStringWriter.create();
writer.writeXMLDeclaration();
writer.writeStartTag(XMLWriter.createStartTag("jobs"));
synchronized ( jobs ) {
for ( Iterator i = jobs.iterator(); i.hasNext(); ) {
Job job = (Job)i.next();
if (job.isPersistent()) {
job.save(writer);
}
}
}
writer.writeEndTag(XMLWriter.createEndTag("jobs"));
try {
Projector.getRepository().setResource(new URIValue(Projector.getWorkDir() + JOBS), new StringValue(writer.toString()), Projector.getCredentials());
} catch (IOException exception) {
logger.log(Level.SEVERE, "Exception occured while writing jobs to repository", exception);
}
}
}