/** * 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.ingest; import org.opencastproject.job.api.JobContext; import org.opencastproject.mediapackage.MediaPackage; import org.opencastproject.mediapackage.MediaPackageElement; import org.opencastproject.security.api.TrustedHttpClient; import org.opencastproject.security.api.TrustedHttpClientException; import org.opencastproject.serviceregistry.api.ServiceRegistration; import org.opencastproject.serviceregistry.api.ServiceRegistryException; 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.opencastproject.workingfilerepository.api.WorkingFileRepository; import org.opencastproject.workspace.api.Workspace; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpDelete; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.SortedMap; import java.util.TreeMap; /** * Downloads all external URI's to the working file repository and optionally deletes external working file repository * resources */ public class IngestDownloadWorkflowOperationHandler extends AbstractWorkflowOperationHandler { /** The logger */ private static final Logger logger = LoggerFactory.getLogger(IngestDownloadWorkflowOperationHandler.class); /** Deleting external working file repository URI's after download config key */ public static final String DELETE_EXTERNAL = "delete-external"; /** The configuration properties */ protected SortedMap<String, String> configurationOptions = null; /** * The workspace to use in retrieving and storing files. */ protected Workspace workspace; /** The http client to use when connecting to remote servers */ protected TrustedHttpClient client = null; /** The default no-arg constructor builds the configuration options set */ public IngestDownloadWorkflowOperationHandler() { configurationOptions = new TreeMap<String, String>(); configurationOptions.put(DELETE_EXTERNAL, "Whether to try to delete external working file repository URIs. Default is false."); } /** * Sets the workspace to use. * * @param workspace * the workspace */ public void setWorkspace(Workspace workspace) { this.workspace = workspace; } /** * Sets the trusted http client * * @param client * the trusted http client */ public void setTrustedHttpClient(TrustedHttpClient client) { this.client = client; } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.AbstractWorkflowOperationHandler#start(org.opencastproject.workflow.api.WorkflowInstance, * JobContext) */ @Override public WorkflowOperationResult start(WorkflowInstance workflowInstance, JobContext context) throws WorkflowOperationException { MediaPackage mediaPackage = workflowInstance.getMediaPackage(); WorkflowOperationInstance currentOperation = workflowInstance.getCurrentOperation(); boolean deleteExternal = BooleanUtils.toBoolean(currentOperation.getConfiguration(DELETE_EXTERNAL)); String baseUrl = workspace.getBaseUri().toString(); List<URI> externalUris = new ArrayList<URI>(); for (MediaPackageElement element : mediaPackage.getElements()) { if (element.getURI() == null) continue; URI originalElementUri = element.getURI(); if (originalElementUri.toString().startsWith(baseUrl)) { logger.info("Skipping downloading already existing element {}", originalElementUri); continue; } // Download the external URI File file; try { file = workspace.get(element.getURI()); } catch (Exception e) { logger.warn("Unable to download the external element {}", element.getURI()); throw new WorkflowOperationException("Unable to download the external element " + element.getURI(), e); } // Put to working file repository and rewrite URI on element InputStream in = null; try { in = new FileInputStream(file); URI uri = workspace.put(mediaPackage.getIdentifier().compact(), element.getIdentifier(), FilenameUtils.getName(element.getURI().getPath()), in); element.setURI(uri); } catch (Exception e) { logger.warn("Unable to store downloaded element '{}': {}", element.getURI(), e.getMessage()); throw new WorkflowOperationException("Unable to store downloaded element " + element.getURI(), e); } finally { IOUtils.closeQuietly(in); try { workspace.delete(originalElementUri); } catch (Exception e) { logger.warn("Unable to delete ingest-downloaded element {}: {}", element.getURI(), e); } } logger.info("Downloaded the external element {}", originalElementUri); // Store origianl URI for deletion externalUris.add(originalElementUri); } if (!deleteExternal || externalUris.size() == 0) return createResult(mediaPackage, Action.CONTINUE); // Find all external working file repository base Urls logger.debug("Assembling list of external working file repositories"); List<String> externalWfrBaseUrls = new ArrayList<String>(); try { for (ServiceRegistration reg : serviceRegistry.getServiceRegistrationsByType(WorkingFileRepository.SERVICE_TYPE)) { if (baseUrl.startsWith(reg.getHost())) { logger.trace("Skpping local working file repository"); continue; } externalWfrBaseUrls.add(UrlSupport.concat(reg.getHost(), reg.getPath())); } logger.debug("{} external working file repositories found", externalWfrBaseUrls.size()); } catch (ServiceRegistryException e) { logger.error("Unable to load WFR services from service registry: {}", e.getMessage()); throw new WorkflowOperationException(e); } for (URI uri : externalUris) { String elementUri = uri.toString(); // Delete external working file repository URI's String wfrBaseUrl = null; for (String url : externalWfrBaseUrls) { if (elementUri.startsWith(url)) { wfrBaseUrl = url; break; } } if (wfrBaseUrl == null) { logger.info("Unable to delete external URI {}, no working file repository found", elementUri); continue; } HttpDelete delete; if (elementUri.startsWith(UrlSupport.concat(wfrBaseUrl, WorkingFileRepository.MEDIAPACKAGE_PATH_PREFIX))) { String wfrDeleteUrl = elementUri.substring(0, elementUri.lastIndexOf("/")); delete = new HttpDelete(wfrDeleteUrl); } else if (elementUri.startsWith(UrlSupport.concat(wfrBaseUrl, WorkingFileRepository.COLLECTION_PATH_PREFIX))) { delete = new HttpDelete(elementUri); } else { logger.info("Unable to handle working file repository URI {}", elementUri); continue; } HttpResponse response = null; try { response = client.execute(delete); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == HttpStatus.SC_NO_CONTENT || statusCode == HttpStatus.SC_OK) { logger.info("Sucessfully deleted external URI {}", delete.getURI()); } else if (statusCode == HttpStatus.SC_NOT_FOUND) { logger.info("External URI {} has already been deleted", delete.getURI()); } else { logger.info("Unable to delete external URI {}, status code '{}' returned", delete.getURI(), statusCode); } } catch (TrustedHttpClientException e) { logger.warn("Unable to execute DELETE request on external URI {}", delete.getURI()); throw new WorkflowOperationException(e); } finally { client.close(response); } } return createResult(mediaPackage, Action.CONTINUE); } /** * {@inheritDoc} * * @see org.opencastproject.workflow.api.WorkflowOperationHandler#getConfigurationOptions() */ @Override public SortedMap<String, String> getConfigurationOptions() { return configurationOptions; } }