/**
Copyright (C) 2012 Delcyon, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.delcyon.capo.tasks;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import javax.jcr.LoginException;
import javax.jcr.RepositoryException;
import javax.jcr.SimpleCredentials;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import org.tanukisoftware.wrapper.WrapperManager;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXParseException;
import com.delcyon.capo.CapoApplication;
import com.delcyon.capo.CapoApplication.ApplicationState;
import com.delcyon.capo.CapoApplication.Location;
import com.delcyon.capo.ContextThread;
import com.delcyon.capo.annotations.DefaultDocumentProvider;
import com.delcyon.capo.annotations.DirectoyProvider;
import com.delcyon.capo.client.CapoClient;
import com.delcyon.capo.controller.LocalRequestProcessor;
import com.delcyon.capo.controller.elements.GroupElement;
import com.delcyon.capo.controller.elements.TaskElement;
import com.delcyon.capo.controller.elements.TaskElement.Attributes;
import com.delcyon.capo.modules.ModuleProvider;
import com.delcyon.capo.preferences.Preference;
import com.delcyon.capo.preferences.PreferenceInfo;
import com.delcyon.capo.preferences.PreferenceInfoHelper;
import com.delcyon.capo.preferences.PreferenceProvider;
import com.delcyon.capo.protocol.client.CapoConnection;
import com.delcyon.capo.resourcemanager.CapoDataManager;
import com.delcyon.capo.resourcemanager.ResourceDescriptor;
import com.delcyon.capo.resourcemanager.ResourceDescriptor.Action;
import com.delcyon.capo.server.CapoServer;
import com.delcyon.capo.server.jackrabbit.CapoJcrServer;
import com.delcyon.capo.xml.XPath;
/**
* @author jeremiah
*
*/
@PreferenceProvider(preferences=TaskManagerThread.Preferences.class)
@DirectoyProvider(preferenceName="TASK_DIR",preferences=TaskManagerThread.Preferences.class)
@DefaultDocumentProvider(name="task-status.xml",directoryPreferenceName="TASK_DIR", preferences = TaskManagerThread.Preferences.class)
public class TaskManagerThread extends ContextThread
{
public enum Preferences implements Preference
{
@PreferenceInfo(arguments={"dir"}, defaultValue="tasks", description="Where to store resource monitor file", longOption="TASK_DIR", option="TASK_DIR")
TASK_DIR,
@PreferenceInfo(arguments={"ms"}, defaultValue="30000", description="How long before a task is considered orphaned", longOption="TASK_LIFESPAN", option="TASK_LIFESPAN")
TASK_DEFAULT_LIFESPAN,
@PreferenceInfo(arguments={"ms"}, defaultValue="30000", description="Delay between client update and task syncing", longOption="DEFAULT_CLIENT_SYNC_INTERVAL", option="DEFAULT_CLIENT_SYNC_INTERVAL",location=Location.CLIENT)
DEFAULT_CLIENT_SYNC_INTERVAL,
@PreferenceInfo(arguments={"action"}, defaultValue="DELETE", description="What to do when a task is considered orphaned (DELETE|IGNORE)", longOption="TASK_DEFAULT_ORPHAN_ACTION", option="TASK_DEFAULT_ORPHAN_ACTION")
TASK_DEFAULT_ORPHAN_ACTION,
@PreferenceInfo(arguments={"boolean"}, defaultValue="true", description="When shutting down the client. Controls if we want a hard exit, or controlled. Testing wants a hard exit. [true|false] default is false", longOption="QUICK_SHUTDOWN", option="QUICK_SHUTDOWN",location=Location.CLIENT)
QUICK_SHUTDOWN,
@PreferenceInfo(arguments={"ms"}, defaultValue="10000", description="Interval at which overall task manager thread runs", longOption="TASK_INTERVAL", option="TASK_INTERVAL")
TASK_INTERVAL;
@Override
public String[] getArguments()
{
return PreferenceInfoHelper.getInfo(this).arguments();
}
@Override
public String getDefaultValue()
{
return PreferenceInfoHelper.getInfo(this).defaultValue();
}
@Override
public String getDescription()
{
return PreferenceInfoHelper.getInfo(this).description();
}
@Override
public String getLongOption()
{
return PreferenceInfoHelper.getInfo(this).longOption();
}
@Override
public String getOption()
{
return PreferenceInfoHelper.getInfo(this).option();
}
@Override
public Location getLocation()
{
return PreferenceInfoHelper.getInfo(this).location();
}
}
private boolean runAsService = true;
private Transformer transformer;
private CapoDataManager capoDataManager;
private ReentrantLock lock = new ReentrantLock();
//private volatile Document taskManagerDocument;
//private ResourceDescriptor taskManagerDocumentFileDescriptor;
private TaskManagerDocumentUpdaterThread tasksDocumentUpdaterThread;
private DocumentBuilder documentBuilder;
private ConcurrentHashMap<String,HashMap<String, String>> taskConcurrentHashMap = new ConcurrentHashMap<String, HashMap<String,String>>();
//private volatile boolean interrupted = false;
//private volatile boolean finished = false;
private volatile ApplicationState taskManagerState = ApplicationState.NONE;
private long lastSyncTime = 0l;
private long lastRunTime = 0l;
/**
* This should only called once, and is the main initialization method for this class
* @param capoDataManager
* @throws Exception
*/
public synchronized static void startTaskManagerThread() throws Exception
{
boolean runAsService = true;
if (CapoApplication.getTaskManagerThread() == null)
{
if (CapoApplication.getApplication() instanceof CapoServer)
{
runAsService = true;
}
else
{
String clientAsServiceValue = CapoApplication.getConfiguration().getValue(CapoClient.Preferences.CLIENT_AS_SERVICE);
if (clientAsServiceValue != null && clientAsServiceValue.equalsIgnoreCase("true"))
{
CapoApplication.logger.log(Level.INFO, "Running CapoClient as a service");
runAsService = true;
}
else
{
CapoApplication.logger.log(Level.INFO, "Running CapoClient in Once NON-SERIVCE mode. See "+CapoClient.Preferences.CLIENT_AS_SERVICE+" preference.");
runAsService = false;
}
}
TaskManagerThread taskManagerThread = new TaskManagerThread(runAsService);
CapoApplication.setTaskManagerThread(taskManagerThread);
taskManagerThread.start(); //if this is NOT a service, the thread will only run once.
}
}
/**
* Provides Global Access to The Monitor Thread. This is how things should be called.
* @return
*/
public static TaskManagerThread getTaskManagerThread()
{
if (CapoApplication.getApplication() != null)
{
return CapoApplication.getTaskManagerThread();
}
else
{
return null;
}
}
/**
* Setup and start the resource monitor thread
* @param capoDataManager
* @throws Exception
*/
private TaskManagerThread(boolean runAsService) throws Exception
{
super(TaskManagerThread.class.getName()+" - "+CapoApplication.getApplication().getApplicationDirectoryName().toUpperCase());
this.capoDataManager = CapoApplication.getDataManager();
this.runAsService = runAsService;
//this sets up our basic identity transform, that removes indentation and extraneous spaces
//it has nothing to do with figuring out who we are
Document identityDocument = CapoApplication.getDefaultDocument("identity_transform.xsl");
TransformerFactory tFactory = TransformerFactory.newInstance();
transformer = tFactory.newTransformer(new DOMSource(identityDocument));
transformer.setOutputProperty(OutputKeys.INDENT, "no");
//this.taskManagerDocumentFileDescriptor = capoDataManager.getResourceDescriptor(null,CapoApplication.getConfiguration().getValue(Preferences.TASKS_FILE));
//this.taskManagerDocumentFileDescriptor.addResourceParameters(null, new ResourceParameter(FileResourceType.Parameters.PARENT_PROVIDED_DIRECTORY,Preferences.TASK_DIR));
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
documentBuilder = documentBuilderFactory.newDocumentBuilder();
// try
// {
// this.taskManagerDocument = documentBuilder.parse(taskManagerDocumentFileDescriptor.getInputStream(null));
// }
// catch (Exception exception)
// {
// //on an exception, just make a blank file
// CapoApplication.logger.log(Level.WARNING, "Error parseing tasks file, using default");
// this.taskManagerDocument = CapoApplication.getDefaultDocument("tasks.xml");
// }
//go ahead and start things up
tasksDocumentUpdaterThread = new TaskManagerDocumentUpdaterThread(lock,runAsService);
if (runAsService == true) // TODO in theory if this is false, you can do a single pass of monitors, then it will exit, and not update anything
{
tasksDocumentUpdaterThread.start();
}
}
public ReentrantLock getLock()
{
return lock;
}
public void interrupt()
{
synchronized (this)
{
//check to see if we've already been interrupted, it's possible that we can be called twice sometimes esp. in testing.
if(this.taskManagerState.ordinal() >= ApplicationState.STOPPING.ordinal())
{
CapoServer.logger.log(Level.WARNING, "Second call to interrupt TaskManager.");
//just bail out if we're already stopping.
return;
}
//only set us to stopping if we're running or something earlier, this can happen when we don't run the client as a service
else if (this.taskManagerState.ordinal() < ApplicationState.STOPPING.ordinal())
{
this.taskManagerState = ApplicationState.STOPPING;
}
super.interrupt();
}
CapoServer.logger.log(Level.INFO, "Waiting for TaskManager to finish");
while(this.taskManagerState != ApplicationState.STOPPED)
{
try
{
Thread.sleep(100);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
tasksDocumentUpdaterThread.interrupt();
CapoServer.logger.log(Level.INFO, "Waiting for TaskDocumentUpdater Thread to shutdown");
while(tasksDocumentUpdaterThread.getUpdaterState() != ApplicationState.STOPPED)
{
try
{
Thread.sleep(100);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
//SORT OF DONE
@Override
public void run()
{
taskManagerState = ApplicationState.READY;
if(CapoApplication.isServer())
{
try
{
setSession(CapoJcrServer.createSession());
tasksDocumentUpdaterThread.setSession(getSession());
}
catch (Exception exception)
{
CapoServer.logger.log(Level.SEVERE, "Couldn't start TaskManager because of repository errors.",exception);
this.taskManagerState = ApplicationState.STOPPED;
WrapperManager.stopAndReturn(0);
}
}
while(taskManagerState.ordinal() < ApplicationState.STOPPING.ordinal())
{
try
{
//Lock everything down until we finish running
lock.lock();
//if we got asked to stop while waiting for the lock, bail out
if (taskManagerState.ordinal() >= ApplicationState.STOPPING.ordinal())
{
break;
}
//don't update this unless we are actually going to run.
this.lastRunTime = System.currentTimeMillis();
//===============================================BEGIN CLIENT SYNC===============================================================
//check to see if we need to check in with the server
if (lastSyncTime == 0) //skip initial sync since we can't get here without it happening first
{
lastSyncTime = System.currentTimeMillis();
}
else if (runAsService == true && CapoApplication.isServer() == false)
{
//see if it's time to run the client sync
if (System.currentTimeMillis() - lastSyncTime > CapoApplication.getConfiguration().getLongValue(Preferences.DEFAULT_CLIENT_SYNC_INTERVAL))
{
//run the standard update and identity scripts. This is not configurable, because we want to make sure that the client always checks and for the basics.
HashMap<String, String> sessionHashMap = new HashMap<String, String>();
CapoConnection capoConnection = new CapoConnection();
((CapoClient)CapoApplication.getApplication()).runUpdateRequest(capoConnection, sessionHashMap);
//capoConnection.close();
if (WrapperManager.isShuttingDown()) //bail out of thread and client sync if we're restarting
{
break;
}
//capoConnection = new CapoConnection();
((CapoClient)CapoApplication.getApplication()).runIdentityRequest(capoConnection, sessionHashMap);
((CapoClient)CapoApplication.getApplication()).runTasksUpdateRequest(capoConnection, sessionHashMap);
((CapoClient)CapoApplication.getApplication()).runDefaultRequest(capoConnection, sessionHashMap);
capoConnection.close();
lastSyncTime = System.currentTimeMillis();
}
}
//===============================================END CLIENT SYNC===============================================================
//===============================================BEGIN OVERALL TASK RUN===============================================================
ResourceDescriptor taskDirResourceDescriptor = capoDataManager.getResourceDirectory(Preferences.TASK_DIR.toString());
ResourceDescriptor taskStatusDocumentResourceDescriptor = taskDirResourceDescriptor.getChildResourceDescriptor(null, "task-status.xml");
taskStatusDocumentResourceDescriptor.getResourceMetaData(null).exists();
Document taskStatusDocument = null;
try
{
taskStatusDocument = CapoApplication.getDocumentBuilder().parse(taskStatusDocumentResourceDescriptor.getInputStream(null));
}
catch (Exception saxParseException)
{
taskStatusDocument = CapoApplication.getDefaultDocument("task-status.xml");
}
//don't let the document change while we are running
List<ResourceDescriptor> taskResourceDescriptorList = CapoApplication.getDataManager().findDocuments(capoDataManager.getResourceDirectory(Preferences.TASK_DIR.toString()));
for (ResourceDescriptor resourceDescriptor : taskResourceDescriptorList)
{
//skip our status document
if (resourceDescriptor.getLocalName().equals("task-status.xml"))
{
resourceDescriptor.release(null);
continue; //loop to next file
}
Document taskManagerDocument = null;
try
{
taskManagerDocument = documentBuilder.parse(resourceDescriptor.getInputStream(null));
}
catch (SAXParseException parseException)
{
CapoApplication.logger.log(Level.WARNING, "Skipping unparsable task '"+resourceDescriptor.getLocalName()+"'");
resourceDescriptor.release(null);
continue; //loop to next file
}
NodeList tasksNodeList = XPath.selectNodes(taskManagerDocument, "//server:task");
String taskURI = resourceDescriptor.getLocalName();
String resourceMD5 = resourceDescriptor.getResourceMetaData(null).getMD5();
//if we didn't find anything see if we are referring to a module
if (tasksNodeList.getLength() == 0)
{
tasksNodeList = loadModule(tasksNodeList,taskManagerDocument);
}
//===============================================BEGIN INDIVIDUAL TASK RUN===============================================================
for (int monitorInfoIndex = 0; monitorInfoIndex < tasksNodeList.getLength(); monitorInfoIndex++)
{
//for each resource monitor get the resource that it is monitoring
Element taskElement = (Element) tasksNodeList.item(monitorInfoIndex);
Document taskDocument = documentBuilder.newDocument();
taskDocument.appendChild(taskDocument.importNode(taskElement, true));
taskElement = taskDocument.getDocumentElement();
String name = taskElement.getAttribute(Attributes.name.toString());
//forgot name attribute, use filename if there's only one thing here
if (name.trim().isEmpty())
{
if (tasksNodeList.getLength() == 1)
{
name = resourceDescriptor.getLocalName();
CapoApplication.logger.log(Level.WARNING, "Defaulting task name to "+name+" task in '"+resourceDescriptor.getLocalName()+"' due to missing name attribute");
}
else
{
CapoApplication.logger.log(Level.WARNING, "Skipping task in '"+resourceDescriptor.getLocalName()+"' due to missing name attribute");
resourceDescriptor.release(null);
continue; //loop to next task
}
}
//find the corresponding task in the status document.
Element taskStatusElement = (Element) XPath.selectSingleNode(taskStatusDocument, "//server:task[@name = '"+name+"']");
//init new task status element for unknown task
if (taskStatusElement == null)
{
taskStatusElement = taskStatusDocument.createElement("server:task");
taskStatusElement.setAttribute(Attributes.name.toString(), name);
//make sure we always know where this task came from so we can cull the file
taskStatusElement.setAttribute(Attributes.taskURI.toString(), taskURI);
taskStatusDocument.getDocumentElement().appendChild(taskStatusElement);
//clear out the persisted values since this is a new or replaced task
taskConcurrentHashMap.remove(name);
}
if (taskStatusElement.getAttribute("ACTION").equals("IGNORE") && taskStatusElement.getAttribute(Attributes.MD5.toString()).equals(resourceMD5))
{
continue; //loop to next task
}
//walk our persisted attributes and stick them back in the task element
NamedNodeMap namedNodeMap = taskStatusElement.getAttributes();
for(int index = 0; index < namedNodeMap.getLength(); index++)
{
String attributeName = namedNodeMap.item(index).getNodeName();
taskElement.setAttribute(attributeName,taskStatusElement.getAttribute(attributeName));
}
//===============================================BEGIN LIFECYCLE VAR INITIALIZATION===============================================================
String lastExecutionTimeValue = taskElement.getAttribute(Attributes.lastExecutionTime.toString());
String lastAccessTimeValue = taskElement.getAttribute(Attributes.lastAccessTime.toString());
String executionIntervalValue = taskElement.getAttribute(Attributes.executionInterval.toString());
String lifeSpanValue = taskElement.getAttribute(Attributes.lifeSpan.toString());
long executionInterval = 0l;
long lastExecutionTime = 0l;
long lastAccessTime = 0l;
long lifeSpan = 0l;
if (lifeSpanValue.matches("\\d+"))
{
lifeSpan = Long.parseLong(lifeSpanValue);
}
else
{
lifeSpan = CapoApplication.getConfiguration().getLongValue(Preferences.TASK_DEFAULT_LIFESPAN);
}
if (lastAccessTimeValue.matches("\\d+"))
{
lastAccessTime = Long.parseLong(lastAccessTimeValue);
}
else
{
lastAccessTime = System.currentTimeMillis();
}
if (executionIntervalValue.matches("\\d+"))
{
executionInterval = Long.parseLong(executionIntervalValue);
}
if (lastExecutionTimeValue.matches("\\d+"))
{
lastExecutionTime = Long.parseLong(lastExecutionTimeValue);
}
//===============================================END LIFECYCLE VAR INITIALIZATION===============================================================
//===============================================BEGIN ORPHAN CHECK===============================================================
//check to see if task is orphaned
if (this.lastRunTime > (lastAccessTime + lifeSpan)) //use last run time so that everything is on equal footing, as opposed to having tasks that take too long and cause other tasks to expire before it's too late
{
String orpanAction = CapoApplication.getConfiguration().getValue(Preferences.TASK_DEFAULT_ORPHAN_ACTION);
if (taskElement.hasAttribute(Attributes.orpanAction.toString()))
{
orpanAction = taskElement.getAttribute(Attributes.orpanAction.toString());
}
if(orpanAction.equals("DELETE"))
{
CapoApplication.logger.warning("task '"+name+"' appears to be orphaned, deleteing. lastAccessTime = "+lastAccessTime+" lrt="+this.lastRunTime+" ls="+lifeSpan+" delta ="+(lastAccessTime + lifeSpan - this.lastRunTime));
resourceDescriptor.performAction(null, Action.DELETE);
//mark this task for deletion, by server
//if we're running on the server, then just delete it.
if (taskElement.getAttribute("local").equalsIgnoreCase("true"))
{
taskStatusElement.getParentNode().removeChild(taskStatusElement);
}
else //otherwise we've got to wait for it to sync back up to the server
{
taskStatusElement.setAttribute("ACTION", Action.DELETE.toString());
}
}
else
{
CapoApplication.logger.warning("task '"+taskElement.getAttribute(Attributes.name.toString())+"' appears to be orphaned, ignoreing. lastAccessTime = "+lastAccessTime);
}
resourceDescriptor.release(null);
continue; //loop to next task
}
//===============================================END ORPHAN CHECK===============================================================
//check for single run task
if (executionInterval == 0l && lastExecutionTime > 0l)
{
//this was only to run once
String orpanAction = CapoApplication.getConfiguration().getValue(Preferences.TASK_DEFAULT_ORPHAN_ACTION);
if (taskElement.hasAttribute(Attributes.orpanAction.toString()))
{
orpanAction = taskElement.getAttribute(Attributes.orpanAction.toString());
}
taskStatusElement.setAttribute("ACTION", orpanAction);
resourceDescriptor.release(null);
continue; //loop to next task
}
//check to see if it's time for this task to run
if (System.currentTimeMillis() < lastExecutionTime + executionInterval)
{
//not time to run yet.
resourceDescriptor.release(null);
continue;
}
//===============================================BEGIN PROCESSING OF TASK===============================================================
//Tasks get run locally
LocalRequestProcessor localRequestProcessor = new LocalRequestProcessor();
HashMap<String, String> variableHashMap = null; //place to store memory persisted values from repeated task runs
//see if this is a task we've never heard of
if (taskConcurrentHashMap.containsKey(taskElement.getAttribute(Attributes.name.toString())) == true)
{
//We've run this one before, so get any memory persisted variables from the previous run
variableHashMap = taskConcurrentHashMap.get(taskElement.getAttribute(Attributes.name.toString()));
//walk our previous variables and stick them back in the task document
namedNodeMap = taskElement.getAttributes();
for(int index = 0; index < namedNodeMap.getLength(); index++)
{
String attributeName = namedNodeMap.item(index).getLocalName();
if (variableHashMap.containsKey(attributeName))
{
//set attribute for the run
taskElement.setAttribute(attributeName, variableHashMap.get(attributeName));
//set attributes that need to be persisted
taskStatusElement.setAttribute(attributeName, variableHashMap.get(attributeName));
}
}
}
//RUN TASK
try
{
GroupElement processedGroupElement = localRequestProcessor.process(taskElement,variableHashMap);
processedGroupElement.getGroup();
//after running save any variables for the next run
taskConcurrentHashMap.put(taskElement.getAttribute(Attributes.name.toString()), processedGroupElement.getGroup().getVariableHashMap());
}
catch (Exception exception)
{
taskStatusElement.setAttribute("ACTION", "IGNORE");
taskStatusElement.setAttribute("EXCEPTION", exception.getMessage());
}
//update task status
taskStatusElement.setAttribute(Attributes.lastExecutionTime.toString(), System.currentTimeMillis()+"");
taskStatusElement.setAttribute(Attributes.MD5.toString(), resourceMD5);
} //===============================================END INDIVIDUAL TASK RUN===============================================================
//cleanup file resource descriptor
resourceDescriptor.release(null);
//we're done running, so see if a task asked us to restart
if (taskManagerState.ordinal() >= ApplicationState.STOPPING.ordinal())
{
CapoServer.logger.log(Level.INFO, "Ignoring any remaining tasks due to STOP request");
break;
}
}//===============================================END OVERALL TASKS RUN===============================================================
//after running it's time to store all of the status information
updateTasksDocument(taskStatusDocumentResourceDescriptor,taskStatusDocument);
} //END MAIN TRY BLOCK
catch (Exception exception)
{
CapoServer.logger.log(Level.WARNING, "error processing tasks",exception);
}
finally //make sure we always unlock things if we're bailing out
{
while(lock.isHeldByCurrentThread())
{
lock.unlock(); //unlock everything since we;re now done and about to sleep or finish
}
}
//check to see if we need to sleep then loop because we're a service, or bail out because we've been interrupted.
if (runAsService == true && taskManagerState.ordinal() < ApplicationState.STOPPING.ordinal())
{
try
{
//sleep for a while, then do it all over again
Thread.sleep(CapoApplication.getConfiguration().getLongValue(Preferences.TASK_INTERVAL));
}
catch (InterruptedException interruptedException)
{
//someone asked us to stop sleeping, not an error
}
}
else //this is not a service or we were inturrupted, so exit the thread.
{
break;
}
}
this.taskManagerState = ApplicationState.STOPPED;
//we're the last man standing, if we're not a service, so let the wrapper manager know to shutdown.
//This is a little cleaner than just exiting.
//We DON'T want to do this if we are testing, as it will kill the JVM
if(runAsService == false && CapoApplication.getConfiguration().getBooleanValue(Preferences.QUICK_SHUTDOWN) == false)
{
//execute controlled shutdown
WrapperManager.stopAndReturn(0);
}
}
private NodeList loadModule(NodeList tasksNodeList, Document taskManagerDocument) throws Exception
{
Element moduleElement = ModuleProvider.getModuleElement(taskManagerDocument.getDocumentElement().getLocalName());
if (moduleElement != null && moduleElement.getLocalName().equals("task"))
{
//verify name attribute on module element
if (moduleElement.hasAttribute("name") == false)
{
moduleElement.setAttribute("name", taskManagerDocument.getDocumentElement().getLocalName());
}
//copy over attributes from mod declaration to task element
NamedNodeMap attributeNodeMap = taskManagerDocument.getDocumentElement().getAttributes();
for(int index = 0; index < attributeNodeMap.getLength(); index++)
{
moduleElement.setAttribute(attributeNodeMap.item(index).getLocalName(), attributeNodeMap.item(index).getNodeValue());
}
//copy over child elements to first element of temp task
NodeList childNodes = taskManagerDocument.getDocumentElement().getChildNodes();
boolean isFirstChild = true;
Element moduleDataElement = moduleElement.getOwnerDocument().createElement("server:moduleData");
moduleDataElement.setAttribute("DoNotProcess", "true");
for(int index = 0; index < childNodes.getLength(); index++)
{
Node childNode = childNodes.item(index);
if (childNode instanceof Element)
{
if (isFirstChild)
{
moduleElement.appendChild(moduleDataElement);
}
moduleDataElement.appendChild(moduleElement.getOwnerDocument().importNode(childNode, true));
}
}
//reselect to make a new list with one item
tasksNodeList = XPath.selectNodes(moduleDataElement, "//server:task");
}
return tasksNodeList;
}
//DONE
private void updateTasksDocument(ResourceDescriptor resourceDescriptor, Document taskDocument) throws Exception
{
tasksDocumentUpdaterThread.add(resourceDescriptor,taskDocument);
if (runAsService == false)
{
tasksDocumentUpdaterThread.run();
}
}
// /**
// * TODO this should be configurable, as opposed to hard coded
// * @param id
// * @param lastAccessTime - should generally be null
// * @param pollInterval - 15 seconds aka 15000
// * @param expirationInterval - 3 days aka 259200000
// * @return
// * @throws Exception
// */
// private Element createResourceMonitorElement(ResourceType resourceType, String resourceID, Long lastAccessTime, Long pollInterval, Long expirationInterval) throws Exception
// {
//
//
// Element resourceMonitorElement = taskManagerDocument.createElementNS(null,TASK_ELEMENT_NAME);
// if (resourceID == null || resourceID.trim().isEmpty() == true)
// {
// throw new NullPointerException("Missing MonitorInfo ID");
// }
// else
// {
// resourceMonitorElement.setAttribute("ID", resourceID);
// }
//
// if (resourceType == null)
// {
// throw new NullPointerException("Missing Resource Monitor Type");
// }
// else
// {
// resourceMonitorElement.setAttribute("type", resourceType.getName());
// }
//
// if (lastAccessTime != null)
// {
// resourceMonitorElement.setAttribute("lastAccessTime", lastAccessTime.toString());
// }
// else
// {
// resourceMonitorElement.setAttribute("lastAccessTime", System.currentTimeMillis()+"");
// }
//
// if (pollInterval != null)
// {
// resourceMonitorElement.setAttribute("pollInterval", pollInterval.toString());
// }
// else
// {
// resourceMonitorElement.setAttribute("pollInterval", "15000");
// }
//
// if (expirationInterval != null)
// {
// resourceMonitorElement.setAttribute("expirationInterval", expirationInterval.toString());
// }
// else
// {
// resourceMonitorElement.setAttribute("expirationInterval", "259200000");
// }
//
// Element resourceElementDeclaration = taskManagerDocument.createElementNS(null, "ResourceElement");
// resourceElementDeclaration.setAttribute("name", resourceID);
// resourceElementDeclaration.setAttribute("uri", resourceID);
// resourceElementDeclaration.setAttribute("lifeCycle", LifeCycle.REF.toString());
// Element resourceDependentsElement = taskManagerDocument.createElementNS(null, "ResourceDependents");
// Element resourceListenersElement = taskManagerDocument.createElementNS(null, "ResourceListeners");
// resourceMonitorElement.appendChild(resourceElementDeclaration);
// resourceMonitorElement.appendChild(resourceDependentsElement);
// resourceMonitorElement.appendChild(resourceListenersElement);
//
// ResourceElement resourceElement = new ResourceElement(); //TODO absolute bull shit
// resourceElement.init(resourceElementDeclaration, null, null, null); //TODO FIXME!!!! too much BS here, making it undependable for use on the client side etc.
//
//
//
// ResourceDescriptor fileResourceDescriptor = capoDataManager.getResourceDescriptor(resourceElement,resourceID);
// ResourceParameterBuilder resourceParameterBuilder = new ResourceParameterBuilder();
// resourceParameterBuilder.addAll(resourceElementDeclaration);
// fileResourceDescriptor.open(null);
// Element fileDescriptorElement = XMLSerializer.export(taskManagerDocument,fileResourceDescriptor);
// resourceElementDeclaration.appendChild(fileDescriptorElement);
//
//
//
// return resourceMonitorElement;
// }
// /**
// * This returns an element of unknown type that corresponds to /ResourceMonitors/ResourceMonitor[@ID = 'resourceID']/ResourceDependents/Dependent[@ID = 'dependantID']/*[1]
// * @param resourceID
// * @param dependantID
// * @return
// * @throws Exception
// */
// public Element getDependentDataElement(String resourceID, String dependantID) throws Exception
// {
// Element dependantDataElement = null;
// Element dependantElement = getDependentElement(resourceID, dependantID);
// if (dependantElement != null)
// {
// dependantDataElement = (Element) XPath.selectSingleNode(dependantElement, "./*[1]");
// }
//
// return dependantDataElement;
// }
//
// private Element getResourceMonitorElement(String resourceID) throws Exception
// {
// Element resourceMonitorElement = (Element) XPath.selectSingleNode(taskManagerDocument, "/ResourceMonitors/ResourceMonitor[@ID = '"+resourceID+"']");
// if (resourceMonitorElement != null)
// {
// resourceMonitorElement.setAttribute("lastAccessTime", System.currentTimeMillis()+"");
// updateTasksDocument();
// }
// return resourceMonitorElement;
// }
//
// private Element getDependentElement(String resourceID, String dependantID) throws Exception
// {
// Element dependantElement = null;
// Element resourceMonitorElement = getResourceMonitorElement(resourceID);
// if (resourceMonitorElement != null)
// {
// dependantElement = (Element) XPath.selectSingleNode(resourceMonitorElement, "ResourceDependents/Dependent[@ID = '"+dependantID+"'][1]");
// if (dependantElement != null)
// {
// dependantElement.setAttribute("lastAccessTime", System.currentTimeMillis()+"");
// updateTasksDocument();
// }
// }
//
// return dependantElement;
// }
//
/**
* taskName
* clientID - can be null
* dependentID
* element to be stored
*/
public void setTaskDataElement(String taskID, String clientID,String dependantID, Element someElement) throws Exception
{
lock.lock();
try
{
ResourceDescriptor taskDocumentResourceDescriptor = capoDataManager.findDocumentResourceDescriptor(taskID, clientID, Preferences.TASK_DIR);
if (taskDocumentResourceDescriptor != null && taskDocumentResourceDescriptor.getResourceMetaData(null).exists() == true)
{
Document taskDocument = CapoApplication.getDocumentBuilder().parse(taskDocumentResourceDescriptor.getInputStream(null));
Node taskDataNode = XPath.selectSingleNode(taskDocument, "//server:taskData");
if (taskDataNode == null)
{
taskDataNode = taskDocument.createElement("server:taskData");
taskDocument.appendChild(taskDataNode);
}
Node taskDependentNode = XPath.selectSingleNode(taskDataNode, "server:taskDataItem[@dependantID = '"+dependantID+"']");
if (taskDependentNode == null)
{
taskDependentNode = taskDocument.createElement("server:taskDataItem");
((Element) taskDependentNode).setAttribute("dependantID",dependantID);
taskDataNode.appendChild(taskDataNode);
taskDependentNode.appendChild(taskDocument.importNode(someElement,true));
}
XPath.removeNodes(taskDependentNode,"./*");
taskDependentNode.appendChild(taskDocument.importNode(someElement,true));
updateTasksDocument(taskDocumentResourceDescriptor, taskDocument);
}
}
finally
{
lock.unlock();
}
}
public Element getTaskDataElement(String taskID, String clientID,String dependantID) throws Exception
{
lock.lock();
try
{
ResourceDescriptor taskDocumentResourceDescriptor = capoDataManager.findDocumentResourceDescriptor(taskID, clientID, Preferences.TASK_DIR);
if (taskDocumentResourceDescriptor != null && taskDocumentResourceDescriptor.getResourceMetaData(null).exists() == true)
{
Document taskDocument = CapoApplication.getDocumentBuilder().parse(taskDocumentResourceDescriptor.getInputStream(null));
Node taskDataNode = XPath.selectSingleNode(taskDocument, "//server:taskData/server:taskDataItem[@dependantID = '"+dependantID+"']/*");
return (Element) taskDataNode;
}
return null;
}
finally
{
lock.unlock();
}
}
// private Element getResourceDependantsElement(String resourceID) throws Exception
// {
// Element resourceDependantsElement = null;
// Element resourceMonitorElement = getResourceMonitorElement(resourceID);
// if (resourceMonitorElement != null)
// {
// resourceDependantsElement = (Element) XPath.selectSingleNode(resourceMonitorElement, "ResourceDependents");
// }
// return resourceDependantsElement;
// }
//
// private Element createResourceDependentElement(String dependantID)
// {
// Element resourceDependentElement = taskManagerDocument.createElementNS(null, "Dependent");
// resourceDependentElement.setAttribute("ID", dependantID);
// resourceDependentElement.setAttribute("lastAccessTime", System.currentTimeMillis()+"");
// resourceDependentElement.setAttribute("removeOnChange", "true");
// return resourceDependentElement;
// }
/**
* This will add a monitor thread to the document if it doesn't already exists, or has changed in some way.
* @param name
* @param md5
* @param taskElement
* @throws Exception
*/
public void setTask(String name, String md5, TaskElement taskElement,String clientID) throws Exception
{
lock.lock();
try
{
ResourceDescriptor taskDocumentResourceDescriptor = capoDataManager.findDocumentResourceDescriptor(name, clientID, Preferences.TASK_DIR);
Document taskDocument = null;
//create a new task file
if (taskDocumentResourceDescriptor == null)
{
if (clientID != null)
{
ResourceDescriptor clientResourceDescriptor = capoDataManager.getResourceDescriptor(null, "clients:"+clientID);
//see if we have a child filesystem
if (clientResourceDescriptor.getResourceMetaData(null).exists() == true)
{
ResourceDescriptor relaventChildResourceDescriptor = clientResourceDescriptor.getChildResourceDescriptor(null,CapoApplication.getConfiguration().getValue(Preferences.TASK_DIR));
if (relaventChildResourceDescriptor != null && relaventChildResourceDescriptor.getResourceMetaData(null).exists() == true)
{
//search the client filesystem
taskDocumentResourceDescriptor = relaventChildResourceDescriptor.getChildResourceDescriptor(taskElement, name+".xml");
}
}
}
else //this is for server side stuff
{
ResourceDescriptor tasksResourceDescriptor = CapoApplication.getDataManager().getResourceDirectory(Preferences.TASK_DIR.toString());
taskDocumentResourceDescriptor = tasksResourceDescriptor.getChildResourceDescriptor(taskElement, name+".xml");
}
taskDocument = documentBuilder.newDocument();
}
else if (taskDocumentResourceDescriptor.getResourceMetaData(null).exists() == false)
{
taskDocument = documentBuilder.newDocument();
}
else
{
taskDocument = documentBuilder.parse(taskDocumentResourceDescriptor.getInputStream(null));
}
//check to see if we already have this monitor
Element previousMonitorElement = (Element) XPath.selectSingleNode(taskDocument, "//server:task[@name = '"+name+"']");
if (previousMonitorElement != null)
{
//see if the md5s match
if (previousMonitorElement.getAttribute("md5").equals(md5))
{
CapoApplication.logger.log(Level.FINE, "Task hasn't changed: "+name);
previousMonitorElement.setAttribute(Attributes.lastAccessTime.toString(), System.currentTimeMillis()+"");
updateTasksDocument(taskDocumentResourceDescriptor,taskDocument);
return;
}
else
{
//remove the old element
CapoApplication.logger.log(Level.INFO, "Removing Old Task: "+name);
previousMonitorElement.getParentNode().removeChild(previousMonitorElement);
}
}
CapoApplication.logger.log(Level.INFO, "Updating Task: "+name);
//if we made it this far add the element
Element monitorElementDeclaration = (Element) taskDocument.importNode(taskElement.getControlElementDeclaration(), true);
monitorElementDeclaration.setAttribute("md5", md5);
monitorElementDeclaration.setAttribute(Attributes.lastAccessTime.toString(), System.currentTimeMillis()+"");
taskConcurrentHashMap.remove(name);
taskDocument.appendChild(monitorElementDeclaration);
updateTasksDocument(taskDocumentResourceDescriptor,taskDocument);
}
finally
{
lock.unlock();
}
}
public long getLastRunTime()
{
return lastRunTime;
}
public ApplicationState getTaskManagerState()
{
return taskManagerState;
}
}