use of org.opencastproject.scheduler.api.SchedulerConflictException in project opencast by opencast.
the class IngestRestService method schedule.
@POST
@Path("schedule/{wdID}")
@RestQuery(name = "schedule", description = "Schedule an event based on the given media package", pathParameters = { @RestParameter(description = "Workflow definition id", isRequired = true, name = "wdID", type = RestParameter.Type.STRING) }, restParameters = { @RestParameter(description = "The media package", isRequired = true, name = "mediaPackage", type = RestParameter.Type.TEXT) }, reponses = { @RestResponse(description = "Event scheduled", responseCode = HttpServletResponse.SC_CREATED), @RestResponse(description = "Media package not valid", responseCode = HttpServletResponse.SC_BAD_REQUEST) }, returnDescription = "")
public Response schedule(@PathParam("wdID") String wdID, MultivaluedMap<String, String> formData) {
if (StringUtils.isBlank(wdID)) {
logger.trace("workflow definition id is not specified");
return Response.status(Response.Status.BAD_REQUEST).build();
}
Map<String, String> wfConfig = getWorkflowConfig(formData);
if (StringUtils.isNotBlank(wdID)) {
wfConfig.put(CaptureParameters.INGEST_WORKFLOW_DEFINITION, wdID);
}
logger.debug("Schedule with workflow definition '{}'", wfConfig.get(WORKFLOW_DEFINITION_ID_PARAM));
String mediaPackageXml = formData.getFirst("mediaPackage");
if (StringUtils.isBlank(mediaPackageXml)) {
logger.debug("Rejected schedule without media package");
return Response.status(Status.BAD_REQUEST).build();
}
MediaPackage mp = null;
try {
mp = factory.newMediaPackageBuilder().loadFromXml(mediaPackageXml);
if (MediaPackageSupport.sanityCheck(mp).isSome()) {
throw new MediaPackageException("Insane media package");
}
} catch (MediaPackageException e) {
logger.debug("Rejected ingest with invalid media package {}", mp);
return Response.status(Status.BAD_REQUEST).build();
}
MediaPackageElement[] mediaPackageElements = mp.getElementsByFlavor(MediaPackageElements.EPISODE);
if (mediaPackageElements.length != 1) {
logger.debug("There can be only one (and exactly one) episode dublin core catalog: https://youtu.be/_J3VeogFUOs");
return Response.status(Status.BAD_REQUEST).build();
}
try {
ingestService.schedule(mp, wdID, wfConfig);
return Response.status(Status.CREATED).build();
} catch (IngestException e) {
return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
} catch (SchedulerConflictException e) {
return Response.status(Status.CONFLICT).entity(e.getMessage()).build();
} catch (NotFoundException | UnauthorizedException | SchedulerException e) {
return Response.serverError().build();
}
}
use of org.opencastproject.scheduler.api.SchedulerConflictException in project opencast by opencast.
the class SchedulerServiceImpl method createTransaction.
@Override
public synchronized SchedulerTransaction createTransaction(String schedulingSource) throws SchedulerException {
logger.debug(format("Transaction for source %s | create", schedulingSource));
try {
boolean hasTransaction = persistence.hasTransaction(schedulingSource);
if (hasTransaction)
throw new SchedulerConflictException("Transaction already exists");
SchedulerTransaction transaction = new SchedulerTransactionImpl(schedulingSource);
persistence.storeTransaction(transaction.getId(), schedulingSource);
logger.info(format("Transaction for source %s | created | id=%s", transaction.getId(), schedulingSource));
return transaction;
} catch (SchedulerServiceDatabaseException e) {
logger.error(format("Transaction for source %s | error | %s", schedulingSource, getStackTrace(e)));
throw new SchedulerException(e);
}
}
use of org.opencastproject.scheduler.api.SchedulerConflictException in project opencast by opencast.
the class SchedulerServiceImpl method addMultipleEventInternal.
private Map<String, Period> addMultipleEventInternal(List<Period> periods, String captureAgentId, Set<String> userIds, MediaPackage templateMp, Map<String, String> wfProperties, Map<String, String> caMetadata, String modificationOrigin, Opt<Boolean> optOutStatus, Opt<String> schedulingSource, Opt<String> trxId) throws SchedulerException {
notNull(periods, "periods");
requireTrue(periods.size() > 0, "periods");
notEmpty(captureAgentId, "captureAgentId");
notNull(userIds, "userIds");
notNull(templateMp, "mediaPackages");
notNull(wfProperties, "wfProperties");
notNull(caMetadata, "caMetadata");
notEmpty(modificationOrigin, "modificationOrigin");
notNull(optOutStatus, "optOutStatus");
notNull(schedulingSource, "schedulingSource");
notNull(trxId, "trxId");
Map<String, Period> scheduledEvents = new LinkedHashMap<>();
try {
LinkedList<Id> ids = new LinkedList<>();
AQueryBuilder qb = assetManager.createQuery();
Predicate p = null;
// While we don't have a list of IDs equal to the number of periods
while (ids.size() <= periods.size()) {
// Create a list of IDs equal to the number of periods, along with a set of AM predicates
while (ids.size() <= periods.size()) {
Id id = new IdImpl(UUID.randomUUID().toString());
ids.add(id);
Predicate np = qb.mediaPackageId(id.compact());
// Haha, p = np jokes with the AM query language. Ha. Haha. Ha. (Sob...)
if (null == p) {
p = np;
} else {
p = p.or(np);
}
}
// Select the list of ids which alread exist. Hint: this needs to be zero
AResult result = qb.select(qb.nothing()).where(withOrganization(qb).and(p).and(qb.version().isLatest())).run();
// If there is conflict, clear the list and start over
if (result.getTotalSize() > 0) {
ids.clear();
}
}
Opt<String> seriesId = Opt.nul(StringUtils.trimToNull(templateMp.getSeries()));
// Get opt out status
boolean optOut = getOptOutStatus(seriesId, optOutStatus);
if (trxId.isNone()) {
// Check for locked transactions
if (schedulingSource.isSome() && persistence.hasTransaction(schedulingSource.get())) {
logger.warn("Unable to add events, source '{}' is currently locked due to an active transaction!", schedulingSource.get());
throw new SchedulerTransactionLockException("Unable to add event, locked source " + schedulingSource.get());
}
// Check for conflicting events if not opted out
if (!optOut) {
List<MediaPackage> conflictingEvents = findConflictingEvents(periods, captureAgentId, TimeZone.getDefault());
if (conflictingEvents.size() > 0) {
logger.info("Unable to add events, conflicting events found: {}", conflictingEvents);
throw new SchedulerConflictException("Unable to add event, conflicting events found");
}
}
}
// counter for index into the list of mediapackages
int counter = 0;
for (Period event : periods) {
MediaPackage mediaPackage = (MediaPackage) templateMp.clone();
Date startDate = new Date(event.getStart().getTime());
Date endDate = new Date(event.getEnd().getTime());
Id id = ids.get(counter);
// Get, or make, the DC catalog
DublinCoreCatalog dc;
Opt<DublinCoreCatalog> dcOpt = DublinCoreUtil.loadEpisodeDublinCore(workspace, templateMp);
if (dcOpt.isSome()) {
dc = dcOpt.get();
dc = (DublinCoreCatalog) dc.clone();
// make sure to bind the OC_PROPERTY namespace
dc.addBindings(XmlNamespaceContext.mk(XmlNamespaceBinding.mk(DublinCores.OC_PROPERTY_NS_PREFIX, DublinCores.OC_PROPERTY_NS_URI)));
} else {
dc = DublinCores.mkOpencastEpisode().getCatalog();
}
// Set the new media package identifier
mediaPackage.setIdentifier(id);
// Update dublincore title and temporal
String newTitle = dc.getFirst(DublinCore.PROPERTY_TITLE) + String.format(" %0" + Integer.toString(periods.size()).length() + "d", ++counter);
dc.set(DublinCore.PROPERTY_TITLE, newTitle);
DublinCoreValue eventTime = EncodingSchemeUtils.encodePeriod(new DCMIPeriod(startDate, endDate), Precision.Second);
dc.set(DublinCore.PROPERTY_TEMPORAL, eventTime);
mediaPackage = updateDublincCoreCatalog(mediaPackage, dc);
mediaPackage.setTitle(newTitle);
String mediaPackageId = mediaPackage.getIdentifier().compact();
// Converting from iCal4j DateTime objects to plain Date objects to prevent AMQ issues below
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTime(event.getStart());
Date startDateTime = cal.getTime();
cal.setTime(event.getEnd());
Date endDateTime = cal.getTime();
// Load dublincore and acl for update
Opt<DublinCoreCatalog> dublinCore = DublinCoreUtil.loadEpisodeDublinCore(workspace, mediaPackage);
Option<AccessControlList> acl = authorizationService.getAcl(mediaPackage, AclScope.Episode);
// Get updated agent properties
Map<String, String> finalCaProperties = getFinalAgentProperties(caMetadata, wfProperties, captureAgentId, seriesId, dublinCore);
// Persist asset
String checksum = calculateChecksum(workspace, getEventCatalogUIAdapterFlavors(), startDateTime, endDateTime, captureAgentId, userIds, mediaPackage, dublinCore, wfProperties, finalCaProperties, optOut, acl.toOpt().getOr(new AccessControlList()));
persistEvent(mediaPackageId, modificationOrigin, checksum, Opt.some(startDateTime), Opt.some(endDateTime), Opt.some(captureAgentId), Opt.some(userIds), Opt.some(mediaPackage), Opt.some(wfProperties), Opt.some(finalCaProperties), Opt.some(optOut), schedulingSource, trxId);
if (trxId.isNone()) {
// Send updates
sendUpdateAddEvent(mediaPackageId, acl.toOpt(), dublinCore, Opt.some(startDateTime), Opt.some(endDateTime), Opt.some(userIds), Opt.some(captureAgentId), Opt.some(finalCaProperties), Opt.some(optOut));
// Update last modified
touchLastEntry(captureAgentId);
}
scheduledEvents.put(mediaPackageId, event);
for (MediaPackageElement mediaPackageElement : mediaPackage.getElements()) {
try {
workspace.delete(mediaPackage.getIdentifier().toString(), mediaPackageElement.getIdentifier());
} catch (NotFoundException | IOException e) {
logger.warn("Failed to delete media package element", e);
}
}
}
return scheduledEvents;
} catch (SchedulerException e) {
throw e;
} catch (Exception e) {
logger.error("Failed to create events: {}", getStackTrace(e));
throw new SchedulerException(e);
}
}
use of org.opencastproject.scheduler.api.SchedulerConflictException in project opencast by opencast.
the class SchedulerServiceImplTest method testCreateAndUpdateConflictingEvents.
@Test
public void testCreateAndUpdateConflictingEvents() throws Exception {
Date start = new Date();
Date end = new Date(System.currentTimeMillis() + 60000);
final long currentTime = System.currentTimeMillis();
final MediaPackage mediaPackageA = generateEvent(Opt.<String>none());
final MediaPackage mediaPackageB = generateEvent(Opt.<String>none());
DublinCoreCatalog event = generateEvent("captureId", start, end);
addDublinCore(Opt.<String>none(), mediaPackageB, event);
//
schedSvc.addEvent(new Date(currentTime), new Date(currentTime + hours(1) + seconds(10)), "Device A", Collections.<String>emptySet(), mediaPackageA, wfProperties, Collections.<String, String>emptyMap(), Opt.<Boolean>none(), Opt.<String>none(), SchedulerService.ORIGIN);
schedSvc.addEvent(new Date(currentTime + hours(2)), new Date(currentTime + hours(25)), "Device A", Collections.<String>emptySet(), mediaPackageB, wfProperties, Collections.<String, String>emptyMap(), Opt.some(true), Opt.<String>none(), SchedulerService.ORIGIN);
{
List<MediaPackage> allEvents = schedSvc.search(Opt.<String>none(), Opt.<Date>none(), Opt.<Date>none(), Opt.<Date>none(), Opt.<Date>none());
assertEquals(2, allEvents.size());
}
// Update opted out event to a conflicting time
schedSvc.updateEvent(mediaPackageB.getIdentifier().compact(), Opt.some(new Date(currentTime)), Opt.<Date>none(), Opt.<String>none(), Opt.<Set<String>>none(), Opt.<MediaPackage>none(), Opt.<Map<String, String>>none(), Opt.<Map<String, String>>none(), Opt.<Opt<Boolean>>none(), SchedulerService.ORIGIN);
// Update opted out status
try {
schedSvc.updateEvent(mediaPackageB.getIdentifier().compact(), Opt.<Date>none(), Opt.<Date>none(), Opt.<String>none(), Opt.<Set<String>>none(), Opt.<MediaPackage>none(), Opt.<Map<String, String>>none(), Opt.<Map<String, String>>none(), Opt.some(Opt.some(false)), SchedulerService.ORIGIN);
fail("Conflict not detected!");
} catch (SchedulerConflictException e) {
Assert.assertNotNull(e);
}
}
use of org.opencastproject.scheduler.api.SchedulerConflictException in project opencast by opencast.
the class SchedulerServiceImplTest method testTransactionCommitConflict.
@Test
public void testTransactionCommitConflict() throws Exception {
Date start = new Date();
Date end = new Date(System.currentTimeMillis() + 60000);
String captureDeviceID = "demo";
String seriesId = "series1";
Set<String> userIds = new HashSet<>();
userIds.add("user1");
userIds.add("user2");
MediaPackage mp = generateEvent(Opt.<String>none());
mp.setSeries(seriesId);
DublinCoreCatalog extendedEvent = generateExtendedEvent(Opt.<String>none(), "extended");
addDublinCore(Opt.<String>none(), mp, extendedEvent);
DublinCoreCatalog event = generateEvent(captureDeviceID, start, end);
addDublinCore(Opt.<String>none(), mp, event);
addAcl(Opt.<String>none(), mp, acl);
Map<String, String> caProperties = generateCaptureAgentMetadata("demo");
SchedulerTransaction trx = schedSvc.createTransaction("new");
trx.addEvent(start, end, captureDeviceID, userIds, mp, wfProperties, caProperties, Opt.<Boolean>none());
try {
trx.commit();
} catch (SchedulerConflictException e) {
fail("Pre-conflict detection not working!");
}
AQueryBuilder query = assetManager.createQuery();
AResult result = query.select(query.snapshot(), query.properties()).where(query.organizationId().eq(new DefaultOrganization().getId()).and(query.mediaPackageId(mp.getIdentifier().compact()))).run();
assertEquals(1, result.getSize());
Opt<ARecord> record = result.getRecords().head();
assertTrue(record.isSome());
assertEquals(new VersionImpl(0), record.get().getSnapshot().get().getVersion());
schedSvc.updateEvent(mp.getIdentifier().compact(), Opt.<Date>none(), Opt.<Date>none(), Opt.<String>none(), Opt.<Set<String>>none(), Opt.<MediaPackage>none(), Opt.<Map<String, String>>none(), Opt.<Map<String, String>>none(), Opt.some(Opt.some(true)), SchedulerService.ORIGIN);
// Update with same checksum
trx = schedSvc.createTransaction("new");
trx.addEvent(start, end, captureDeviceID, userIds, mp, wfProperties, caProperties, Opt.some(true));
try {
// works because of same checksum
trx.commit();
} catch (SchedulerConflictException e) {
fail("Pre-conflict detection not working!");
}
query = assetManager.createQuery();
result = query.select(query.snapshot(), query.properties()).where(query.organizationId().eq(new DefaultOrganization().getId()).and(query.mediaPackageId(mp.getIdentifier().compact()))).run();
assertEquals(1, result.getSize());
record = result.getRecords().head();
assertTrue(record.isSome());
assertEquals(new VersionImpl(1), record.get().getSnapshot().get().getVersion());
assertTrue(record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getBoolean("optout")));
assertEquals("new", record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("source")));
assertEquals("new", record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("last_modified_origin")));
// Update with different checksum
schedSvc.updateEvent(mp.getIdentifier().compact(), Opt.<Date>none(), Opt.<Date>none(), Opt.<String>none(), Opt.<Set<String>>none(), Opt.<MediaPackage>none(), Opt.<Map<String, String>>none(), Opt.<Map<String, String>>none(), Opt.some(Opt.some(false)), SchedulerService.ORIGIN);
query = assetManager.createQuery();
result = query.select(query.snapshot(), query.properties()).where(query.organizationId().eq(new DefaultOrganization().getId()).and(query.mediaPackageId(mp.getIdentifier().compact()))).run();
assertEquals(1, result.getSize());
record = result.getRecords().head();
assertTrue(record.isSome());
assertEquals(new VersionImpl(1), record.get().getSnapshot().get().getVersion());
assertFalse(record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getBoolean("optout")));
assertTrue(record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getStringOpt("checksum")).isSome());
assertEquals("new", record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("source")));
assertEquals(SchedulerService.ORIGIN, record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("last_modified_origin")));
trx = schedSvc.createTransaction("new");
trx.addEvent(start, end, captureDeviceID, userIds, mp, wfProperties, caProperties, Opt.some(true));
try {
// creates conflict
trx.commit();
} catch (SchedulerConflictException e) {
fail("Pre-conflict detection not working!");
}
query = assetManager.createQuery();
result = query.select(query.snapshot(), query.properties()).where(query.organizationId().eq(new DefaultOrganization().getId()).and(query.mediaPackageId(mp.getIdentifier().compact()))).run();
assertEquals(1, result.getSize());
record = result.getRecords().head();
assertTrue(record.isSome());
assertEquals(new VersionImpl(1), record.get().getSnapshot().get().getVersion());
assertFalse(record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getBoolean("optout")));
assertTrue(record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getStringOpt("last_conflict")).isSome());
assertEquals("new", record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("source")));
assertEquals(SchedulerService.ORIGIN, record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("last_modified_origin")));
// Commit again with no changes
trx = schedSvc.createTransaction("new");
trx.addEvent(start, end, captureDeviceID, userIds, mp, wfProperties, caProperties, Opt.some(true));
try {
// ignores conflict
trx.commit();
} catch (SchedulerConflictException e) {
fail("Pre-conflict detection not working!");
}
query = assetManager.createQuery();
result = query.select(query.snapshot(), query.properties()).where(query.organizationId().eq(new DefaultOrganization().getId()).and(query.mediaPackageId(mp.getIdentifier().compact()))).run();
assertEquals(1, result.getSize());
record = result.getRecords().head();
assertTrue(record.isSome());
assertEquals(new VersionImpl(1), record.get().getSnapshot().get().getVersion());
assertFalse(record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getBoolean("optout")));
assertTrue(record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getStringOpt("last_conflict")).isSome());
assertEquals("new", record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("source")));
assertEquals(SchedulerService.ORIGIN, record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("last_modified_origin")));
// Commit again with changes
trx = schedSvc.createTransaction("new");
trx.addEvent(start, end, "newdevice", userIds, mp, wfProperties, caProperties, Opt.some(true));
try {
// override from existing
// creates new conflict
testConflictHandler.setStrategy(Strategy.OLD);
trx.commit();
} catch (SchedulerConflictException e) {
fail("Pre-conflict detection not working!");
}
query = assetManager.createQuery();
result = query.select(query.snapshot(), query.properties()).where(query.organizationId().eq(new DefaultOrganization().getId()).and(query.mediaPackageId(mp.getIdentifier().compact()))).run();
assertEquals(1, result.getSize());
record = result.getRecords().head();
assertTrue(record.isSome());
assertEquals(new VersionImpl(1), record.get().getSnapshot().get().getVersion());
assertFalse(record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getBoolean("optout")));
assertTrue(record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getStringOpt("last_conflict")).isSome());
assertEquals("new", record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("source")));
assertEquals("demo", record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("agent")));
assertEquals(SchedulerService.ORIGIN, record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("last_modified_origin")));
// Commit again with new changes
trx = schedSvc.createTransaction("new");
trx.addEvent(start, end, "newdevice", userIds, mp, wfProperties, caProperties, Opt.some(false));
try {
// override from transaction
testConflictHandler.setStrategy(Strategy.NEW);
trx.commit();
} catch (SchedulerConflictException e) {
fail("Pre-conflict detection not working!");
}
query = assetManager.createQuery();
result = query.select(query.snapshot(), query.properties()).where(query.organizationId().eq(new DefaultOrganization().getId()).and(query.mediaPackageId(mp.getIdentifier().compact()))).run();
assertEquals(1, result.getSize());
record = result.getRecords().head();
assertTrue(record.isSome());
assertEquals(new VersionImpl(5), record.get().getSnapshot().get().getVersion());
assertFalse(record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getBoolean("optout")));
assertFalse(record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getStringOpt("last_conflict")).isSome());
assertEquals("newdevice", record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("agent")));
assertEquals("new", record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("source")));
assertEquals("new", record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("last_modified_origin")));
// Commit again with new changes
trx = schedSvc.createTransaction("new2");
trx.addEvent(start, end, captureDeviceID, userIds, mp, wfProperties, caProperties, Opt.some(true));
try {
// merge opt out only and keep agent id
testConflictHandler.setStrategy(Strategy.MERGED);
trx.commit();
} catch (SchedulerConflictException e) {
fail("Pre-conflict detection not working!");
}
query = assetManager.createQuery();
result = query.select(query.snapshot(), query.properties()).where(query.organizationId().eq(new DefaultOrganization().getId()).and(query.mediaPackageId(mp.getIdentifier().compact()))).run();
assertEquals(1, result.getSize());
record = result.getRecords().head();
assertTrue(record.isSome());
assertEquals(new VersionImpl(7), record.get().getSnapshot().get().getVersion());
assertFalse(record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getBoolean("optout")));
assertFalse(record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getStringOpt("last_conflict")).isSome());
assertEquals(captureDeviceID, record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("agent")));
assertEquals("new2", record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("source")));
assertEquals("new2", record.get().getProperties().apply(org.opencastproject.assetmanager.api.fn.Properties.getString("last_modified_origin")));
}
Aggregations