use of org.opencastproject.execute.api.ExecuteException in project opencast by opencast.
the class ExecuteServiceImpl method runCommand.
private String runCommand(List<String> command, File outFile, Type expectedType) throws ExecuteException {
Process p = null;
int result = 0;
try {
logger.info("Running command {}", command.get(0));
logger.debug("Starting subprocess {} with arguments {}", command.get(0), StringUtils.join(command.subList(1, command.size()), ", "));
ProcessBuilder pb = new ProcessBuilder(command);
pb.redirectErrorStream(true);
p = pb.start();
result = p.waitFor();
logger.debug("Command {} finished with result {}", command.get(0), result);
if (result == 0) {
// Read the command output
if (outFile != null) {
if (outFile.isFile()) {
URI newURI = workspace.putInCollection(ExecuteService.COLLECTION, outFile.getName(), new FileInputStream(outFile));
if (outFile.delete()) {
logger.debug("Deleted the local copy of the encoded file at {}", outFile.getAbsolutePath());
} else {
logger.warn("Unable to delete the encoding output at {}", outFile.getAbsolutePath());
}
return MediaPackageElementParser.getAsXml(MediaPackageElementBuilderFactory.newInstance().newElementBuilder().elementFromURI(newURI, expectedType, null));
} else {
throw new ExecuteException("Expected output file does not exist: " + outFile.getAbsolutePath());
}
}
return "";
} else {
// 'Scanner' reads tokens delimited by an specific character (set).
// By telling a Scanner to use the 'beginning of the input boundary' character as delimiter, which of course
// will never find, yields the whole String as the next token.
String line;
try (Scanner scanner = new Scanner(p.getInputStream())) {
scanner.useDelimiter("\\A");
line = scanner.next();
} catch (NoSuchElementException e) {
line = "";
}
throw new ExecuteException(String.format("Process %s returned error code %d with this output:\n%s", command.get(0), result, line.trim()));
}
} catch (InterruptedException e) {
throw new ExecuteException("The executor thread has been unexpectedly interrupted", e);
} catch (IOException e) {
// Only log the first argument, the executable, as other arguments may contain sensitive values
// e.g. MySQL password/user, paths, etc. that should not be shown to caller
logger.error("Could not start subprocess {}", command.get(0));
throw new ExecuteException("Could not start subprocess: " + command.get(0), e);
} catch (UnsupportedElementException e) {
throw new ExecuteException("Couldn't create a new MediaPackage element of type " + expectedType.toString(), e);
} catch (ConfigurationException e) {
throw new ExecuteException("Couldn't instantiate a new MediaPackage element builder", e);
} catch (MediaPackageException e) {
throw new ExecuteException("Couldn't serialize a new Mediapackage element of type " + expectedType.toString(), e);
} finally {
IoSupport.closeQuietly(p);
}
}
use of org.opencastproject.execute.api.ExecuteException in project opencast by opencast.
the class ExecuteServiceImpl method doProcess.
/**
* Does the actual processing, given a mediapackage (Execute Once WOH)
*
* @param arguments
* The list containing the program and its arguments
* @param mp
* MediaPackage used in the operation
* @param outFileName
* The name of the resulting file
* @param expectedType
* The expected element type
* @return A {@code String} containing the command output
* @throws ExecuteException
* if some internal error occurred
*/
protected String doProcess(List<String> arguments, MediaPackage mp, String outFileName, Type expectedType) throws ExecuteException {
String params = arguments.remove(1);
File outFile = null;
MediaPackageElement[] elementsByFlavor = null;
try {
if (outFileName != null) {
// FIXME : Find a better way to place the output File
File firstElement = workspace.get(mp.getElements()[0].getURI());
outFile = new File(firstElement.getParentFile(), outFileName);
}
// Get the substitution pattern.
// The following pattern matches, any construct with the form
// #{name}
// , where 'name' is the value of a certain property. It is stored in the backreference group 1.
// Optionally, expressions can take a parameter, like
// #{name(parameter)}
// , where 'parameter' is the name of a certain parameter.
// If specified, 'parameter' is stored in the group 2. Otherwise it's null.
// Both name and parameter match any character sequence that does not contain {, }, ( or ) .
Pattern pat = Pattern.compile("#\\{([^\\{\\}\\(\\)]+)(?:\\(([^\\{\\}\\(\\)]+)\\))?\\}");
// Substitute the appearances of the patterns with the actual absolute paths
Matcher matcher = pat.matcher(params);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
// group(1) = property. group(2) = (optional) parameter
if (matcher.group(1).equals("id")) {
matcher.appendReplacement(sb, mp.getIdentifier().toString());
} else if (matcher.group(1).equals("flavor")) {
elementsByFlavor = mp.getElementsByFlavor(MediaPackageElementFlavor.parseFlavor(matcher.group(2)));
if (elementsByFlavor.length == 0)
throw new ExecuteException("No elements in the MediaPackage match the flavor '" + matcher.group(2) + "'.");
if (elementsByFlavor.length > 1)
logger.warn("Found more than one element with flavor '{}'. Using {} by default...", matcher.group(2), elementsByFlavor[0].getIdentifier());
File elementFile = workspace.get(elementsByFlavor[0].getURI());
matcher.appendReplacement(sb, elementFile.getAbsolutePath());
} else if (matcher.group(1).equals("out")) {
matcher.appendReplacement(sb, outFile.getAbsolutePath());
} else if (properties.get(matcher.group(1)) != null) {
matcher.appendReplacement(sb, (String) properties.get(matcher.group(1)));
} else if (bundleContext.getProperty(matcher.group(1)) != null) {
matcher.appendReplacement(sb, bundleContext.getProperty(matcher.group(1)));
}
}
matcher.appendTail(sb);
params = sb.toString();
} catch (IllegalArgumentException e) {
throw new ExecuteException("Tag 'flavor' must specify a valid MediaPackage element flavor.", e);
} catch (NotFoundException e) {
throw new ExecuteException("The element '" + elementsByFlavor[0].getURI().toString() + "' does not exist in the workspace.", e);
} catch (IOException e) {
throw new ExecuteException("Error retrieving MediaPackage element from workspace: '" + elementsByFlavor[0].getURI().toString() + "'.", e);
}
arguments.addAll(splitParameters(params));
return runCommand(arguments, outFile, expectedType);
}
use of org.opencastproject.execute.api.ExecuteException in project opencast by opencast.
the class ExecuteServiceImpl method execute.
/**
* {@inheritDoc}
*
* @see org.opencastproject.execute.api.ExecuteService#execute(java.lang.String, java.lang.String,
* org.opencastproject.mediapackage.MediaPackageElement, java.lang.String,
* org.opencastproject.mediapackage.MediaPackageElement.Type, float)
* @throws IllegalArgumentException
* if the input arguments are incorrect
* @throws ExecuteException
* if an internal error occurs
*/
@Override
public Job execute(String exec, String params, MediaPackageElement inElement, String outFileName, Type expectedType, float load) throws ExecuteException, IllegalArgumentException {
logger.debug("Creating Execute Job for command: {}", exec);
if (StringUtils.isBlank(exec))
throw new IllegalArgumentException("The command to execute cannot be null");
if (StringUtils.isBlank(params))
throw new IllegalArgumentException("The command arguments cannot be null");
if (inElement == null)
throw new IllegalArgumentException("The input MediaPackage element cannot be null");
outFileName = StringUtils.trimToNull(outFileName);
if ((outFileName == null) && (expectedType != null) || (outFileName != null) && (expectedType == null))
throw new IllegalArgumentException("Expected element type and output filename cannot be null");
try {
List<String> paramList = new ArrayList<String>(5);
paramList.add(exec);
paramList.add(params);
paramList.add(MediaPackageElementParser.getAsXml(inElement));
paramList.add(outFileName);
paramList.add((expectedType == null) ? null : expectedType.toString());
return serviceRegistry.createJob(JOB_TYPE, Operation.Execute_Element.toString(), paramList, load);
} catch (ServiceRegistryException e) {
throw new ExecuteException(String.format("Unable to create a job of type '%s'", JOB_TYPE), e);
} catch (MediaPackageException e) {
throw new ExecuteException("Error serializing an element", e);
}
}
use of org.opencastproject.execute.api.ExecuteException in project opencast by opencast.
the class ExecuteServiceImpl method execute.
/**
* {@inheritDoc}
*
* @see org.opencastproject.execute.api.ExecuteService#execute(java.lang.String, java.lang.String,
* org.opencastproject.mediapackage.MediaPackage, java.lang.String,
* org.opencastproject.mediapackage.MediaPackageElement.Type, float)
*/
@Override
public Job execute(String exec, String params, MediaPackage mp, String outFileName, Type expectedType, float load) throws ExecuteException {
if (StringUtils.isBlank(exec))
throw new IllegalArgumentException("The command to execute cannot be null");
if (StringUtils.isBlank(params))
throw new IllegalArgumentException("The command arguments cannot be null");
if (mp == null)
throw new IllegalArgumentException("The input MediaPackage cannot be null");
outFileName = StringUtils.trimToNull(outFileName);
if ((outFileName == null) && (expectedType != null) || (outFileName != null) && (expectedType == null))
throw new IllegalArgumentException("Expected element type and output filename cannot be null");
try {
List<String> paramList = new ArrayList<String>(5);
paramList.add(exec);
paramList.add(params);
paramList.add(MediaPackageParser.getAsXml(mp));
paramList.add(outFileName);
paramList.add((expectedType == null) ? null : expectedType.toString());
return serviceRegistry.createJob(JOB_TYPE, Operation.Execute_Mediapackage.toString(), paramList, load);
} catch (ServiceRegistryException e) {
throw new ExecuteException(String.format("Unable to create a job of type '%s'", JOB_TYPE), e);
}
}
use of org.opencastproject.execute.api.ExecuteException in project opencast by opencast.
the class ExecuteManyWorkflowOperationHandler method start.
/**
* {@inheritDoc}
*
* @see org.opencastproject.workflow.api.WorkflowOperationHandler#start(org.opencastproject.workflow.api.WorkflowInstance,
* JobContext)
*/
@Override
public WorkflowOperationResult start(WorkflowInstance workflowInstance, JobContext context) throws WorkflowOperationException {
MediaPackage mediaPackage = workflowInstance.getMediaPackage();
WorkflowOperationInstance operation = workflowInstance.getCurrentOperation();
logger.debug("Running execute workflow operation with ID {}", operation.getId());
// Get operation parameters
String exec = StringUtils.trimToNull(operation.getConfiguration(EXEC_PROPERTY));
String params = StringUtils.trimToNull(operation.getConfiguration(PARAMS_PROPERTY));
float load = 1.0f;
String loadPropertyStr = StringUtils.trimToEmpty(operation.getConfiguration(LOAD_PROPERTY));
if (StringUtils.isNotBlank(loadPropertyStr)) {
try {
load = Float.parseFloat(loadPropertyStr);
} catch (NumberFormatException e) {
String description = StringUtils.trimToEmpty(operation.getDescription());
logger.warn("Ignoring invalid load value '{}' on execute operation with description '{}'", loadPropertyStr, description);
}
}
String sourceFlavor = StringUtils.trimToNull(operation.getConfiguration(SOURCE_FLAVOR_PROPERTY));
String sourceTags = StringUtils.trimToNull(operation.getConfiguration(SOURCE_TAGS_PROPERTY));
String targetFlavorStr = StringUtils.trimToNull(operation.getConfiguration(TARGET_FLAVOR_PROPERTY));
String targetTags = StringUtils.trimToNull(operation.getConfiguration(TARGET_TAGS_PROPERTY));
String outputFilename = StringUtils.trimToNull(operation.getConfiguration(OUTPUT_FILENAME_PROPERTY));
String expectedTypeStr = StringUtils.trimToNull(operation.getConfiguration(EXPECTED_TYPE_PROPERTY));
MediaPackageElementFlavor matchingFlavor = null;
if (sourceFlavor != null)
matchingFlavor = MediaPackageElementFlavor.parseFlavor(sourceFlavor);
// Unmarshall target flavor
MediaPackageElementFlavor targetFlavor = null;
if (targetFlavorStr != null)
targetFlavor = MediaPackageElementFlavor.parseFlavor(targetFlavorStr);
// Unmarshall expected mediapackage element type
MediaPackageElement.Type expectedType = null;
if (expectedTypeStr != null) {
for (MediaPackageElement.Type type : MediaPackageElement.Type.values()) if (type.toString().equalsIgnoreCase(expectedTypeStr)) {
expectedType = type;
break;
}
if (expectedType == null)
throw new WorkflowOperationException("'" + expectedTypeStr + "' is not a valid element type");
}
List<String> sourceTagList = asList(sourceTags);
// Select the tracks based on source flavors and tags
Set<MediaPackageElement> inputSet = new HashSet<>();
for (MediaPackageElement element : mediaPackage.getElementsByTags(sourceTagList)) {
MediaPackageElementFlavor elementFlavor = element.getFlavor();
if (sourceFlavor == null || (elementFlavor != null && elementFlavor.matches(matchingFlavor))) {
inputSet.add(element);
}
}
if (inputSet.size() == 0) {
logger.warn("Mediapackage {} has no suitable elements to execute the command {} based on tags {} and flavor {}", mediaPackage, exec, sourceTags, sourceFlavor);
return createResult(mediaPackage, Action.CONTINUE);
}
MediaPackageElement[] inputElements = inputSet.toArray(new MediaPackageElement[inputSet.size()]);
try {
Job[] jobs = new Job[inputElements.length];
MediaPackageElement[] resultElements = new MediaPackageElement[inputElements.length];
long totalTimeInQueue = 0;
for (int i = 0; i < inputElements.length; i++) jobs[i] = executeService.execute(exec, params, inputElements[i], outputFilename, expectedType, load);
// Wait for all jobs to be finished
if (!waitForStatus(jobs).isSuccess())
throw new WorkflowOperationException("Execute operation failed");
// Find which output elements are tracks and inspect them
HashMap<Integer, Job> jobMap = new HashMap<>();
for (int i = 0; i < jobs.length; i++) {
// Add this job's queue time to the total
totalTimeInQueue += jobs[i].getQueueTime();
if (StringUtils.trimToNull(jobs[i].getPayload()) != null) {
resultElements[i] = MediaPackageElementParser.getFromXml(jobs[i].getPayload());
if (resultElements[i].getElementType() == MediaPackageElement.Type.Track) {
jobMap.put(i, inspectionService.inspect(resultElements[i].getURI()));
}
} else
resultElements[i] = inputElements[i];
}
if (jobMap.size() > 0) {
if (!waitForStatus(jobMap.values().toArray(new Job[jobMap.size()])).isSuccess())
throw new WorkflowOperationException("Execute operation failed in track inspection");
for (Entry<Integer, Job> entry : jobMap.entrySet()) {
// Add this job's queue time to the total
totalTimeInQueue += entry.getValue().getQueueTime();
resultElements[entry.getKey()] = MediaPackageElementParser.getFromXml(entry.getValue().getPayload());
}
}
for (int i = 0; i < resultElements.length; i++) {
if (resultElements[i] != inputElements[i]) {
// Store new element to mediaPackage
mediaPackage.addDerived(resultElements[i], inputElements[i]);
// Store new element to mediaPackage
URI uri = workspace.moveTo(resultElements[i].getURI(), mediaPackage.getIdentifier().toString(), resultElements[i].getIdentifier(), outputFilename);
resultElements[i].setURI(uri);
// Set new flavor
if (targetFlavor != null)
resultElements[i].setFlavor(targetFlavor);
}
// Set new tags
if (targetTags != null) {
// Assume the tags starting with "-" means we want to eliminate such tags form the result element
for (String tag : asList(targetTags)) {
if (tag.startsWith("-"))
// We remove the tag resulting from stripping all the '-' characters at the beginning of the tag
resultElements[i].removeTag(tag.replaceAll("^-+", ""));
else
resultElements[i].addTag(tag);
}
}
}
WorkflowOperationResult result = createResult(mediaPackage, Action.CONTINUE, totalTimeInQueue);
logger.debug("Execute operation {} completed", operation.getId());
return result;
} catch (ExecuteException e) {
throw new WorkflowOperationException(e);
} catch (MediaPackageException e) {
throw new WorkflowOperationException("Some result element couldn't be serialized", e);
} catch (NotFoundException e) {
throw new WorkflowOperationException("Could not find mediapackage", e);
} catch (IOException e) {
throw new WorkflowOperationException("Error unmarshalling a result mediapackage element", e);
} catch (MediaInspectionException e) {
throw new WorkflowOperationException("Error inspecting one of the created tracks", e);
}
}
Aggregations