org.dspace.curate.TaskResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dspace-api Show documentation
Show all versions of dspace-api Show documentation
DSpace core data model and service APIs.
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.curate;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.core.factory.CoreServiceFactory;
import org.dspace.services.factory.DSpaceServicesFactory;
/**
* TaskResolver takes a logical name of a curation task and attempts to deliver
* a suitable implementation object. Supported implementation types include:
* (1) Classpath-local Java classes configured and loaded via PluginService.
* (2) Local script-based tasks, viz. coded in any scripting language whose
* runtimes are accessible via the JSR-223 scripting API. This really amounts
* to the family of dynamic JVM languages: JRuby, Jython, Groovy, Javascript, etc
* Note that the requisite jars and other resources for these languages must be
* installed in the DSpace instance for them to be used here.
* Further work may involve remote URL-loadable code, etc.
*
* Scripted tasks are managed in a directory configured with the
* dspace/config/modules/curate.cfg property "script.dir". A catalog of
* scripted tasks named 'task.catalog" is kept in this directory.
* Each task has a 'descriptor' property with value syntax:
* {@code ||}
* An example property:
*
* {@code linkchecker = ruby|rubytask.rb|LinkChecker.new}
*
* This descriptor means that a 'ruby' script engine will be created,
* a script file named 'rubytask.rb' in the directory {@code } will be
* loaded and the resolver will expect an evaluation of 'LinkChecker.new' will
* provide a correct implementation object.
*
* Script files may embed their descriptors to facilitate deployment.
* To accomplish this, a script must include the descriptor string with syntax:
* {@code $td=} somewhere on a comment line. for example:
*
* {@code My descriptor $td=ruby|rubytask.rb|LinkChecker.new}
*
* For portability, the {@code } component may be omitted in this context.
* Thus, {@code $td=ruby||LinkChecker.new} will be expanded to a descriptor
* with the name of the embedding file.
*
* @author richardrodgers
*/
public class TaskResolver {
// logging service
private static final Logger log = LogManager.getLogger(TaskResolver.class);
// base directory of task scripts & catalog name
protected static final String CATALOG = "task.catalog";
protected final String scriptDir;
// catalog of script tasks
protected Properties catalog;
public TaskResolver() {
scriptDir = DSpaceServicesFactory.getInstance().getConfigurationService().getProperty("curate.script.dir");
}
/**
* Installs a task script. Succeeds only if script:
* (1) exists in the configured script directory and
* (2) contains a recognizable descriptor in a comment line.
* If script lacks a descriptor, it may still be installed
* by manually invoking addDescriptor
.
*
* @param taskName logical name of task to associate with script
* @param fileName name of file containing task script
* @return true if script installed, false if installation failed
*/
public boolean installScript(String taskName, String fileName) {
// Can we locate the file in the script directory?
File script = new File(scriptDir, fileName);
if (script.exists()) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(script, StandardCharsets.UTF_8));
String line = null;
while ((line = reader.readLine()) != null) {
if (line.startsWith("#") && line.indexOf("$td=") > 0) {
String desc = line.substring(line.indexOf("$td=") + 4);
// insert relFilePath if missing
String[] tokens = desc.split("\\|");
if (tokens[1].length() == 0) {
desc = tokens[0] + "|" + fileName + "|" + tokens[2];
}
addDescriptor(taskName, desc);
return true;
}
}
} catch (IOException ioE) {
log.error("Error reading task script: " + fileName);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ioE) {
log.error("Error closing task script: " + fileName);
}
}
}
} else {
log.error("Task script: " + fileName + "not found in: " + scriptDir);
}
return false;
}
/**
* Adds a task descriptor property and flushes catalog to disk.
*
* @param taskName logical task name
* @param descriptor descriptor for task
*/
public void addDescriptor(String taskName, String descriptor) {
loadCatalog();
catalog.put(taskName, descriptor);
Writer writer = null;
try {
writer = new FileWriter(new File(scriptDir, CATALOG), StandardCharsets.UTF_8);
catalog.store(writer, "do not edit");
} catch (IOException ioE) {
log.error("Error saving scripted task catalog: " + CATALOG);
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException ioE) {
log.error("Error closing scripted task catalog: " + CATALOG);
}
}
}
}
/**
* Returns a task implementation for a given task name,
* or null
if no implementation could be obtained.
*
* @param taskName logical task name
* @return task
* an object that implements the CurationTask interface
*/
public ResolvedTask resolveTask(String taskName) {
CurationTask ctask = (CurationTask) CoreServiceFactory.getInstance().getPluginService()
.getNamedPlugin(CurationTask.class, taskName);
if (ctask != null) {
return new ResolvedTask(taskName, ctask);
}
// maybe it is implemented by a script?
loadCatalog();
String scriptDesc = catalog.getProperty(taskName);
if (scriptDesc != null) {
String[] tokens = scriptDesc.split("\\|");
// first descriptor token is name ('alias') of scripting engine
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName(tokens[0]);
if (engine != null) {
// see if we can locate the script file and load it
// the second token is the relative path to the file
File script = new File(scriptDir, tokens[1]);
if (script.exists()) {
try {
Reader reader = new FileReader(script, StandardCharsets.UTF_8);
engine.eval(reader);
reader.close();
// third token is the constructor expression for the class
// implementing CurationTask interface
ScriptedTask stask = (ScriptedTask) engine.eval(tokens[2]);
return new ResolvedTask(taskName, stask);
} catch (FileNotFoundException fnfE) {
log.error("Script: '" + script.getName() + "' not found for task: " + taskName);
} catch (IOException ioE) {
log.error("Error loading script: '" + script.getName() + "'");
} catch (ScriptException scE) {
log.error("Error evaluating script: '" + script.getName() + "' msg: " + scE.getMessage());
}
} else {
log.error("No script: '" + script.getName() + "' found for task: " + taskName);
}
} else {
log.error("Script engine: '" + tokens[0] + "' is not installed");
}
}
return null;
}
/**
* Loads catalog of descriptors for tasks if not already loaded
*/
protected void loadCatalog() {
if (catalog == null) {
catalog = new Properties();
File catalogFile = new File(scriptDir, CATALOG);
if (catalogFile.exists()) {
try {
Reader reader = new FileReader(catalogFile, StandardCharsets.UTF_8);
catalog.load(reader);
reader.close();
} catch (IOException ioE) {
log.error("Error loading scripted task catalog: " + CATALOG);
}
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy