use of com.entwinemedia.fn.Fn in project opencast by opencast.
the class SeriesEndpoint method updateSeriesAcl.
@PUT
@Path("{seriesId}/acl")
@Produces({ "application/json", "application/v1.0.0+json" })
@RestQuery(name = "updateseriesacl", description = "Updates a series' access policy.", returnDescription = "", pathParameters = { @RestParameter(name = "seriesId", description = "The series id", isRequired = true, type = STRING) }, restParameters = { @RestParameter(name = "acl", isRequired = true, description = "Access policy", type = STRING) }, reponses = { @RestResponse(description = "The access control list for the specified series is updated.", responseCode = HttpServletResponse.SC_OK), @RestResponse(description = "The specified series does not exist.", responseCode = HttpServletResponse.SC_NOT_FOUND) })
public Response updateSeriesAcl(@HeaderParam("Accept") String acceptHeader, @PathParam("seriesId") String seriesID, @FormParam("acl") String aclJson) throws NotFoundException, SeriesException, UnauthorizedException {
if (isBlank(aclJson))
return R.badRequest("Missing form parameter 'acl'");
JSONParser parser = new JSONParser();
JSONArray acl;
try {
acl = (JSONArray) parser.parse(aclJson);
} catch (ParseException e) {
logger.debug("Could not parse ACL ({}): {}", aclJson, getStackTrace(e));
return R.badRequest("Could not parse ACL");
}
List<AccessControlEntry> accessControlEntries = $(acl.toArray()).map(new Fn<Object, AccessControlEntry>() {
@Override
public AccessControlEntry apply(Object a) {
JSONObject ace = (JSONObject) a;
return new AccessControlEntry((String) ace.get("role"), (String) ace.get("action"), (boolean) ace.get("allow"));
}
}).toList();
seriesService.updateAccessControl(seriesID, new AccessControlList(accessControlEntries));
return ApiResponses.Json.ok(VERSION_1_0_0, aclJson);
}
use of com.entwinemedia.fn.Fn in project opencast by opencast.
the class DublinCoreUtil method calculateChecksum.
/**
* Calculate an MD5 checksum for a DublinCore catalog.
*/
public static Checksum calculateChecksum(DublinCoreCatalog dc) {
// Use 0 as a word separator. This is safe since none of the UTF-8 code points
// except \u0000 contains a null byte when converting to a byte array.
final byte[] sep = new byte[] { 0 };
final MessageDigest md = // consider all DublinCore properties
$(getPropertiesSorted(dc)).bind(new Fn<CatalogEntry, Stream<String>>() {
@Override
public Stream<String> apply(CatalogEntry entry) {
// get attributes, sorted and serialized as [name, value, name, value, ...]
final Stream<String> attributesSorted = $(entry.getAttributes().entrySet()).sort(new Comparator<Entry<EName, String>>() {
@Override
public int compare(Entry<EName, String> o1, Entry<EName, String> o2) {
return o1.getKey().compareTo(o2.getKey());
}
}).bind(new Fn<Entry<EName, String>, Stream<String>>() {
@Override
public Stream<String> apply(Entry<EName, String> attribute) {
return $(attribute.getKey().toString(), attribute.getValue());
}
});
return $(entry.getEName().toString(), entry.getValue()).append(attributesSorted);
}
}).append(Opt.nul(dc.getRootTag()).map(toString)).foldl(mkMd5MessageDigest(), new Fn2<MessageDigest, String, MessageDigest>() {
@Override
public MessageDigest apply(MessageDigest digest, String s) {
digest.update(s.getBytes(StandardCharsets.UTF_8));
// add separator byte (see definition above)
digest.update(sep);
return digest;
}
});
try {
return Checksum.create("md5", Checksum.convertToHex(md.digest()));
} catch (NoSuchAlgorithmException e) {
return chuck(e);
}
}
use of com.entwinemedia.fn.Fn in project opencast by opencast.
the class MetadataField method createDateMetadata.
/**
* Create a metadata field based on a {@link Date}.
*
* @param inputID
* The identifier of the new metadata field
* @param label
* The label of the new metadata field
* @param readOnly
* Define if the new metadata is or not a readonly field
* @param required
* Define if the new metadata field is or not required
* @param pattern
* The date pattern for {@link SimpleDateFormat}.
* @param order
* The ui order for the new field, 0 at the top and progressively down from there.
* @return The new metadata field
*/
public static MetadataField<Date> createDateMetadata(String inputID, Opt<String> outputID, String label, boolean readOnly, boolean required, final String pattern, Opt<Integer> order, Opt<String> namespace) {
final SimpleDateFormat dateFormat = getSimpleDateFormatter(pattern);
Fn<Opt<Date>, JValue> dateToJSON = new Fn<Opt<Date>, JValue>() {
@Override
public JValue apply(Opt<Date> date) {
if (date.isNone())
return Jsons.BLANK;
else {
return v(dateFormat.format(date.get()), Jsons.BLANK);
}
}
};
Fn<Object, Date> jsonToDate = new Fn<Object, Date>() {
@Override
public Date apply(Object value) {
try {
String date = (String) value;
if (StringUtils.isBlank(date))
return null;
return dateFormat.parse(date);
} catch (java.text.ParseException e) {
logger.error("Not able to parse date {}: {}", value, e.getMessage());
return null;
}
}
};
MetadataField<Date> dateField = new MetadataField<>(inputID, outputID, label, readOnly, required, null, Opt.none(), Type.DATE, JsonType.DATE, Opt.<Map<String, String>>none(), Opt.<String>none(), dateToJSON, jsonToDate, order, namespace);
if (StringUtils.isNotBlank(pattern)) {
dateField.setPattern(Opt.some(pattern));
}
return dateField;
}
use of com.entwinemedia.fn.Fn in project opencast by opencast.
the class MetadataField method createTemporalMetadata.
private static MetadataField<String> createTemporalMetadata(String inputID, Opt<String> outputID, String label, boolean readOnly, boolean required, final String pattern, final Type type, final JsonType jsonType, Opt<Integer> order, Opt<String> namespace) {
if (StringUtils.isBlank(pattern)) {
throw new IllegalArgumentException("For temporal metadata field " + inputID + " of type " + type + " there needs to be a pattern.");
}
final SimpleDateFormat dateFormat = getSimpleDateFormatter(pattern);
Fn<Object, String> jsonToDateString = new Fn<Object, String>() {
@Override
public String apply(Object value) {
String date = (String) value;
if (StringUtils.isBlank(date))
return "";
try {
dateFormat.parse(date);
} catch (java.text.ParseException e) {
logger.error("Not able to parse date string {}: {}", value, getMessage(e));
return null;
}
return date;
}
};
Fn<Opt<String>, JValue> dateToJSON = new Fn<Opt<String>, JValue>() {
@Override
public JValue apply(Opt<String> periodEncodedString) {
if (periodEncodedString.isNone() || StringUtils.isBlank(periodEncodedString.get())) {
return Jsons.BLANK;
}
// Try to parse the metadata as DCIM metadata.
DCMIPeriod p = EncodingSchemeUtils.decodePeriod(periodEncodedString.get());
if (p != null) {
return v(dateFormat.format(p.getStart()), Jsons.BLANK);
}
// Not DCIM metadata so it might already be formatted (given from the front and is being returned there
try {
dateFormat.parse(periodEncodedString.get());
return v(periodEncodedString.get(), Jsons.BLANK);
} catch (Exception e) {
logger.error("Unable to parse temporal metadata '{}' as either DCIM data or a formatted date using pattern {} because: {}", periodEncodedString.get(), pattern, getStackTrace(e));
throw new IllegalArgumentException(e);
}
}
};
MetadataField<String> temporalStart = new MetadataField<>(inputID, outputID, label, readOnly, required, null, Opt.none(), type, jsonType, Opt.<Map<String, String>>none(), Opt.<String>none(), dateToJSON, jsonToDateString, order, namespace);
temporalStart.setPattern(Opt.some(pattern));
return temporalStart;
}
use of com.entwinemedia.fn.Fn in project opencast by opencast.
the class SchedulerServiceImpl method updateEventInternal.
private void updateEventInternal(final String mpId, String modificationOrigin, Opt<Date> startDateTime, Opt<Date> endDateTime, Opt<String> captureAgentId, Opt<Set<String>> userIds, Opt<MediaPackage> mediaPackage, Opt<Map<String, String>> wfProperties, Opt<Map<String, String>> caMetadata, Opt<Opt<Boolean>> optOutOption, Opt<String> trxId) throws NotFoundException, SchedulerException {
notEmpty(mpId, "mpId");
notEmpty(modificationOrigin, "modificationOrigin");
notNull(startDateTime, "startDateTime");
notNull(endDateTime, "endDateTime");
notNull(captureAgentId, "captureAgentId");
notNull(userIds, "userIds");
notNull(mediaPackage, "mediaPackage");
notNull(wfProperties, "wfProperties");
notNull(caMetadata, "caMetadata");
notNull(optOutOption, "optOutStatus");
notNull(trxId, "trxId");
try {
AQueryBuilder query = assetManager.createQuery();
Props p = new Props(query);
ASelectQuery select = query.select(query.snapshot(), p.start().target(), p.end().target(), query.propertiesOf(WORKFLOW_NAMESPACE, CA_NAMESPACE), p.agent().target(), p.source().target(), p.checksum().target(), p.optOut().target(), p.presenters().target()).where(withOrganization(query).and(query.mediaPackageId(mpId).and(query.version().isLatest()).and(query.hasPropertiesOf(p.namespace()))));
Opt<ARecord> optEvent = select.run().getRecords().head();
if (optEvent.isNone())
throw new NotFoundException("No event found while updating event " + mpId);
ARecord record = optEvent.get();
if (record.getSnapshot().isNone())
throw new NotFoundException("No mediapackage found while updating event " + mpId);
Opt<DublinCoreCatalog> dublinCoreOpt = loadEpisodeDublinCoreFromAsset(record.getSnapshot().get());
if (dublinCoreOpt.isNone())
throw new NotFoundException("No dublincore found while updating event " + mpId);
verifyActive(mpId, record);
Date start = record.getProperties().apply(Properties.getDate(START_DATE_CONFIG));
Date end = record.getProperties().apply(Properties.getDate(END_DATE_CONFIG));
if ((startDateTime.isSome() || endDateTime.isSome()) && endDateTime.getOr(end).before(startDateTime.getOr(start)))
throw new SchedulerException("The end date is before the start date");
String agentId = record.getProperties().apply(Properties.getString(AGENT_CONFIG));
Opt<String> seriesId = Opt.nul(record.getSnapshot().get().getMediaPackage().getSeries());
boolean oldOptOut = record.getProperties().apply(Properties.getBoolean(OPTOUT_CONFIG));
// Get opt out status
Opt<Boolean> optOut = Opt.none();
for (Opt<Boolean> optOutToUpdate : optOutOption) {
optOut = Opt.some(getOptOutStatus(seriesId, optOutToUpdate));
}
if (trxId.isNone()) {
// Check for locked transactions
Opt<String> source = record.getProperties().apply(Properties.getStringOpt(SOURCE_CONFIG));
if (source.isSome() && persistence.hasTransaction(source.get())) {
logger.warn("Unable to update event '{}', source '{}' is currently locked due to an active transaction!", mpId, source.get());
throw new SchedulerTransactionLockException("Unable to update event, locked source " + source.get());
}
// Set to opted out
boolean isNewOptOut = optOut.isSome() && optOut.get();
// Changed to ready for recording
boolean readyForRecording = optOut.isSome() && !optOut.get();
// Has a conflict related property be changed?
boolean propertyChanged = captureAgentId.isSome() || startDateTime.isSome() || endDateTime.isSome();
// Check for conflicting events
if (!isNewOptOut && (readyForRecording || (propertyChanged && !oldOptOut))) {
List<MediaPackage> conflictingEvents = $(findConflictingEvents(captureAgentId.getOr(agentId), startDateTime.getOr(start), endDateTime.getOr(end))).filter(new Fn<MediaPackage, Boolean>() {
@Override
public Boolean apply(MediaPackage mp) {
return !mpId.equals(mp.getIdentifier().compact());
}
}).toList();
if (conflictingEvents.size() > 0) {
logger.info("Unable to update event {}, conflicting events found: {}", mpId, conflictingEvents);
throw new SchedulerConflictException("Unable to update event, conflicting events found for event " + mpId);
}
}
}
Set<String> presenters = getPresenters(record.getProperties().apply(getStringOpt(PRESENTERS_CONFIG)).getOr(""));
Map<String, String> wfProps = record.getProperties().filter(filterByNamespace._2(WORKFLOW_NAMESPACE)).group(toKey, toValue);
Map<String, String> caProperties = record.getProperties().filter(filterByNamespace._2(CA_NAMESPACE)).group(toKey, toValue);
boolean propertiesChanged = false;
boolean dublinCoreChanged = false;
// Get workflow properties
for (Map<String, String> wfPropsToUpdate : wfProperties) {
propertiesChanged = true;
wfProps = wfPropsToUpdate;
}
// Get capture agent properties
for (Map<String, String> caMetadataToUpdate : caMetadata) {
propertiesChanged = true;
caProperties = caMetadataToUpdate;
}
if (captureAgentId.isSome())
propertiesChanged = true;
Opt<AccessControlList> acl = Opt.none();
Opt<DublinCoreCatalog> dublinCore = Opt.none();
Opt<AccessControlList> aclOld = loadEpisodeAclFromAsset(record.getSnapshot().get());
for (MediaPackage mpToUpdate : mediaPackage) {
// Check for series change
if (ne(record.getSnapshot().get().getMediaPackage().getSeries(), mpToUpdate.getSeries())) {
propertiesChanged = true;
seriesId = Opt.nul(mpToUpdate.getSeries());
}
// Check for ACL change and send update
Option<AccessControlList> aclNew = authorizationService.getAcl(mpToUpdate, AclScope.Episode);
if (aclNew.isSome()) {
if (aclOld.isNone() || !AccessControlUtil.equals(aclNew.get(), aclOld.get())) {
acl = aclNew.toOpt();
}
}
// Check for dublin core change and send update
Opt<DublinCoreCatalog> dublinCoreNew = DublinCoreUtil.loadEpisodeDublinCore(workspace, mpToUpdate);
if (dublinCoreNew.isSome() && !DublinCoreUtil.equals(dublinCoreOpt.get(), dublinCoreNew.get())) {
dublinCoreChanged = true;
propertiesChanged = true;
dublinCore = dublinCoreNew;
}
}
Opt<Map<String, String>> finalCaProperties = Opt.none();
if (propertiesChanged) {
finalCaProperties = Opt.some(getFinalAgentProperties(caProperties, wfProps, captureAgentId.getOr(agentId), seriesId, some(dublinCore.getOr(dublinCoreOpt.get()))));
}
String checksum = calculateChecksum(workspace, getEventCatalogUIAdapterFlavors(), startDateTime.getOr(start), endDateTime.getOr(end), captureAgentId.getOr(agentId), userIds.getOr(presenters), mediaPackage.getOr(record.getSnapshot().get().getMediaPackage()), some(dublinCore.getOr(dublinCoreOpt.get())), wfProperties.getOr(wfProps), finalCaProperties.getOr(caProperties), optOut.getOr(oldOptOut), acl.getOr(aclOld.getOr(new AccessControlList())));
if (trxId.isNone()) {
String oldChecksum = record.getProperties().apply(Properties.getString(CHECKSUM));
if (checksum.equals(oldChecksum)) {
logger.debug("Updated event {} has same checksum, ignore update", mpId);
return;
}
}
// Update asset
persistEvent(mpId, modificationOrigin, checksum, startDateTime, endDateTime, captureAgentId, userIds, mediaPackage, wfProperties, finalCaProperties, optOut, Opt.<String>none(), trxId);
if (trxId.isNone()) {
// Send updates
sendUpdateAddEvent(mpId, acl, dublinCore, startDateTime, endDateTime, userIds, Opt.some(agentId), finalCaProperties, optOut);
// Update last modified
if (propertiesChanged || dublinCoreChanged || optOutOption.isSome() || startDateTime.isSome() || endDateTime.isSome()) {
touchLastEntry(agentId);
for (String agent : captureAgentId) {
touchLastEntry(agent);
}
}
}
} catch (NotFoundException e) {
throw e;
} catch (SchedulerException e) {
throw e;
} catch (Exception e) {
logger.error("Failed to update event with id '{}': {}", mpId, getStackTrace(e));
throw new SchedulerException(e);
}
}
Aggregations