/**
* 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.distribution;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.opencastproject.systems.MatterhornConstants.SERVER_URL_PROPERTY;
import static org.opencastproject.util.data.Option.option;
import static org.opencastproject.util.data.functions.Strings.toBool;
import static org.opencastproject.util.data.functions.Strings.trimToNone;
import static org.opencastproject.workflow.handler.distribution.EngagePublicationChannel.CHANNEL_ID;
import org.opencastproject.distribution.api.DistributionException;
import org.opencastproject.distribution.api.DistributionService;
import org.opencastproject.distribution.api.DownloadDistributionService;
import org.opencastproject.job.api.Job;
import org.opencastproject.job.api.JobContext;
import org.opencastproject.mediapackage.Attachment;
import org.opencastproject.mediapackage.MediaPackage;
import org.opencastproject.mediapackage.MediaPackageElement;
import org.opencastproject.mediapackage.MediaPackageElementFlavor;
import org.opencastproject.mediapackage.MediaPackageElementParser;
import org.opencastproject.mediapackage.MediaPackageElements;
import org.opencastproject.mediapackage.MediaPackageException;
import org.opencastproject.mediapackage.MediaPackageReference;
import org.opencastproject.mediapackage.MediaPackageReferenceImpl;
import org.opencastproject.mediapackage.Publication;
import org.opencastproject.mediapackage.PublicationImpl;
import org.opencastproject.mediapackage.selector.SimpleElementSelector;
import org.opencastproject.mediapackage.track.TrackImpl;
import org.opencastproject.search.api.SearchException;
import org.opencastproject.search.api.SearchQuery;
import org.opencastproject.search.api.SearchResult;
import org.opencastproject.search.api.SearchService;
import org.opencastproject.security.api.UnauthorizedException;
import org.opencastproject.serviceregistry.api.ServiceRegistryException;
import org.opencastproject.util.MimeTypes;
import org.opencastproject.util.NotFoundException;
import org.opencastproject.util.UrlSupport;
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.apache.http.client.utils.URIUtils;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;
/**
* The workflow definition for handling "engage publication" operations
*/
public class PublishEngageWorkflowOperationHandler extends AbstractWorkflowOperationHandler {
/** The logging facility */
private static final Logger logger = LoggerFactory.getLogger(PublishEngageWorkflowOperationHandler.class);
/** Configuration properties id */
private static final String ENGAGE_URL_PROPERTY = "org.opencastproject.engage.ui.url";
private static final String STREAMING_URL_PROPERTY = "org.opencastproject.streaming.url";
/** Workflow configuration option keys */
private static final String DOWNLOAD_SOURCE_FLAVORS = "download-source-flavors";
private static final String DOWNLOAD_TARGET_SUBFLAVOR = "download-target-subflavor";
private static final String DOWNLOAD_SOURCE_TAGS = "download-source-tags";
private static final String DOWNLOAD_TARGET_TAGS = "download-target-tags";
private static final String STREAMING_SOURCE_TAGS = "streaming-source-tags";
private static final String STREAMING_TARGET_TAGS = "streaming-target-tags";
private static final String STREAMING_SOURCE_FLAVORS = "streaming-source-flavors";
private static final String STREAMING_TARGET_SUBFLAVOR = "streaming-target-subflavor";
private static final String CHECK_AVAILABILITY = "check-availability";
private static final String STRATEGY = "strategy";
/** The streaming distribution service */
private DistributionService streamingDistributionService = null;
/** The download distribution service */
private DownloadDistributionService downloadDistributionService = null;
/** The search service */
private SearchService searchService = null;
/** The server url */
private URL serverUrl;
/** Whether to distribute to streaming server */
private boolean distributeStreaming = false;
/**
* Callback for the OSGi declarative services configuration.
*
* @param streamingDistributionService
* the streaming distribution service
*/
protected void setStreamingDistributionService(DistributionService streamingDistributionService) {
this.streamingDistributionService = streamingDistributionService;
}
/**
* Callback for the OSGi declarative services configuration.
*
* @param downloadDistributionService
* the download distribution service
*/
protected void setDownloadDistributionService(DownloadDistributionService downloadDistributionService) {
this.downloadDistributionService = downloadDistributionService;
}
/**
* Callback for declarative services configuration that will introduce us to the search service. Implementation
* assumes that the reference is configured as being static.
*
* @param searchService
* an instance of the search service
*/
protected void setSearchService(SearchService searchService) {
this.searchService = searchService;
}
/** The configuration options for this handler */
private static final SortedMap<String, String> CONFIG_OPTIONS;
static {
CONFIG_OPTIONS = new TreeMap<String, String>();
CONFIG_OPTIONS.put(DOWNLOAD_SOURCE_FLAVORS,
"Distribute any mediapackage elements with one of these (comma separated) flavors to download");
CONFIG_OPTIONS.put(STREAMING_TARGET_SUBFLAVOR,
"Target subflavor for elements that have been distributed for downloads");
CONFIG_OPTIONS.put(DOWNLOAD_SOURCE_TAGS,
"Distribute any mediapackage elements with one of these (comma separated) tags to download.");
CONFIG_OPTIONS.put(DOWNLOAD_TARGET_TAGS,
"Add all of these comma separated tags to elements that have been distributed for download.");
CONFIG_OPTIONS.put(STREAMING_SOURCE_FLAVORS,
"Distribute any mediapackage elements with one of these (comma separated) flavors to streaming");
CONFIG_OPTIONS.put(STREAMING_TARGET_SUBFLAVOR,
"Target subflavor for elements that have been distributed for streaming");
CONFIG_OPTIONS.put(STREAMING_SOURCE_TAGS,
"Distribute any mediapackage elements with one of these (comma separated) tags to streaming.");
CONFIG_OPTIONS.put(STREAMING_TARGET_TAGS,
"Add all of these comma separated tags to elements that have been distributed for download.");
CONFIG_OPTIONS.put(CHECK_AVAILABILITY,
"( true | false ) defaults to true. Check if the distributed download artifact is available at its URL");
CONFIG_OPTIONS.put(STRATEGY,
"Strategy if there is an existing Publication");
}
@Override
protected void activate(ComponentContext cc) {
super.activate(cc);
BundleContext bundleContext = cc.getBundleContext();
// Get configuration
serverUrl = UrlSupport.url(bundleContext.getProperty(SERVER_URL_PROPERTY));
distributeStreaming = StringUtils.isNotBlank(bundleContext.getProperty(STREAMING_URL_PROPERTY));
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workflow.api.WorkflowOperationHandler#getConfigurationOptions()
*/
@Override
public SortedMap<String, String> getConfigurationOptions() {
return CONFIG_OPTIONS;
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workflow.api.WorkflowOperationHandler#start(org.opencastproject.workflow.api.WorkflowInstance,
* JobContext)
*/
@Override
public WorkflowOperationResult start(final WorkflowInstance workflowInstance, JobContext context)
throws WorkflowOperationException {
logger.debug("Running engage publication workflow operation");
MediaPackage mediaPackage = workflowInstance.getMediaPackage();
WorkflowOperationInstance op = workflowInstance.getCurrentOperation();
// Check which tags have been configured
String downloadSourceTags = StringUtils.trimToEmpty(op.getConfiguration(DOWNLOAD_SOURCE_TAGS));
String downloadTargetTags = StringUtils.trimToEmpty(op.getConfiguration(DOWNLOAD_TARGET_TAGS));
String downloadSourceFlavors = StringUtils.trimToEmpty(op.getConfiguration(DOWNLOAD_SOURCE_FLAVORS));
String downloadTargetSubflavor = StringUtils.trimToNull(op.getConfiguration(DOWNLOAD_TARGET_SUBFLAVOR));
String streamingSourceTags = StringUtils.trimToEmpty(op.getConfiguration(STREAMING_SOURCE_TAGS));
String streamingTargetTags = StringUtils.trimToEmpty(op.getConfiguration(STREAMING_TARGET_TAGS));
String streamingSourceFlavors = StringUtils.trimToEmpty(op.getConfiguration(STREAMING_SOURCE_FLAVORS));
String streamingTargetSubflavor = StringUtils.trimToNull(op.getConfiguration(STREAMING_TARGET_SUBFLAVOR));
String republishStrategy = StringUtils.trimToEmpty(op.getConfiguration(STRATEGY));
boolean checkAvailability = option(op.getConfiguration(CHECK_AVAILABILITY)).bind(trimToNone).map(toBool)
.getOrElse(true);
String[] sourceDownloadTags = StringUtils.split(downloadSourceTags, ",");
String[] targetDownloadTags = StringUtils.split(downloadTargetTags, ",");
String[] sourceDownloadFlavors = StringUtils.split(downloadSourceFlavors, ",");
String[] sourceStreamingTags = StringUtils.split(streamingSourceTags, ",");
String[] targetStreamingTags = StringUtils.split(streamingTargetTags, ",");
String[] sourceStreamingFlavors = StringUtils.split(streamingSourceFlavors, ",");
if (sourceDownloadTags.length == 0 && sourceDownloadFlavors.length == 0 && sourceStreamingTags.length == 0
&& sourceStreamingFlavors.length == 0) {
logger.warn("No tags or flavors have been specified, so nothing will be published to the engage publication channel");
return createResult(mediaPackage, Action.CONTINUE);
}
// Parse the download target flavor
MediaPackageElementFlavor downloadSubflavor = null;
if (downloadTargetSubflavor != null) {
try {
downloadSubflavor = MediaPackageElementFlavor.parseFlavor(downloadTargetSubflavor);
} catch (IllegalArgumentException e) {
throw new WorkflowOperationException(e);
}
}
// Parse the streaming target flavor
MediaPackageElementFlavor streamingSubflavor = null;
if (streamingTargetSubflavor != null) {
try {
streamingSubflavor = MediaPackageElementFlavor.parseFlavor(streamingTargetSubflavor);
} catch (IllegalArgumentException e) {
throw new WorkflowOperationException(e);
}
}
// Configure the download element selector
SimpleElementSelector downloadElementSelector = new SimpleElementSelector();
for (String flavor : sourceDownloadFlavors) {
downloadElementSelector.addFlavor(MediaPackageElementFlavor.parseFlavor(flavor));
}
for (String tag : sourceDownloadTags) {
downloadElementSelector.addTag(tag);
}
// Configure the streaming element selector
SimpleElementSelector streamingElementSelector = new SimpleElementSelector();
for (String flavor : sourceStreamingFlavors) {
streamingElementSelector.addFlavor(MediaPackageElementFlavor.parseFlavor(flavor));
}
for (String tag : sourceStreamingTags) {
streamingElementSelector.addTag(tag);
}
// Select the appropriate elements for download and streaming
Collection<MediaPackageElement> downloadElements = downloadElementSelector.select(mediaPackage, false);
Collection<MediaPackageElement> streamingElements = streamingElementSelector.select(mediaPackage, false);
try {
Set<String> downloadElementIds = new HashSet<String>();
Set<String> streamingElementIds = new HashSet<String>();
// Look for elements matching the tag
for (MediaPackageElement elem : downloadElements) {
downloadElementIds.add(elem.getIdentifier());
}
for (MediaPackageElement elem : streamingElements) {
streamingElementIds.add(elem.getIdentifier());
}
// Also distribute the security configuration
// -----
// This was removed in the meantime by a fix for MH-8515, but could now be used again.
// -----
Attachment[] securityAttachments = mediaPackage.getAttachments(MediaPackageElements.XACML_POLICY);
if (securityAttachments != null && securityAttachments.length > 0) {
for (Attachment a : securityAttachments) {
downloadElementIds.add(a.getIdentifier());
streamingElementIds.add(a.getIdentifier());
}
}
removePublicationElement(mediaPackage);
switch (republishStrategy) {
case ("merge"):
// nothing to do here. other publication strategies can be added to this list later on
break;
default:
retractFromEngage(mediaPackage);
}
List<Job> jobs = new ArrayList<Job>();
//distribute Elements
try {
if (downloadElementIds.size() > 0) {
Job job = downloadDistributionService.distribute(CHANNEL_ID, mediaPackage, downloadElementIds, checkAvailability);
if (job != null) {
jobs.add(job);
}
}
if (distributeStreaming) {
for (String elementId : streamingElementIds) {
Job job = streamingDistributionService.distribute(CHANNEL_ID, mediaPackage, elementId);
if (job != null) {
jobs.add(job);
}
}
}
} catch (DistributionException e) {
throw new WorkflowOperationException(e);
}
if (jobs.size() < 1) {
logger.info("No mediapackage element was found for distribution to engage");
return createResult(mediaPackage, Action.CONTINUE);
}
// Wait until all distribution jobs have returned
if (!waitForStatus(jobs.toArray(new Job[jobs.size()])).isSuccess())
throw new WorkflowOperationException("One of the distribution jobs did not complete successfully");
logger.debug("Distribute of mediapackage {} completed", mediaPackage);
String engageUrlString = null;
try {
MediaPackage mediaPackageForSearch = getMediaPackageForSearchIndex(mediaPackage, jobs, downloadSubflavor,
targetDownloadTags, downloadElementIds, streamingSubflavor, streamingElementIds, targetStreamingTags);
// MH-10216, check if only merging into existing mediapackage
removePublicationElement(mediaPackage);
switch (republishStrategy) {
case ("merge"):
// merge() returns merged mediapackage or null mediaPackage is not published
mediaPackageForSearch = merge(mediaPackageForSearch);
if (mediaPackageForSearch == null) {
logger.info("Skipping republish for {} since it is not currently published", mediaPackage.getIdentifier().toString());
return createResult(mediaPackage, Action.SKIP);
}
break;
default:
// nothing to do here
}
if (!isPublishable(mediaPackageForSearch))
throw new WorkflowOperationException("Media package does not meet criteria for publication");
logger.info("Publishing media package {} to search index", mediaPackageForSearch);
URL engageBaseUrl = null;
engageUrlString = StringUtils.trimToNull(workflowInstance.getOrganization().getProperties()
.get(ENGAGE_URL_PROPERTY));
if (engageUrlString != null) {
engageBaseUrl = new URL(engageUrlString);
} else {
engageBaseUrl = serverUrl;
logger.info(
"Using 'server.url' as a fallback for the non-existing organization level key '{}' for the publication url",
ENGAGE_URL_PROPERTY);
}
// Create new distribution element
URI engageUri = URIUtils.resolve(engageBaseUrl.toURI(), "/engage/ui/watch.html?id="
+ mediaPackage.getIdentifier().compact());
Publication publicationElement = PublicationImpl.publication(UUID.randomUUID().toString(), CHANNEL_ID,
engageUri, MimeTypes.parseMimeType("text/html"));
mediaPackage.add(publicationElement);
// Adding media package to the search index
Job publishJob = null;
try {
publishJob = searchService.add(mediaPackageForSearch);
if (!waitForStatus(publishJob).isSuccess()) {
throw new WorkflowOperationException("Mediapackage " + mediaPackageForSearch.getIdentifier()
+ " could not be published");
}
} catch (SearchException e) {
throw new WorkflowOperationException("Error publishing media package", e);
} catch (MediaPackageException e) {
throw new WorkflowOperationException("Error parsing media package", e);
}
logger.debug("Publishing of mediapackage {} completed", mediaPackage);
return createResult(mediaPackage, Action.CONTINUE);
} catch (MalformedURLException e) {
logger.error("{} is malformed: {}", ENGAGE_URL_PROPERTY, engageUrlString);
throw new WorkflowOperationException(e);
} catch (Throwable t) {
if (t instanceof WorkflowOperationException)
throw (WorkflowOperationException) t;
else
throw new WorkflowOperationException(t);
}
} catch (Exception e) {
if (e instanceof WorkflowOperationException) {
throw (WorkflowOperationException) e;
} else {
throw new WorkflowOperationException(e);
}
}
}
/**
* Returns a mediapackage that only contains elements that are marked for distribution.
*
* @param current
* the current mediapackage
* @param jobs
* the distribution jobs
* @param downloadSubflavor
* flavor to be applied to elements distributed to download
* @param downloadTargetTags
* tags to be applied to elements distributed to downloads
* @param downloadElementIds
* identifiers for elements that have been distributed to downloads
* @param streamingSubflavor
* flavor to be applied to elements distributed to streaming
* @param streamingElementIds
* identifiers for elements that have been distributed to streaming
* @param streamingTargetTags
* tags to be applied to elements distributed to streaming
* @return the new mediapackage
*/
protected MediaPackage getMediaPackageForSearchIndex(MediaPackage current, List<Job> jobs,
MediaPackageElementFlavor downloadSubflavor, String[] downloadTargetTags, Set<String> downloadElementIds,
MediaPackageElementFlavor streamingSubflavor, Set<String> streamingElementIds, String[] streamingTargetTags)
throws MediaPackageException, NotFoundException, ServiceRegistryException, WorkflowOperationException {
MediaPackage mp = (MediaPackage) current.clone();
// All the jobs have passed, let's update the mediapackage with references to the distributed elements
List<String> elementsToPublish = new ArrayList<String>();
Map<String, String> distributedElementIds = new HashMap<String, String>();
for (Job entry : jobs) {
Job job = serviceRegistry.getJob(entry.getId());
// If there is no payload, then the item has not been distributed.
if (job.getPayload() == null)
continue;
List <MediaPackageElement> distributedElements = null;
try {
distributedElements = (List <MediaPackageElement>) MediaPackageElementParser.getArrayFromXml(job.getPayload());
} catch (MediaPackageException e) {
throw new WorkflowOperationException(e);
}
// If the job finished successfully, but returned no new element, the channel simply doesn't support this
// kind of element. So we just keep on looping.
if (distributedElements == null || distributedElements.size() < 1)
continue;
for (MediaPackageElement distributedElement : distributedElements) {
String sourceElementId = distributedElement.getIdentifier();
if (sourceElementId != null) {
MediaPackageElement sourceElement = mp.getElementById(sourceElementId);
// Make sure the mediapackage is prompted to create a new identifier for this element
distributedElement.setIdentifier(null);
if (sourceElement != null) {
// Adjust the flavor and tags for downloadable elements
if (downloadElementIds.contains(sourceElementId)) {
if (downloadSubflavor != null) {
MediaPackageElementFlavor flavor = sourceElement.getFlavor();
if (flavor != null) {
MediaPackageElementFlavor newFlavor = new MediaPackageElementFlavor(flavor.getType(),
downloadSubflavor.getSubtype());
distributedElement.setFlavor(newFlavor);
}
}
}
// Adjust the flavor and tags for streaming elements
else if (streamingElementIds.contains(sourceElementId)) {
if (streamingSubflavor != null && streamingElementIds.contains(sourceElementId)) {
MediaPackageElementFlavor flavor = sourceElement.getFlavor();
if (flavor != null) {
MediaPackageElementFlavor newFlavor = new MediaPackageElementFlavor(flavor.getType(),
streamingSubflavor.getSubtype());
distributedElement.setFlavor(newFlavor);
}
}
}
// Copy references from the source elements to the distributed elements
MediaPackageReference ref = sourceElement.getReference();
if (ref != null && mp.getElementByReference(ref) != null) {
MediaPackageReference newReference = (MediaPackageReference) ref.clone();
distributedElement.setReference(newReference);
}
}
}
if (isStreamingFormat(distributedElement))
applyTags(distributedElement, streamingTargetTags);
else applyTags(distributedElement, downloadTargetTags);
// Add the new element to the mediapackage
mp.add(distributedElement);
elementsToPublish.add(distributedElement.getIdentifier());
distributedElementIds.put(sourceElementId, distributedElement.getIdentifier());
}
}
// Mark everything that is set for removal
List<MediaPackageElement> removals = new ArrayList<MediaPackageElement>();
for (MediaPackageElement element : mp.getElements()) {
if (!elementsToPublish.contains(element.getIdentifier())) {
removals.add(element);
}
}
// Translate references to the distributed artifacts
for (MediaPackageElement element : mp.getElements()) {
if (removals.contains(element))
continue;
// Is the element referencing anything?
MediaPackageReference reference = element.getReference();
if (reference == null)
continue;
// See if the element has been distributed
String distributedElementId = distributedElementIds.get(reference.getIdentifier());
if (distributedElementId == null)
continue;
MediaPackageReference translatedReference = new MediaPackageReferenceImpl(mp.getElementById(distributedElementId));
if (reference.getProperties() != null) {
translatedReference.getProperties().putAll(reference.getProperties());
}
// Set the new reference
element.setReference(translatedReference);
}
// Remove everything we don't want to add to publish
for (MediaPackageElement element : removals) {
mp.remove(element);
}
return mp;
}
/**
* Checks if the MediaPackage track transport protocol is a streaming format protocol
* @param element The MediapackageElement to analyze
* @return true if it is a TrackImpl and has a streaming protocol as transport
*/
private boolean isStreamingFormat(MediaPackageElement element) {
if (element instanceof TrackImpl) {
if (TrackImpl.StreamingProtocol.RTMP.equals(((TrackImpl) element).getTransport())
|| TrackImpl.StreamingProtocol.RTMPE.equals(((TrackImpl) element).getTransport())
|| TrackImpl.StreamingProtocol.HLS.equals(((TrackImpl) element).getTransport())
|| TrackImpl.StreamingProtocol.DASH.equals(((TrackImpl) element).getTransport())
|| TrackImpl.StreamingProtocol.HDS.equals(((TrackImpl) element).getTransport())
|| TrackImpl.StreamingProtocol.SMOOTH.equals(((TrackImpl) element).getTransport()))
return true;
}
return false;
}
/**
* Adds Tags to a MediaPackageElement
* @param element the element that needs the tags
* @param tags the list of tags to apply
*/
private void applyTags(MediaPackageElement element, String[] tags) {
for (String tag : tags) {
element.addTag(tag);
}
}
/** Media package must meet these criteria in order to be published. */
private boolean isPublishable(MediaPackage mp) {
boolean hasTitle = !isBlank(mp.getTitle());
if (!hasTitle)
logger.warn("Media package does not meet criteria for publication: There is no title");
boolean hasTracks = mp.hasTracks();
if (!hasTracks)
logger.warn("Media package does not meet criteria for publication: There are no tracks");
return hasTitle && hasTracks;
}
protected MediaPackage getDistributedMediapackage(String mediaPackageID) throws WorkflowOperationException {
MediaPackage mediaPackage = null;
SearchQuery query = new SearchQuery().withId(mediaPackageID);
query.includeEpisodes(true);
query.includeSeries(false);
SearchResult result = searchService.getByQuery(query);
if (result.size() == 0) {
logger.info("The search service doesn't know mediapackage {}.", mediaPackageID);
return mediaPackage; // i.e. null
} else if (result.size() > 1) {
logger.warn("More than one mediapackage with id {} returned from search service", mediaPackageID);
throw new WorkflowOperationException("More than one mediapackage with id " + mediaPackageID + " found");
} else {
// else, merge the new with the existing (new elements will overwrite existing elements)
mediaPackage = result.getItems()[0].getMediaPackage();
}
return mediaPackage;
}
/**
* MH-10216, method copied from the original RepublishWorkflowOperationHandler
* Merges mediapackage with published mediapackage.
*
* @param mediaPackageForSearch
* @return merged mediapackage or null if a published medipackage was not found
* @throws WorkflowOperationException
*/
protected MediaPackage merge(MediaPackage mediaPackageForSearch) throws WorkflowOperationException {
MediaPackage mergedMediaPackage = null;
mergedMediaPackage = mergePackages(mediaPackageForSearch,
getDistributedMediapackage(mediaPackageForSearch.toString()));
return mergedMediaPackage;
}
/**
* MH-10216, Copied from the original RepublishWorkflowOperationHandler
*
* Merges the updated mediapackage with the one that is currently published in a way where the updated elements
* replace existing ones in the published mediapackage based on their flavor.
* <p>
* If <code>publishedMp</code> is <code>null</code>, this method returns the updated mediapackage without any
* modifications.
*
* @param updatedMp
* the updated media package
* @param publishedMp
* the mediapackage that is currently published
* @return the merged mediapackage
*/
protected MediaPackage mergePackages(MediaPackage updatedMp, MediaPackage publishedMp) {
if (publishedMp == null)
return updatedMp;
MediaPackage mergedMediaPackage = (MediaPackage) updatedMp.clone();
for (MediaPackageElement element : publishedMp.elements()) {
String type = element.getElementType().toString().toLowerCase();
if (updatedMp.getElementsByFlavor(element.getFlavor()).length == 0) {
logger.info("Merging {} '{}' into the updated mediapackage", type, element.getIdentifier());
mergedMediaPackage.add((MediaPackageElement) element.clone());
} else {
logger.info(String.format("Overwriting existing %s '%s' with '%s' in the updated mediapackage",
type, element.getIdentifier(), updatedMp.getElementsByFlavor(element.getFlavor())[0].getIdentifier()));
}
}
return mergedMediaPackage;
}
private void removePublicationElement(MediaPackage mediaPackage) {
for (Publication publicationElement : mediaPackage.getPublications()) {
if (CHANNEL_ID.equals(publicationElement.getChannel())) {
mediaPackage.remove(publicationElement);
}
}
}
/**
* Removes every Publication for Searchindex from Mediapackage
* Removes Mediapackage from Searchindex
* @param mediaPackage Mediapackage
* @param mediaPackageForSearch Mediapackage prepared for searchIndex
* @throws WorkflowOperationException
*/
private void retractFromEngage(MediaPackage mediaPackage) throws WorkflowOperationException {
List<Job> jobs = new ArrayList<Job>();
Set<String> elementIds = new HashSet<String>();
try {
MediaPackage distributedMediaPackage = getDistributedMediapackage(mediaPackage.toString());
if (distributedMediaPackage != null) {
for (MediaPackageElement element : distributedMediaPackage.getElements()) {
elementIds.add(element.getIdentifier());
}
//bulk retraction
if (elementIds.size() > 0) {
Job retractDownloadDistributionJob = downloadDistributionService.retract(CHANNEL_ID, distributedMediaPackage, elementIds);
if (retractDownloadDistributionJob != null) {
jobs.add(retractDownloadDistributionJob);
}
}
if (distributeStreaming) {
for (MediaPackageElement element : distributedMediaPackage.getElements()) {
Job retractStreamingJob = streamingDistributionService.retract(CHANNEL_ID, distributedMediaPackage, element.getIdentifier());
if (retractStreamingJob != null) {
jobs.add(retractStreamingJob);
}
}
}
Job deleteSearchJob = null;
logger.info("Retracting already published Elements for Mediapackage: {}", mediaPackage.getIdentifier().toString());
deleteSearchJob = searchService.delete(mediaPackage.getIdentifier().toString());
if (deleteSearchJob != null) {
jobs.add(deleteSearchJob);
}
}
// Wait until all retraction jobs have returned
if (!waitForStatus(jobs.toArray(new Job[jobs.size()])).isSuccess()) {
throw new WorkflowOperationException("One of the retraction jobs did not complete successfully");
}
} catch (DistributionException e) {
throw new WorkflowOperationException(e);
} catch (SearchException e) {
throw new WorkflowOperationException("Error retracting media package", e);
} catch (UnauthorizedException | NotFoundException ex) {
logger.error("Retraction failed of Mediapackage: { }", mediaPackage.getIdentifier().toString(), ex);
}
}
}