/**
* 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.notification;
import org.opencastproject.job.api.JobContext;
import org.opencastproject.mediapackage.MediaPackage;
import org.opencastproject.mediapackage.MediaPackageParser;
import org.opencastproject.search.api.SearchQuery;
import org.opencastproject.search.api.SearchResult;
import org.opencastproject.search.api.SearchService;
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.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* Workflow Operation for POSTing a MediaPackage via HTTP
*/
public class MediaPackagePostOperationHandler extends AbstractWorkflowOperationHandler {
/** The logging facility */
private static final Logger logger = LoggerFactory.getLogger(MediaPackagePostOperationHandler.class);
/** search service **/
private SearchService searchService;
public void setSearchService(SearchService searchService) {
this.searchService = searchService;
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workflow.api.WorkflowOperationHandler#getConfigurationOptions()
*/
@Override
public SortedMap<String, String> getConfigurationOptions() {
return Configuration.OPTIONS;
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workflow.api.WorkflowOperationHandler#start(org.opencastproject.workflow.api.WorkflowInstance,
* JobContext)
*/
public WorkflowOperationResult start(final WorkflowInstance workflowInstance, JobContext context)
throws WorkflowOperationException {
// get configuration
WorkflowOperationInstance currentOperation = workflowInstance.getCurrentOperation();
Configuration config = new Configuration(currentOperation);
MediaPackage workflowMP = workflowInstance.getMediaPackage();
MediaPackage mp = workflowMP;
/* Check if we need to replace the Mediapackage we got with the published
* Mediapackage from the Search Service */
if (config.mpFromSearch()) {
SearchQuery searchQuery = new SearchQuery();
searchQuery.withId(mp.getIdentifier().toString());
SearchResult result = searchService.getByQuery(searchQuery);
if (result.size() != 1) {
throw new WorkflowOperationException("Received multiple results for identifier"
+ "\"" + mp.getIdentifier().toString() + "\" from search service. ");
}
logger.info("Getting Mediapackage from Search Service");
mp = result.getItems()[0].getMediaPackage();
}
logger.info("Submitting \"" + mp.getTitle() + "\" (" + mp.getIdentifier().toString() + ") as "
+ config.getFormat().name() + " to " + config.getUrl().toString());
try {
// serialize MediaPackage to target format
String mpStr;
if (config.getFormat() == Configuration.Format.JSON) {
mpStr = MediaPackageParser.getAsJSON(mp);
} else {
mpStr = MediaPackageParser.getAsXml(mp);
}
// Log mediapackge
if (config.debug()) {
logger.info(mpStr);
}
// constrcut message body
List<NameValuePair> data = new ArrayList<NameValuePair>();
data.add(new BasicNameValuePair("mediapackage", mpStr));
data.addAll(config.getAdditionalFields());
// construct POST
HttpPost post = new HttpPost(config.getUrl());
post.setEntity(new UrlEncodedFormEntity(data, config.getEncoding()));
// execute POST
DefaultHttpClient client = new DefaultHttpClient();
// Handle authentication
if (config.authenticate()) {
URL targetUrl = config.getUrl().toURL();
client.getCredentialsProvider().setCredentials(
new AuthScope(targetUrl.getHost(), targetUrl.getPort()),
config.getCredentials());
}
HttpResponse response = client.execute(post);
// throw Exception if target host did not return 200
int status = response.getStatusLine().getStatusCode();
if ((status >= 200) && (status < 300)) {
if (config.debug()) {
logger.info("Successfully submitted \"" + mp.getTitle()
+ "\" (" + mp.getIdentifier().toString() + ") to " + config.getUrl().toString()
+ ": " + status);
}
} else if (status == 418) {
logger.warn("Submitted \"" + mp.getTitle() + "\" ("
+ mp.getIdentifier().toString() + ") to " + config.getUrl().toString()
+ ": The target claims to be a teapot. "
+ "The Reason for this is probably an insane programmer.");
} else {
throw new WorkflowOperationException("Faild to submit \"" + mp.getTitle()
+ "\" (" + mp.getIdentifier().toString() + "), " + config.getUrl().toString()
+ " answered with: " + Integer.toString(status));
}
} catch (Exception e) {
if (e instanceof WorkflowOperationException) {
throw (WorkflowOperationException) e;
} else {
logger.error("Submitting mediapackage failed: {}", e.toString());
throw new WorkflowOperationException(e);
}
}
return createResult(workflowMP, Action.CONTINUE);
}
// <editor-fold defaultstate="collapsed" desc="Inner class that wraps around this WorkflowOperations Configuration">
private static class Configuration {
public enum Format {
XML, JSON
};
// Key for the WorkflowOperation Configuration
public static final String PROPERTY_URL = "url";
public static final String PROPERTY_FORMAT = "format";
public static final String PROPERTY_ENCODING = "encoding";
public static final String PROPERTY_AUTH = "auth.enabled";
public static final String PROPERTY_AUTHUSER = "auth.username";
public static final String PROPERTY_AUTHPASSWD = "auth.password";
public static final String PROPERTY_DEBUG = "debug";
public static final String PROPERTY_MEDIAPACKAGE_TYPE = "mediapackage.type";
public static final TreeMap<String, String> OPTIONS;
static {
OPTIONS = new TreeMap<String, String>();
OPTIONS.put(Configuration.PROPERTY_URL,
"The URL to which the MediaPackage will be submitted");
OPTIONS.put(Configuration.PROPERTY_FORMAT,
"The output format for the MediaPackage (default: XML)");
OPTIONS.put(Configuration.PROPERTY_ENCODING,
"Message Encoding (default: UTF-8)");
OPTIONS.put(Configuration.PROPERTY_AUTH,
"The authentication method to use (no/http-digest)");
OPTIONS.put(Configuration.PROPERTY_AUTHUSER,
"The username to use for authentication");
OPTIONS.put(Configuration.PROPERTY_AUTHPASSWD,
"The password to use for authentication");
OPTIONS.put(Configuration.PROPERTY_DEBUG,
"If this options is set the message body returned by target host is dumped to log (default: no)");
OPTIONS.put(Configuration.PROPERTY_MEDIAPACKAGE_TYPE,
"Type of Mediapackage to send (workflow, search; default: search)");
}
// Configuration values
private URI url;
private Format format = Format.XML;
private String encoding = "UTF-8";
private boolean authenticate = false;
private UsernamePasswordCredentials credentials = null;
private List<NameValuePair> additionalFields = new ArrayList<NameValuePair>();
private boolean debug = false;
private boolean mpFromSearch = true;
Configuration(WorkflowOperationInstance operation) throws WorkflowOperationException {
try {
Set<String> keys = operation.getConfigurationKeys();
// configure URL
if (keys.contains(PROPERTY_URL)) {
url = new URI(operation.getConfiguration(PROPERTY_URL));
} else {
throw new IllegalArgumentException("No target URL provided.");
}
// configure format
if (keys.contains(PROPERTY_FORMAT)) {
format = Format.valueOf(operation.getConfiguration(PROPERTY_FORMAT).toUpperCase());
}
// configure message encoding
if (keys.contains(PROPERTY_ENCODING)) {
encoding = operation.getConfiguration(PROPERTY_ENCODING);
}
// configure authentication
if (keys.contains(PROPERTY_AUTH)) {
String auth = operation.getConfiguration(PROPERTY_AUTH).toUpperCase();
if (!("NO").equals(auth) && !("FALSE").equals(auth)) {
String username = operation.getConfiguration(PROPERTY_AUTHUSER);
String password = operation.getConfiguration(PROPERTY_AUTHPASSWD);
if (username == null || password == null) {
throw new WorkflowOperationException("Username and Password must be provided for authentication!");
}
credentials = new UsernamePasswordCredentials(username, password);
authenticate = true;
}
}
// Configure debug mode
if (keys.contains(PROPERTY_DEBUG))
{
String debugstr = operation.getConfiguration(PROPERTY_DEBUG).trim().toUpperCase();
debug = "YES".equals(debugstr) || "TRUE".equals(debugstr);
}
// Configure debug mode
if (keys.contains(PROPERTY_MEDIAPACKAGE_TYPE))
{
String cfgval = operation.getConfiguration(PROPERTY_MEDIAPACKAGE_TYPE).trim().toUpperCase();
mpFromSearch = "SEARCH".equals(cfgval);
}
// get additional form fields
for (Iterator<String> iter = operation.getConfigurationKeys().iterator(); iter.hasNext();) {
String key = iter.next();
if (key.startsWith("+")) {
String value = operation.getConfiguration(key);
additionalFields.add(new BasicNameValuePair(key.substring(1), value));
}
}
} catch (Exception e) {
throw new WorkflowOperationException("Faild to configure operation instance.", e);
}
}
public URI getUrl() {
return url;
}
public Format getFormat() {
return format;
}
public String getEncoding() {
return encoding;
}
public boolean authenticate() {
return authenticate;
}
public UsernamePasswordCredentials getCredentials() {
return credentials;
}
public List<NameValuePair> getAdditionalFields() {
return additionalFields;
}
public boolean debug() {
return debug;
}
public boolean mpFromSearch() {
return mpFromSearch;
}
}
// </editor-fold>
}