/**
* Licensed to The Apereo Foundation under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
*
* The Apereo Foundation licenses this file to you under the Educational
* Community License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License
* at:
*
* http://opensource.org/licenses/ecl2.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
*/
package org.opencastproject.workflow.handler.assetmanager;
import static org.opencastproject.assetmanager.api.AssetManager.DEFAULT_OWNER;
import static org.opencastproject.util.data.Collections.smap;
import static org.opencastproject.util.data.Tuple.tuple;
import org.opencastproject.assetmanager.api.AssetManager;
import org.opencastproject.job.api.JobContext;
import org.opencastproject.mediapackage.MediaPackage;
import org.opencastproject.mediapackage.MediaPackageElement;
import org.opencastproject.mediapackage.MediaPackageException;
import org.opencastproject.mediapackage.MediaPackageReference;
import org.opencastproject.mediapackage.Publication;
import org.opencastproject.mediapackage.selector.SimpleElementSelector;
import org.opencastproject.workflow.api.AbstractWorkflowOperationHandler;
import org.opencastproject.workflow.api.WorkflowInstance;
import org.opencastproject.workflow.api.WorkflowOperationException;
import org.opencastproject.workflow.api.WorkflowOperationInstance;
import org.opencastproject.workflow.api.WorkflowOperationResult;
import org.opencastproject.workflow.api.WorkflowOperationResult.Action;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
/**
* Workflow operation for taking a snapshot of a media package.
*
* @see AssetManager#takeSnapshot(String, MediaPackage)
*/
public class AssetManagerSnapshotWorkflowOperationHandler extends AbstractWorkflowOperationHandler {
private static final Logger logger = LoggerFactory.getLogger(AssetManagerSnapshotWorkflowOperationHandler.class);
/** The asset manager. */
private AssetManager assetManager;
/** The configuration options for this handler */
private static final SortedMap<String, String> CONFIG_OPTIONS = smap(
tuple("source-tags", "Add any media package elements with one of these (comma separated) tags"),
tuple("source-flavors", "Add any media package elements with one of these (comma separated) flavors"));
@Override
public SortedMap<String, String> getConfigurationOptions() {
return CONFIG_OPTIONS;
}
/** OSGi DI */
public void setAssetManager(AssetManager assetManager) {
this.assetManager = assetManager;
}
@Override
public WorkflowOperationResult start(WorkflowInstance wi, JobContext ctx)
throws WorkflowOperationException {
final MediaPackage mpWorkflow = wi.getMediaPackage();
final WorkflowOperationInstance currentOperation = wi.getCurrentOperation();
// Check which tags have been configured
final String tags = StringUtils.trimToNull(currentOperation.getConfiguration("source-tags"));
final String sourceFlavorsString = StringUtils.trimToEmpty(currentOperation.getConfiguration("source-flavors"));
final String[] sourceFlavors = StringUtils.split(sourceFlavorsString, ",");
if (sourceFlavors.length < 1 && tags == null)
logger.debug("No source tags have been specified, so everything will be added to the AssetManager");
final List<String> tagSet;
// If a set of tags has been specified, use it
if (tags != null) {
tagSet = asList(tags);
} else {
tagSet = new ArrayList<>();
}
try {
final MediaPackage mpAssetManager = getMediaPackageForArchival(mpWorkflow, tagSet, sourceFlavors);
if (mpAssetManager != null) {
logger.info("Take snapshot of media package {}", mpAssetManager);
// adding media package to the episode service
assetManager.takeSnapshot(DEFAULT_OWNER, mpAssetManager);
logger.debug("Snapshot operation complete");
return createResult(mpWorkflow, Action.CONTINUE);
} else {
return createResult(mpWorkflow, Action.CONTINUE);
}
} catch (Throwable t) {
throw new WorkflowOperationException(t);
}
}
protected MediaPackage getMediaPackageForArchival(MediaPackage current, List<String> tags, String[] sourceFlavors)
throws MediaPackageException {
MediaPackage mp = (MediaPackage) current.clone();
Collection<MediaPackageElement> keep;
if (tags.isEmpty() && sourceFlavors.length < 1) {
keep = new ArrayList<>(Arrays.asList(current.getElementsByTags(tags)));
} else {
SimpleElementSelector simpleElementSelector = new SimpleElementSelector();
for (String flavor : sourceFlavors) {
simpleElementSelector.addFlavor(flavor);
}
for (String tag : tags) {
simpleElementSelector.addTag(tag);
}
keep = simpleElementSelector.select(current, false);
}
// Also archive the publication elements
for (Publication publication : current.getPublications()) {
keep.add(publication);
}
// Mark everything that is set for removal
List<MediaPackageElement> removals = new ArrayList<MediaPackageElement>();
for (MediaPackageElement element : mp.getElements()) {
if (!keep.contains(element)) {
removals.add(element);
}
}
// Fix references and flavors
for (MediaPackageElement element : mp.getElements()) {
if (removals.contains(element))
continue;
// Is the element referencing anything?
MediaPackageReference reference = element.getReference();
if (reference != null) {
Map<String, String> referenceProperties = reference.getProperties();
MediaPackageElement referencedElement = mp.getElementByReference(reference);
// if we are distributing the referenced element, everything is fine. Otherwise...
if (referencedElement != null && removals.contains(referencedElement)) {
// Follow the references until we find a flavor
MediaPackageElement parent;
while ((parent = current.getElementByReference(reference)) != null) {
if (parent.getFlavor() != null && element.getFlavor() == null) {
element.setFlavor(parent.getFlavor());
}
if (parent.getReference() == null) {
break;
}
reference = parent.getReference();
}
// Done. Let's cut the path but keep references to the mediapackage itself
if (reference != null && reference.getType().equals(MediaPackageReference.TYPE_MEDIAPACKAGE))
element.setReference(reference);
else if (reference != null && (referenceProperties == null || referenceProperties.size() == 0))
element.clearReference();
else {
// Ok, there is more to that reference than just pointing at an element. Let's keep the original,
// you never know.
removals.remove(referencedElement);
referencedElement.setURI(null);
referencedElement.setChecksum(null);
}
}
}
}
// Remove everything we don't want to add to publish
for (MediaPackageElement element : removals) {
mp.remove(element);
}
return mp;
}
}