Java tutorial
/** * Copyright 2009, 2010 The Regents of the University of California * Licensed 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://www.osedu.org/licenses/ECL-2.0 * * 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.distribution.hydrant; import org.opencastproject.distribution.api.DistributionException; import org.opencastproject.distribution.api.DistributionService; import org.opencastproject.job.api.AbstractJobProducer; import org.opencastproject.job.api.Job; import org.opencastproject.mediapackage.MediaPackage; import org.opencastproject.mediapackage.MediaPackageElement; import org.opencastproject.mediapackage.MediaPackageElementParser; import org.opencastproject.mediapackage.MediaPackageException; import org.opencastproject.mediapackage.MediaPackageParser; import org.opencastproject.mediapackage.Track; import org.opencastproject.security.api.OrganizationDirectoryService; import org.opencastproject.security.api.SecurityService; import org.opencastproject.security.api.UserDirectoryService; import org.opencastproject.serviceregistry.api.ServiceRegistry; import org.opencastproject.serviceregistry.api.ServiceRegistryException; import org.opencastproject.util.FileSupport; import org.opencastproject.util.NotFoundException; import org.opencastproject.util.PathSupport; import org.opencastproject.util.UrlSupport; import org.opencastproject.workspace.api.Workspace; import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.DeleteMethod; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.PutMethod; import org.apache.commons.httpclient.methods.multipart.StringPart; import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity; import org.apache.commons.httpclient.methods.multipart.Part; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Distributes media to the local media delivery directory. */ public class HydrantDistributionService extends AbstractJobProducer implements DistributionService { /** Logging facility */ private static final Logger logger = LoggerFactory.getLogger(HydrantDistributionService.class); /** Receipt type */ public static final String JOB_TYPE = "org.opencastproject.distribution.hydrant"; /** List of available operations on jobs */ private enum Operation { Distribute, Retract }; /** The workspace reference */ protected Workspace workspace = null; /** The service registry */ protected ServiceRegistry serviceRegistry = null; /** The security service */ protected SecurityService securityService = null; /** The user directory service */ protected UserDirectoryService userDirectoryService = null; /** The organization directory service */ protected OrganizationDirectoryService organizationDirectoryService = null; /** The distribution directory */ protected File distributionDirectory = null; /** The base URL for hydrant */ protected String hydrantUrl = null; /** The admin username for hydrant */ protected String hydrantAdminUsername = null; /** The admin password for hydrant */ protected String hydrantAdminPassword = null; /** * Creates a new instance of the hydrant distribution service. */ public HydrantDistributionService() { super(JOB_TYPE); } protected void activate(ComponentContext cc) { // Get the configured hydrant and server URLs if (cc != null) { hydrantUrl = StringUtils .trimToNull(cc.getBundleContext().getProperty("org.opencastproject.hydrant.url")); if (hydrantUrl == null) logger.warn("Hydrant url was not set (org.opencastproject.hydrant.url)"); else logger.info("hydrant url is {}", hydrantUrl); hydrantAdminUsername = StringUtils .trimToNull(cc.getBundleContext().getProperty("org.opencastproject.hydrant.admin.username")); if (hydrantAdminUsername == null) logger.warn("Hydrant admin username was not set (org.opencastproject.hydrant.admin.username)"); else logger.info("hydrant admin username is {}", hydrantAdminUsername); hydrantAdminPassword = StringUtils .trimToNull(cc.getBundleContext().getProperty("org.opencastproject.hydrant.admin.password")); if (hydrantAdminPassword == null) logger.warn("Hydrant admin password was not set (org.opencastproject.hydrant.admin.password)"); else logger.info("hydrant admin password is {}", hydrantAdminPassword); } } /** * {@inheritDoc} * * @see org.opencastproject.distribution.api.DistributionService#distribute(MediaPackage, java.lang.String) */ @Override public Job distribute(MediaPackage mediapackage, String elementId) throws DistributionException, MediaPackageException { if (mediapackage == null) throw new MediaPackageException("Mediapackage must be specified"); if (elementId == null) throw new MediaPackageException("Element ID must be specified"); if (StringUtils.isBlank(hydrantUrl)) throw new IllegalStateException("Hydrant url must be set (org.opencastproject.hydrant.url)"); if (StringUtils.isBlank(hydrantAdminUsername)) throw new IllegalStateException( "Hydrant admin username must be set (org.opencastproject.hydrant.admin.username)"); if (StringUtils.isBlank(hydrantAdminPassword)) throw new IllegalStateException( "Hydrant admin password must be set (org.opencastproject.hydrant.admin.password)"); try { return serviceRegistry.createJob(JOB_TYPE, Operation.Distribute.toString(), Arrays.asList(MediaPackageParser.getAsXml(mediapackage), elementId)); } catch (ServiceRegistryException e) { throw new DistributionException("Unable to create a job", e); } } /** * Distributes the mediapackage's element to the location that is returned by the concrete implementation. In * addition, a representation of the distributed element is added to the mediapackage. * * @see org.opencastproject.distribution.api.DistributionService#distribute(String, MediaPackageElement) */ protected MediaPackageElement distribute(Job job, MediaPackage mediapackage, String elementId) throws DistributionException { if (mediapackage == null) throw new IllegalArgumentException("Mediapackage must be specified"); if (elementId == null) throw new IllegalArgumentException("Element ID must be specified"); String mediaPackageId = mediapackage.getIdentifier().compact(); MediaPackageElement element = mediapackage.getElementById(elementId); // Make sure the element exists if (mediapackage.getElementById(elementId) == null) throw new IllegalStateException("No element " + elementId + " found in mediapackage"); try { // The hydrant server only supports tracks if (!(element instanceof Track)) { return null; } String parentpid = mediapackage.getTitle(); if (parentpid == null) { throw new DistributionException("Could not find Hydrant pid in mediapackage."); } logger.trace("Found parent pid: {}", parentpid); try { String url = UrlSupport.concat(new String[] { hydrantUrl, "derivatives" }); MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager(); HttpClient client = new HttpClient(mgr); Credentials defaultcreds = new UsernamePasswordCredentials(hydrantAdminUsername, hydrantAdminPassword); client.getState().setCredentials(AuthScope.ANY, defaultcreds); client.getParams().setAuthenticationPreemptive(true); PostMethod post = new PostMethod(url); post.setDoAuthentication(true); Part[] parts = { new StringPart("stream_url", element.getURI().toString()), new StringPart("master", parentpid), }; post.setRequestEntity(new MultipartRequestEntity(parts, post.getParams())); int status = client.executeMethod(post); logger.debug("Got status: " + status); logger.trace("Got response body: " + post.getResponseBodyAsString()); } catch (IOException e) { logger.debug("Exception distributing to Hydrant: " + e.getCause()); throw new DistributionException("Error distributing to Hydrant instance", e); } logger.info("Distributed {} to hydrant", elementId); // Create a representation of the distributed file in the mediapackage MediaPackageElement distributedElement = (MediaPackageElement) element.clone(); //TODO Create and set a valid distribution URI /* try { distributedElement.setURI(getDistributionUri(mediaPackageId, element)); } catch (URISyntaxException e) { throw new DistributionException("Distributed element produces an invalid URI", e); } */ distributedElement.setIdentifier(null); logger.info("Finished distribution of {}", element); return distributedElement; } catch (Exception e) { logger.warn("Error distributing " + element, e); if (e instanceof DistributionException) { throw (DistributionException) e; } else { throw new DistributionException(e); } } } /** * {@inheritDoc} * * @see org.opencastproject.distribution.api.DistributionService#retract(org.opencastproject.mediapackage.MediaPackage, * java.lang.String) */ public Job retract(MediaPackage mediaPackage, String elementId) throws DistributionException { if (mediaPackage == null) throw new IllegalArgumentException("Mediapackage must be specified"); if (elementId == null) throw new IllegalArgumentException("Element ID must be specified"); if (StringUtils.isBlank(hydrantUrl)) throw new IllegalStateException("Hydrant url must be set (org.opencastproject.hydrant.url)"); if (StringUtils.isBlank(hydrantAdminUsername)) throw new IllegalStateException( "Hydrant admin username must be set (org.opencastproject.hydrant.admin.username)"); if (StringUtils.isBlank(hydrantAdminPassword)) throw new IllegalStateException( "Hydrant admin password must be set (org.opencastproject.hydrant.admin.password)"); try { List<String> arguments = new ArrayList<String>(); arguments.add(MediaPackageParser.getAsXml(mediaPackage)); arguments.add(elementId); return serviceRegistry.createJob(JOB_TYPE, Operation.Retract.toString(), arguments); } catch (ServiceRegistryException e) { throw new DistributionException("Unable to create a job", e); } } /** * Retracts the mediapackage with the given identifier from the distribution channel. * * @param job * the associated job * @param mediapackage * the mediapackage * @param elementId * the element identifier * @return the retracted element or <code>null</code> if the element was not retracted */ protected MediaPackageElement retract(Job job, MediaPackage mediapackage, String elementId) throws DistributionException { if (mediapackage == null) throw new IllegalArgumentException("Mediapackage must be specified"); if (elementId == null) throw new IllegalArgumentException("Element ID must be specified"); // Make sure the element exists MediaPackageElement element = mediapackage.getElementById(elementId); if (element == null) throw new IllegalStateException("No element " + elementId + " found in mediapackage"); //TODO implement this!! logger.info("Nothing done...this method is not implemented yet."); return element; /* // Find the element that has been created as part of the distribution process String mediaPackageId = mediapackage.getIdentifier().compact(); URI distributedURI = null; MediaPackageElement distributedElement = null; try { distributedURI = getDistributionUri(mediaPackageId, element); for (MediaPackageElement e : mediapackage.getElements()) { if (distributedURI.equals(e.getURI())) { distributedElement = e; break; } } } catch (URISyntaxException e) { throw new DistributionException("Retracted element produces an invalid URI", e); } // Has this element been distributed? if (distributedElement == null) return null; String mediapackageId = mediapackage.getIdentifier().compact(); try { File mediapackageDir = getMediaPackageDirectory(mediapackageId); File elementDir = getDistributionFile(mediapackage, element); // Does the file exist? If not, the current element has not been distributed to this channel // or has been removed otherwise if (!elementDir.exists()) return distributedElement; // Try to remove the file and - if possible - the parent folder FileUtils.forceDelete(elementDir); if (mediapackageDir.list().length == 0) { FileSupport.delete(mediapackageDir); } logger.info("Finished rectracting element {} of media package {}", elementId, mediapackageId); return distributedElement; } catch (Exception e) { logger.warn("Error retracting element " + elementId + " of mediapackage " + mediapackageId, e); if (e instanceof DistributionException) { throw (DistributionException) e; } else { throw new DistributionException(e); } } */ } /** * Gets the destination file to copy the contents of a mediapackage element. * * @param mediaPackage * the media package * @param element * The mediapackage element being distributed * @return The file to copy the content to */ protected File getDistributionFile(MediaPackage mediaPackage, MediaPackageElement element) { String elementId = element.getIdentifier(); String fileName = FilenameUtils.getName(element.getURI().toString()); String directoryName = distributionDirectory.getAbsolutePath(); String destinationFileName = PathSupport.concat( new String[] { directoryName, mediaPackage.getIdentifier().compact(), elementId, fileName }); return new File(destinationFileName); } /** * Gets the URI for the element to be distributed. * * @param mediaPackageId * the mediapackage identifier * @param element * The mediapackage element being distributed * @return The resulting URI after distribution * @throws URISyntaxException * if the concrete implementation tries to create a malformed uri */ protected URI getDistributionUri(String mediaPackageId, MediaPackageElement element) throws URISyntaxException { //TODO Implement this! String elementId = element.getIdentifier(); String destinationURI = UrlSupport.concat(new String[] { hydrantUrl, mediaPackageId, elementId }); return new URI(destinationURI); } /** * Gets the directory containing the distributed files for this mediapackage. * * @param mediaPackageId * the mediapackage ID * @return the filesystem directory */ protected File getMediaPackageDirectory(String mediaPackageId) { return new File(distributionDirectory, mediaPackageId); } /** * {@inheritDoc} * * @see org.opencastproject.job.api.AbstractJobProducer#process(org.opencastproject.job.api.Job) */ @Override protected String process(Job job) throws Exception { Operation op = null; String operation = job.getOperation(); List<String> arguments = job.getArguments(); try { op = Operation.valueOf(operation); MediaPackage mediapackage = MediaPackageParser.getFromXml(arguments.get(0)); String elementId = arguments.get(1); switch (op) { case Distribute: MediaPackageElement distributedElement = distribute(job, mediapackage, elementId); return (distributedElement != null) ? MediaPackageElementParser.getAsXml(distributedElement) : null; case Retract: MediaPackageElement retractedElement = retract(job, mediapackage, elementId); return (retractedElement != null) ? MediaPackageElementParser.getAsXml(retractedElement) : null; default: throw new IllegalStateException("Don't know how to handle operation '" + operation + "'"); } } catch (IllegalArgumentException e) { throw new ServiceRegistryException("This service can't handle operations of type '" + op + "'", e); } catch (IndexOutOfBoundsException e) { throw new ServiceRegistryException( "This argument list for operation '" + op + "' does not meet expectations", e); } catch (Exception e) { throw new ServiceRegistryException("Error handling operation '" + op + "'", e); } } /** * Callback for the OSGi environment to set the workspace reference. * * @param workspace * the workspace */ protected void setWorkspace(Workspace workspace) { this.workspace = workspace; } /** * Callback for the OSGi environment to set the service registry reference. * * @param serviceRegistry * the service registry */ protected void setServiceRegistry(ServiceRegistry serviceRegistry) { this.serviceRegistry = serviceRegistry; } /** * {@inheritDoc} * * @see org.opencastproject.job.api.AbstractJobProducer#getServiceRegistry() */ @Override protected ServiceRegistry getServiceRegistry() { return serviceRegistry; } /** * Callback for setting the security service. * * @param securityService * the securityService to set */ public void setSecurityService(SecurityService securityService) { this.securityService = securityService; } /** * Callback for setting the user directory service. * * @param userDirectoryService * the userDirectoryService to set */ public void setUserDirectoryService(UserDirectoryService userDirectoryService) { this.userDirectoryService = userDirectoryService; } /** * Sets a reference to the organization directory service. * * @param organizationDirectory * the organization directory */ public void setOrganizationDirectoryService(OrganizationDirectoryService organizationDirectory) { this.organizationDirectoryService = organizationDirectory; } /** * {@inheritDoc} * * @see org.opencastproject.job.api.AbstractJobProducer#getSecurityService() */ @Override protected SecurityService getSecurityService() { return securityService; } /** * {@inheritDoc} * * @see org.opencastproject.job.api.AbstractJobProducer#getUserDirectoryService() */ @Override protected UserDirectoryService getUserDirectoryService() { return userDirectoryService; } /** * {@inheritDoc} * * @see org.opencastproject.job.api.AbstractJobProducer#getOrganizationDirectoryService() */ @Override protected OrganizationDirectoryService getOrganizationDirectoryService() { return organizationDirectoryService; } }