/**
* 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.workflow;
import static com.entwinemedia.fn.Stream.$;
import static org.opencastproject.workflow.handler.workflow.ImportWorkflowPropertiesWOH.loadPropertiesElementFromMediaPackage;
import static org.opencastproject.workflow.handler.workflow.ImportWorkflowPropertiesWOH.loadPropertiesFromXml;
import org.opencastproject.job.api.JobContext;
import org.opencastproject.mediapackage.Attachment;
import org.opencastproject.mediapackage.MediaPackage;
import org.opencastproject.mediapackage.MediaPackageElementBuilder;
import org.opencastproject.mediapackage.MediaPackageElementBuilderFactory;
import org.opencastproject.mediapackage.MediaPackageElementFlavor;
import org.opencastproject.mediapackage.MediaPackageElements;
import org.opencastproject.util.MimeTypes;
import org.opencastproject.workflow.api.AbstractWorkflowOperationHandler;
import org.opencastproject.workflow.api.WorkflowInstance;
import org.opencastproject.workflow.api.WorkflowOperationException;
import org.opencastproject.workflow.api.WorkflowOperationResult;
import org.opencastproject.workflow.api.WorkflowOperationResult.Action;
import org.opencastproject.workspace.api.Workspace;
import com.entwinemedia.fn.Stream;
import com.entwinemedia.fn.data.Opt;
import com.entwinemedia.fn.fns.Strings;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;
/**
* Workflow operation handler for exporting workflow properties.
*/
public class ExportWorkflowPropertiesWOH extends AbstractWorkflowOperationHandler {
/** Configuration options */
public static final String KEYS_PROPERTY = "keys";
public static final String TARGET_FLAVOR_PROPERTY = "target-flavor";
public static final String TARGET_TAGS_PROPERTY = "target-tags";
public static final String DEFAULT_TARGET_FLAVOR = MediaPackageElements.PROCESSING_PROPERTIES.toString();
public static final String EXPORTED_PROPERTIES_FILENAME = "processing-properties.xml";
/** The configuration options for this handler */
public static final SortedMap<String, String> CONFIG_OPTIONS;
static {
CONFIG_OPTIONS = new TreeMap<String, String>();
CONFIG_OPTIONS.put(KEYS_PROPERTY,
"The workflow property keys that need to be persisted. If the option is not specified, all defined properties should be persisted.");
CONFIG_OPTIONS.put(TARGET_FLAVOR_PROPERTY, "The flavor to apply to the exported workflow properties");
CONFIG_OPTIONS.put(TARGET_TAGS_PROPERTY, "The tags to apply to the exported workflow properties");
}
/** The logging facility */
private static final Logger logger = LoggerFactory.getLogger(ExportWorkflowPropertiesWOH.class);
/** The workspace */
private Workspace workspace;
/** OSGi DI */
void setWorkspace(Workspace workspace) {
this.workspace = workspace;
}
@Override
public WorkflowOperationResult start(WorkflowInstance workflowInstance, JobContext context)
throws WorkflowOperationException {
logger.info("Start exporting workflow properties for workflow {}", workflowInstance);
final MediaPackage mediaPackage = workflowInstance.getMediaPackage();
final Set<String> keys = $(getOptConfig(workflowInstance, KEYS_PROPERTY)).bind(Strings.splitCsv).toSet();
final String targetFlavorString = getOptConfig(workflowInstance, TARGET_FLAVOR_PROPERTY).getOr(DEFAULT_TARGET_FLAVOR);
final Stream<String> targetTags = $(getOptConfig(workflowInstance, TARGET_TAGS_PROPERTY)).bind(Strings.splitCsv);
final MediaPackageElementFlavor targetFlavor = MediaPackageElementFlavor.parseFlavor(targetFlavorString);
// Read optional existing workflow properties from mediapackage
Properties workflowProps = new Properties();
Opt<Attachment> existingPropsElem = loadPropertiesElementFromMediaPackage(targetFlavor, workflowInstance);
if (existingPropsElem.isSome()) {
workflowProps = loadPropertiesFromXml(workspace, existingPropsElem.get().getURI());
// Remove specified keys
for (String key : keys)
workflowProps.remove(key);
}
// Extend with specified properties
for (String key : workflowInstance.getConfigurationKeys()) {
if (keys.isEmpty() || keys.contains(key))
workflowProps.put(key, workflowInstance.getConfiguration(key));
}
// Store properties as an attachment
Attachment attachment;
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
workflowProps.storeToXML(out, null, "UTF-8");
String elementId = UUID.randomUUID().toString();
URI uri = workspace.put(mediaPackage.getIdentifier().compact(), elementId, EXPORTED_PROPERTIES_FILENAME,
new ByteArrayInputStream(out.toByteArray()));
MediaPackageElementBuilder builder = MediaPackageElementBuilderFactory.newInstance().newElementBuilder();
attachment = (Attachment) builder.elementFromURI(uri, Attachment.TYPE, targetFlavor);
attachment.setMimeType(MimeTypes.XML);
} catch (IOException e) {
logger.error("Unable to store workflow properties as Attachment with flavor '{}': {}", targetFlavorString,
ExceptionUtils.getStackTrace(e));
throw new WorkflowOperationException("Unable to store workflow properties as Attachment", e);
}
// Add the target tags
for (String tag : targetTags) {
logger.trace("Tagging with '{}'", tag);
attachment.addTag(tag);
}
// Update attachment
if (existingPropsElem.isSome())
mediaPackage.remove(existingPropsElem.get());
mediaPackage.add(attachment);
logger.info("Exported workflow properties with flavor '{}' and content: {}", targetFlavorString,
propertiesAsString(workflowProps));
return createResult(mediaPackage, null, Action.CONTINUE, 0);
}
/** Serialize the properties into a string. */
private String propertiesAsString(Properties properties) {
StringWriter writer = new StringWriter();
properties.list(new PrintWriter(writer));
return writer.getBuffer().toString();
}
}