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  package org.opencastproject.workflow.handler.assetmanager;
22  
23  import static org.opencastproject.assetmanager.api.AssetManager.DEFAULT_OWNER;
24  
25  import org.opencastproject.assetmanager.api.AssetManager;
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.MediaPackageException;
31  import org.opencastproject.mediapackage.MediaPackageReference;
32  import org.opencastproject.mediapackage.Publication;
33  import org.opencastproject.mediapackage.selector.SimpleElementSelector;
34  import org.opencastproject.serviceregistry.api.ServiceRegistry;
35  import org.opencastproject.workflow.api.AbstractWorkflowOperationHandler;
36  import org.opencastproject.workflow.api.ConfiguredTagsAndFlavors;
37  import org.opencastproject.workflow.api.WorkflowInstance;
38  import org.opencastproject.workflow.api.WorkflowOperationException;
39  import org.opencastproject.workflow.api.WorkflowOperationHandler;
40  import org.opencastproject.workflow.api.WorkflowOperationInstance;
41  import org.opencastproject.workflow.api.WorkflowOperationResult;
42  import org.opencastproject.workflow.api.WorkflowOperationResult.Action;
43  
44  import org.osgi.service.component.ComponentContext;
45  import org.osgi.service.component.annotations.Activate;
46  import org.osgi.service.component.annotations.Component;
47  import org.osgi.service.component.annotations.Reference;
48  import org.slf4j.Logger;
49  import org.slf4j.LoggerFactory;
50  
51  import java.util.ArrayList;
52  import java.util.Arrays;
53  import java.util.Collection;
54  import java.util.List;
55  
56  /**
57   * Workflow operation for taking a snapshot of a media package.
58   *
59   * @see AssetManager#takeSnapshot(String, MediaPackage)
60   */
61  @Component(
62      immediate = true,
63      name = "org.opencastproject.workflow.handler.assetmanager.AssetManagerAddWorkflowOperationHandler",
64      service = WorkflowOperationHandler.class,
65      property = {
66          "service.description=Asset Manager Take Snapshot Workflow Operation Handler",
67          "workflow.operation=snapshot"
68      }
69  )
70  public class AssetManagerSnapshotWorkflowOperationHandler extends AbstractWorkflowOperationHandler {
71    private static final Logger logger = LoggerFactory.getLogger(AssetManagerSnapshotWorkflowOperationHandler.class);
72  
73    /** The asset manager. */
74    private AssetManager assetManager;
75  
76    @Activate
77    @Override
78    public void activate(ComponentContext cc) {
79      super.activate(cc);
80    }
81  
82    /** OSGi DI */
83    @Reference
84    public void setAssetManager(AssetManager assetManager) {
85      this.assetManager = assetManager;
86    }
87  
88    @Override
89    public WorkflowOperationResult start(WorkflowInstance wi, JobContext ctx)
90            throws WorkflowOperationException {
91      final MediaPackage mpWorkflow = wi.getMediaPackage();
92      final WorkflowOperationInstance currentOperation = wi.getCurrentOperation();
93  
94      // Check which tags have been configured
95      ConfiguredTagsAndFlavors tagsAndFlavors = getTagsAndFlavors(wi,
96          Configuration.many, Configuration.many, Configuration.none, Configuration.none);
97      List<String> sourceTagsOption = tagsAndFlavors.getSrcTags();
98      List<MediaPackageElementFlavor> sourceFlavorsOption = tagsAndFlavors.getSrcFlavors();
99  
100 
101     if (sourceTagsOption.isEmpty() && sourceFlavorsOption.isEmpty()) {
102       logger.debug("No source tags have been specified, so everything will be added to the AssetManager");
103     }
104 
105     final List<String> tagSet;
106     // If a set of tags has been specified, use it
107     if (!sourceTagsOption.isEmpty()) {
108       tagSet = sourceTagsOption;
109     } else {
110       tagSet = new ArrayList<>();
111     }
112 
113     try {
114       final MediaPackage mpAssetManager = getMediaPackageForArchival(mpWorkflow, tagSet, sourceFlavorsOption);
115       if (mpAssetManager != null) {
116         logger.info("Take snapshot of media package {}", mpAssetManager);
117         // adding media package to the episode service
118         assetManager.takeSnapshot(DEFAULT_OWNER, mpAssetManager);
119         logger.debug("Snapshot operation complete");
120         return createResult(mpWorkflow, Action.CONTINUE);
121       } else {
122         return createResult(mpWorkflow, Action.CONTINUE);
123       }
124     } catch (Throwable t) {
125       throw new WorkflowOperationException(t);
126     }
127   }
128 
129   protected MediaPackage getMediaPackageForArchival(MediaPackage current, List<String> tags,
130                                                     List<MediaPackageElementFlavor> sourceFlavors)
131           throws MediaPackageException {
132     MediaPackage mp = (MediaPackage) current.clone();
133 
134     Collection<MediaPackageElement> keep;
135 
136     if (tags.isEmpty() && sourceFlavors.isEmpty()) {
137       keep = new ArrayList<>(Arrays.asList(current.getElementsByTags(tags)));
138     } else {
139       SimpleElementSelector simpleElementSelector = new SimpleElementSelector();
140       for (MediaPackageElementFlavor flavor : sourceFlavors) {
141         simpleElementSelector.addFlavor(flavor);
142       }
143       for (String tag : tags) {
144         simpleElementSelector.addTag(tag);
145       }
146       keep = simpleElementSelector.select(current, false);
147     }
148 
149     // Also archive the publication elements
150     for (Publication publication : current.getPublications()) {
151       keep.add(publication);
152     }
153 
154     // Mark everything that is set for removal
155     List<MediaPackageElement> removals = new ArrayList<MediaPackageElement>();
156     for (MediaPackageElement element : mp.getElements()) {
157       if (!keep.contains(element)) {
158         removals.add(element);
159       }
160     }
161 
162     // Fix references and flavors
163     for (MediaPackageElement element : mp.getElements()) {
164 
165       if (removals.contains(element)) {
166         continue;
167       }
168 
169       // Is the element referencing anything?
170       MediaPackageReference reference = element.getReference();
171       if (reference != null) {
172         MediaPackageElement referencedElement = mp.getElementByReference(reference);
173 
174         // if we are distributing the referenced element, everything is fine. Otherwise...
175         if (referencedElement != null && removals.contains(referencedElement)) {
176 
177           // Follow the references until we find a flavor
178           MediaPackageElement parent;
179           while ((parent = current.getElementByReference(reference)) != null) {
180             if (parent.getFlavor() != null && element.getFlavor() == null) {
181               element.setFlavor(parent.getFlavor());
182             }
183             if (parent.getReference() == null) {
184               break;
185             }
186             reference = parent.getReference();
187           }
188 
189           // Done. Let's cut the path but keep references to the mediapackage itself
190           if (reference != null && reference.getType().equals(MediaPackageReference.TYPE_MEDIAPACKAGE)) {
191             element.setReference(reference);
192           } else {
193             element.clearReference();
194           }
195         }
196       }
197     }
198 
199     // Remove everything we don't want to add to publish
200     for (MediaPackageElement element : removals) {
201       mp.remove(element);
202     }
203     return mp;
204   }
205 
206   @Reference
207   @Override
208   public void setServiceRegistry(ServiceRegistry serviceRegistry) {
209     super.setServiceRegistry(serviceRegistry);
210   }
211 
212 }