use of org.opencastproject.workflow.api.WorkflowQuery in project opencast by opencast.
the class WorkflowServiceImpl method cleanupWorkflowInstances.
@Override
public synchronized void cleanupWorkflowInstances(int buffer, WorkflowState state) throws UnauthorizedException, WorkflowDatabaseException {
logger.info("Start cleaning up workflow instances older than {} days with status '{}'", buffer, state);
int instancesCleaned = 0;
int cleaningFailed = 0;
WorkflowQuery query = new WorkflowQuery().withState(state).withDateBefore(DateUtils.addDays(new Date(), -buffer)).withCount(Integer.MAX_VALUE);
for (WorkflowInstance workflowInstance : getWorkflowInstances(query).getItems()) {
try {
remove(workflowInstance.getId());
instancesCleaned++;
} catch (WorkflowDatabaseException e) {
throw e;
} catch (UnauthorizedException e) {
throw e;
} catch (NotFoundException e) {
// Since we are in a cleanup operation, we don't have to care about NotFoundExceptions
logger.debug("Workflow instance '{}' could not be removed: {}", workflowInstance.getId(), ExceptionUtils.getStackTrace(e));
} catch (WorkflowParsingException e) {
logger.warn("Workflow instance '{}' could not be removed: {}", workflowInstance.getId(), ExceptionUtils.getStackTrace(e));
cleaningFailed++;
} catch (WorkflowStateException e) {
logger.warn("Workflow instance '{}' could not be removed: {}", workflowInstance.getId(), ExceptionUtils.getStackTrace(e));
cleaningFailed++;
}
}
if (instancesCleaned == 0 && cleaningFailed == 0) {
logger.info("No workflow instances found to clean up");
return;
}
if (instancesCleaned > 0)
logger.info("Cleaned up '%d' workflow instances", instancesCleaned);
if (cleaningFailed > 0) {
logger.warn("Cleaning failed for '%d' workflow instances", cleaningFailed);
throw new WorkflowDatabaseException("Unable to clean all workflow instances, see logs!");
}
}
use of org.opencastproject.workflow.api.WorkflowQuery in project opencast by opencast.
the class WorkflowServiceImpl method remove.
/**
* {@inheritDoc}
*
* @see org.opencastproject.workflow.api.WorkflowService#remove(long)
*/
@Override
public void remove(long workflowInstanceId) throws WorkflowDatabaseException, NotFoundException, UnauthorizedException, WorkflowParsingException, WorkflowStateException {
final Lock lock = this.lock.get(workflowInstanceId);
lock.lock();
try {
WorkflowQuery query = new WorkflowQuery();
query.withId(Long.toString(workflowInstanceId));
WorkflowSet workflows = index.getWorkflowInstances(query, Permissions.Action.READ.toString(), false);
if (workflows.size() == 1) {
WorkflowInstance instance = workflows.getItems()[0];
WorkflowInstance.WorkflowState state = instance.getState();
if (state != WorkflowState.SUCCEEDED && state != WorkflowState.FAILED && state != WorkflowState.STOPPED)
throw new WorkflowStateException("Workflow instance with state '" + state + "' cannot be removed. Only states SUCCEEDED, FAILED & STOPPED are allowed");
try {
assertPermission(instance, Permissions.Action.WRITE.toString());
} catch (MediaPackageException e) {
throw new WorkflowParsingException(e);
}
// First, remove temporary files DO THIS BEFORE REMOVING FROM INDEX
try {
removeTempFiles(instance);
} catch (NotFoundException e) {
// If the files aren't their anymore, we don't have to cleanup up them :-)
logger.debug("Temporary files of workflow instance %d seem to be gone already...", workflowInstanceId);
}
// Second, remove jobs related to a operation which belongs to the workflow instance
List<WorkflowOperationInstance> operations = instance.getOperations();
List<Long> jobsToDelete = new ArrayList<>();
for (WorkflowOperationInstance op : operations) {
if (op.getId() != null) {
long workflowOpId = op.getId();
if (workflowOpId != workflowInstanceId) {
jobsToDelete.add(workflowOpId);
}
}
}
try {
serviceRegistry.removeJobs(jobsToDelete);
} catch (ServiceRegistryException e) {
logger.warn("Problems while removing jobs related to workflow operations '%s': %s", jobsToDelete, e.getMessage());
} catch (NotFoundException e) {
logger.debug("No jobs related to one of the workflow operations '%s' found in the service registry", jobsToDelete);
}
// Third, remove workflow instance job itself
try {
serviceRegistry.removeJobs(Collections.singletonList(workflowInstanceId));
messageSender.sendObjectMessage(WorkflowItem.WORKFLOW_QUEUE, MessageSender.DestinationType.Queue, WorkflowItem.deleteInstance(workflowInstanceId, instance));
} catch (ServiceRegistryException e) {
logger.warn("Problems while removing workflow instance job '%d': %s", workflowInstanceId, ExceptionUtils.getStackTrace(e));
} catch (NotFoundException e) {
logger.info("No workflow instance job '%d' found in the service registry", workflowInstanceId);
}
// At last, remove workflow instance from the index
try {
index.remove(workflowInstanceId);
} catch (NotFoundException e) {
// This should never happen, because we got workflow instance by querying the index...
logger.warn("Workflow instance could not be removed from index: %s", ExceptionUtils.getStackTrace(e));
}
} else if (workflows.size() == 0) {
throw new NotFoundException("Workflow instance with id '" + Long.toString(workflowInstanceId) + "' could not be found");
} else {
throw new WorkflowDatabaseException("More than one workflow found with id: " + Long.toString(workflowInstanceId));
}
} finally {
lock.unlock();
}
}
use of org.opencastproject.workflow.api.WorkflowQuery in project opencast by opencast.
the class WorkflowServiceImpl method start.
/**
* {@inheritDoc}
*
* @see org.opencastproject.workflow.api.WorkflowService#start(org.opencastproject.workflow.api.WorkflowDefinition,
* org.opencastproject.mediapackage.MediaPackage, Long, java.util.Map)
*/
@Override
public WorkflowInstance start(WorkflowDefinition workflowDefinition, MediaPackage sourceMediaPackage, Long parentWorkflowId, Map<String, String> properties) throws WorkflowDatabaseException, WorkflowParsingException, NotFoundException {
// We have to synchronize per media package to avoid starting multiple simultaneous workflows for one media package.
final Lock lock = mediaPackageLocks.get(sourceMediaPackage.getIdentifier().toString());
lock.lock();
try {
logger.startUnitOfWork();
if (workflowDefinition == null)
throw new IllegalArgumentException("workflow definition must not be null");
if (sourceMediaPackage == null)
throw new IllegalArgumentException("mediapackage must not be null");
for (List<String> errors : MediaPackageSupport.sanityCheck(sourceMediaPackage)) {
throw new IllegalArgumentException("Insane media package cannot be processed: " + mkString(errors, "; "));
}
if (parentWorkflowId != null) {
try {
// Let NotFoundException bubble up
getWorkflowById(parentWorkflowId);
} catch (UnauthorizedException e) {
throw new IllegalArgumentException("Parent workflow " + parentWorkflowId + " not visible to this user");
}
} else {
WorkflowQuery wfq = new WorkflowQuery().withMediaPackage(sourceMediaPackage.getIdentifier().compact());
WorkflowSet mpWorkflowInstances = getWorkflowInstances(wfq);
if (mpWorkflowInstances.size() > 0) {
for (WorkflowInstance wfInstance : mpWorkflowInstances.getItems()) {
if (wfInstance.isActive())
throw new IllegalStateException(String.format("Can't start workflow '%s' for media package '%s' because another workflow is currently active.", workflowDefinition.getTitle(), sourceMediaPackage.getIdentifier().compact()));
}
}
}
// Get the current user
User currentUser = securityService.getUser();
if (currentUser == null)
throw new SecurityException("Current user is unknown");
// Get the current organization
Organization organization = securityService.getOrganization();
if (organization == null)
throw new SecurityException("Current organization is unknown");
WorkflowInstance workflowInstance = new WorkflowInstanceImpl(workflowDefinition, sourceMediaPackage, parentWorkflowId, currentUser, organization, properties);
workflowInstance = updateConfiguration(workflowInstance, properties);
// Create and configure the workflow instance
try {
// Create a new job for this workflow instance
String workflowDefinitionXml = WorkflowParser.toXml(workflowDefinition);
String workflowInstanceXml = WorkflowParser.toXml(workflowInstance);
String mediaPackageXml = MediaPackageParser.getAsXml(sourceMediaPackage);
List<String> arguments = new ArrayList<String>();
arguments.add(workflowDefinitionXml);
arguments.add(mediaPackageXml);
if (parentWorkflowId != null || properties != null) {
String parentWorkflowIdString = (parentWorkflowId != null) ? parentWorkflowId.toString() : NULL_PARENT_ID;
arguments.add(parentWorkflowIdString);
}
if (properties != null) {
arguments.add(mapToString(properties));
}
Job job = serviceRegistry.createJob(JOB_TYPE, Operation.START_WORKFLOW.toString(), arguments, workflowInstanceXml, false, null, WORKFLOW_JOB_LOAD);
// Have the workflow take on the job's identity
workflowInstance.setId(job.getId());
// Add the workflow to the search index and have the job enqueued for dispatch.
// Update also sets ACL and mediapackage metadata
update(workflowInstance);
return workflowInstance;
} catch (Throwable t) {
try {
workflowInstance.setState(FAILED);
update(workflowInstance);
} catch (Exception failureToFail) {
logger.warn(failureToFail, "Unable to update workflow to failed state");
}
throw new WorkflowDatabaseException(t);
}
} finally {
logger.endUnitOfWork();
lock.unlock();
}
}
use of org.opencastproject.workflow.api.WorkflowQuery in project opencast by opencast.
the class WorkflowServiceImpl method isReadyToAccept.
/**
* {@inheritDoc}
*
* If we are already running the maximum number of workflows, don't accept another START_WORKFLOW job
*
* @see org.opencastproject.job.api.AbstractJobProducer#isReadyToAccept(org.opencastproject.job.api.Job)
*/
@Override
public boolean isReadyToAccept(Job job) throws ServiceRegistryException, UndispatchableJobException {
String operation = job.getOperation();
// Only restrict execution of new jobs
if (!Operation.START_WORKFLOW.toString().equals(operation))
return true;
// If the first operation is guaranteed to pause, run the job.
if (job.getArguments().size() > 1 && job.getArguments().get(0) != null) {
try {
WorkflowDefinition workflowDef = WorkflowParser.parseWorkflowDefinition(job.getArguments().get(0));
if (workflowDef.getOperations().size() > 0) {
String firstOperationId = workflowDef.getOperations().get(0).getId();
WorkflowOperationHandler handler = getWorkflowOperationHandler(firstOperationId);
if (handler instanceof ResumableWorkflowOperationHandler) {
if (((ResumableWorkflowOperationHandler) handler).isAlwaysPause()) {
return true;
}
}
}
} catch (WorkflowParsingException e) {
throw new UndispatchableJobException(job + " is not a proper job to start a workflow", e);
}
}
WorkflowInstance workflow = null;
WorkflowSet workflowInstances = null;
String mediaPackageId = null;
// Fetch all workflows that are running with the current mediapackage
try {
workflow = getWorkflowById(job.getId());
mediaPackageId = workflow.getMediaPackage().getIdentifier().toString();
workflowInstances = getWorkflowInstances(new WorkflowQuery().withMediaPackage(workflow.getMediaPackage().getIdentifier().toString()).withState(RUNNING).withState(PAUSED).withState(FAILING));
} catch (NotFoundException e) {
logger.error("Trying to start workflow with id %s but no corresponding instance is available from the workflow service", job.getId());
throw new UndispatchableJobException(e);
} catch (UnauthorizedException e) {
logger.error("Authorization denied while requesting to loading workflow instance %s: %s", job.getId(), e.getMessage());
throw new UndispatchableJobException(e);
} catch (WorkflowDatabaseException e) {
logger.error("Error loading workflow instance %s: %s", job.getId(), e.getMessage());
return false;
}
// If more than one workflow is running working on this mediapackage, then we don't start this one
boolean toomany = workflowInstances.size() > 1;
// Make sure we are not excluding ourselves
toomany |= workflowInstances.size() == 1 && workflow.getId() != workflowInstances.getItems()[0].getId();
// Avoid running multiple workflows with same media package id at the same time
if (toomany) {
if (!delayedWorkflows.contains(workflow.getId())) {
logger.info("Delaying start of workflow %s, another workflow on media package %s is still running", workflow.getId(), mediaPackageId);
delayedWorkflows.add(workflow.getId());
}
return false;
}
return true;
}
use of org.opencastproject.workflow.api.WorkflowQuery in project opencast by opencast.
the class WorkflowServiceSolrIndexTest method testBuildNegativeStatesQuery.
/**
* Tests whether the query is built using AND rather than OR when supplying multiple excluded states
*/
@Test
public void testBuildNegativeStatesQuery() throws Exception {
WorkflowQuery q = new WorkflowQuery().withSeriesId("series1").withoutState(WorkflowState.RUNNING).withoutState(WorkflowState.PAUSED);
String solrQuery = dao.createQuery(q, Permissions.Action.READ.toString(), true);
String expected = "oc_org:mh_default_org AND seriesid:series1 AND (-state:running AND -state:paused AND *:*)";
assertEquals(expected, solrQuery);
}
Aggregations