View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  package org.opencastproject.workflow.handler.workflow;
23  
24  import static java.lang.String.format;
25  
26  import org.opencastproject.job.api.JobContext;
27  import org.opencastproject.mediapackage.MediaPackage;
28  import org.opencastproject.mediapackage.MediaPackageElement;
29  import org.opencastproject.mediapackage.MediaPackageElementFlavor;
30  import org.opencastproject.mediapackage.selector.AbstractMediaPackageElementSelector;
31  import org.opencastproject.mediapackage.selector.SimpleElementSelector;
32  import org.opencastproject.serviceregistry.api.ServiceRegistry;
33  import org.opencastproject.util.FileSupport;
34  import org.opencastproject.util.NotFoundException;
35  import org.opencastproject.util.UrlSupport;
36  import org.opencastproject.util.data.Option;
37  import org.opencastproject.workflow.api.AbstractWorkflowOperationHandler;
38  import org.opencastproject.workflow.api.ConfiguredTagsAndFlavors;
39  import org.opencastproject.workflow.api.WorkflowInstance;
40  import org.opencastproject.workflow.api.WorkflowOperationException;
41  import org.opencastproject.workflow.api.WorkflowOperationHandler;
42  import org.opencastproject.workflow.api.WorkflowOperationInstance;
43  import org.opencastproject.workflow.api.WorkflowOperationResult;
44  import org.opencastproject.workflow.api.WorkflowOperationResult.Action;
45  import org.opencastproject.workspace.api.Workspace;
46  
47  import org.apache.commons.io.FilenameUtils;
48  import org.apache.commons.lang3.StringUtils;
49  import org.osgi.service.component.annotations.Component;
50  import org.osgi.service.component.annotations.Reference;
51  import org.slf4j.Logger;
52  import org.slf4j.LoggerFactory;
53  
54  import java.io.File;
55  import java.io.IOException;
56  import java.util.Collection;
57  import java.util.List;
58  
59  /**
60   * Workflow operation handler for copying video data through NFS
61   */
62  @Component(
63      immediate = true,
64      service = WorkflowOperationHandler.class,
65      property = {
66          "service.description=Copy Workflow Operation Handler",
67          "workflow.operation=copy"
68      }
69  )
70  public class CopyWorkflowOperationHandler extends AbstractWorkflowOperationHandler {
71    /** Configuration key for the \"tag\" of the track to use as a source input */
72    public static final String OPT_SOURCE_TAGS = "source-tags";
73  
74    /** Configuration key for the \"flavor\" of the track to use as a source input */
75    public static final String OPT_SOURCE_FLAVORS = "source-flavors";
76  
77    /** Configuration key for the directory where the file must be delivered */
78    public static final String OPT_TARGET_DIRECTORY = "target-directory";
79  
80    /** Configuration key for the name of the target file */
81    public static final String OPT_TARGET_FILENAME = "target-filename";
82  
83    /** The logging facility */
84    private static final Logger logger = LoggerFactory.getLogger(CopyWorkflowOperationHandler.class);
85  
86    /** The workspace reference */
87    protected Workspace workspace = null;
88  
89    /**
90     * Callback for the OSGi environment to set the workspace reference.
91     *
92     * @param workspace
93     *          the workspace
94     */
95    @Reference
96    protected void setWorkspace(Workspace workspace) {
97      this.workspace = workspace;
98    }
99  
100   /**
101    * {@inheritDoc}
102    */
103   @Override
104   public WorkflowOperationResult start(WorkflowInstance workflowInstance, JobContext context)
105           throws WorkflowOperationException {
106     logger.debug("Running copy workflow operation on workflow {}", workflowInstance.getId());
107 
108     MediaPackage mediaPackage = workflowInstance.getMediaPackage();
109     WorkflowOperationInstance currentOperation = workflowInstance.getCurrentOperation();
110 
111     // Check which tags have been configured
112     ConfiguredTagsAndFlavors tagsAndFlavors = getTagsAndFlavors(workflowInstance,
113         Configuration.many, Configuration.many, Configuration.none, Configuration.none);
114     List<String> sourceTagsOption = tagsAndFlavors.getSrcTags();
115     List<MediaPackageElementFlavor> sourceFlavorsOption = tagsAndFlavors.getSrcFlavors();
116     String targetDirectoryOption = StringUtils.trimToNull(currentOperation.getConfiguration(OPT_TARGET_DIRECTORY));
117     Option<String> targetFilenameOption = Option.option(StringUtils.trimToNull(currentOperation
118             .getConfiguration(OPT_TARGET_FILENAME)));
119 
120     StringBuilder sb = new StringBuilder();
121     sb.append("Parameters passed to copy workflow operation:");
122     sb.append("\n source-tags: ").append(sourceTagsOption);
123     sb.append("\n source-flavors: ").append(sourceFlavorsOption);
124     sb.append("\n target-directory: ").append(targetDirectoryOption);
125     sb.append("\n target-filename: ").append(targetFilenameOption);
126     logger.debug(sb.toString());
127 
128     AbstractMediaPackageElementSelector<MediaPackageElement> elementSelector = new SimpleElementSelector();
129 
130     // Make sure either one of tags or flavors are provided
131     if (sourceTagsOption.isEmpty() && sourceFlavorsOption.isEmpty()) {
132       logger.info("No source tags or flavors have been specified, not matching anything");
133       return createResult(mediaPackage, Action.CONTINUE);
134     }
135 
136     // Make the target filename and directory are provided
137     if (StringUtils.isBlank(targetDirectoryOption)) {
138       throw new WorkflowOperationException("No target directory has been set for the copy operation!");
139     }
140 
141     // Select the source flavors
142     for (MediaPackageElementFlavor flavor : sourceFlavorsOption) {
143       try {
144         elementSelector.addFlavor(flavor);
145       } catch (IllegalArgumentException e) {
146         throw new WorkflowOperationException("Source flavor '" + flavor + "' is malformed");
147       }
148     }
149 
150     // Select the source tags
151     for (String tag : sourceTagsOption) {
152       elementSelector.addTag(tag);
153     }
154 
155     // Look for elements matching the tag
156     Collection<MediaPackageElement> elements = elementSelector.select(mediaPackage, true);
157 
158     // Check the the number of element returned
159     if (elements.size() == 0) {
160       // If no one found, we skip the operation
161       return createResult(mediaPackage, Action.SKIP);
162     } else if (elements.size() == 1) {
163       for (MediaPackageElement element : elements) {
164         logger.debug("Copy single element to: {}", targetDirectoryOption);
165         final String fileName;
166         if (targetFilenameOption.isSome()) {
167           fileName = targetFilenameOption.get();
168         } else {
169           fileName = FilenameUtils.getBaseName(element.getURI().toString());
170         }
171 
172         String ext = FilenameUtils.getExtension(element.getURI().toString());
173         ext = ext.length() > 0 ? ".".concat(ext) : "";
174         File targetFile = new File(UrlSupport.concat(targetDirectoryOption, fileName.concat(ext)));
175 
176         copyElement(element, targetFile);
177       }
178     } else {
179       logger.debug("Copy multiple elements to: {}", targetDirectoryOption);
180       int i = 1;
181       for (MediaPackageElement element : elements) {
182         final String fileName;
183         if (targetFilenameOption.isSome()) {
184           fileName = String.format(targetFilenameOption.get(), i);
185         } else {
186           fileName = FilenameUtils.getBaseName(element.getURI().toString());
187         }
188 
189         String ext = FilenameUtils.getExtension(element.getURI().toString());
190         ext = ext.length() > 0 ? ".".concat(ext) : "";
191         File targetFile = new File(UrlSupport.concat(targetDirectoryOption, fileName + ext));
192 
193         copyElement(element, targetFile);
194         i++;
195       }
196     }
197 
198     return createResult(mediaPackage, Action.CONTINUE);
199   }
200 
201   private void copyElement(MediaPackageElement element, File targetFile) throws WorkflowOperationException {
202     File sourceFile;
203     try {
204       sourceFile = workspace.get(element.getURI());
205     } catch (NotFoundException e) {
206       throw new WorkflowOperationException("Unable to find " + element.getURI() + " in the workspace", e);
207     } catch (IOException e) {
208       throw new WorkflowOperationException("Error loading " + element.getURI() + " from the workspace", e);
209     }
210 
211     logger.debug("Start copying element {} to target {}.", sourceFile.getPath(), targetFile.getPath());
212     try {
213       FileSupport.copy(sourceFile, targetFile);
214     } catch (IOException e) {
215       throw new WorkflowOperationException(format("Unable to copy %s to %s: %s", sourceFile.getPath(),
216               targetFile.getPath(), e.getMessage()));
217     }
218     logger.debug("Element {} copied to target {}.", sourceFile.getPath(), targetFile.getPath());
219   }
220 
221   @Reference
222   @Override
223   public void setServiceRegistry(ServiceRegistry serviceRegistry) {
224     super.setServiceRegistry(serviceRegistry);
225   }
226 
227 }