1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.ingestdownloadservice.impl;
23
24 import org.opencastproject.ingestdownloadservice.api.IngestDownloadService;
25 import org.opencastproject.job.api.AbstractJobProducer;
26 import org.opencastproject.job.api.Job;
27 import org.opencastproject.mediapackage.MediaPackage;
28 import org.opencastproject.mediapackage.MediaPackageElement;
29 import org.opencastproject.mediapackage.MediaPackageException;
30 import org.opencastproject.mediapackage.MediaPackageParser;
31 import org.opencastproject.mediapackage.selector.AbstractMediaPackageElementSelector;
32 import org.opencastproject.mediapackage.selector.SimpleElementSelector;
33 import org.opencastproject.security.api.OrganizationDirectoryService;
34 import org.opencastproject.security.api.SecurityService;
35 import org.opencastproject.security.api.TrustedHttpClient;
36 import org.opencastproject.security.api.TrustedHttpClientException;
37 import org.opencastproject.security.api.UserDirectoryService;
38 import org.opencastproject.serviceregistry.api.ServiceRegistration;
39 import org.opencastproject.serviceregistry.api.ServiceRegistry;
40 import org.opencastproject.serviceregistry.api.ServiceRegistryException;
41 import org.opencastproject.util.NotFoundException;
42 import org.opencastproject.util.UrlSupport;
43 import org.opencastproject.workingfilerepository.api.WorkingFileRepository;
44 import org.opencastproject.workspace.api.Workspace;
45
46 import org.apache.commons.io.FilenameUtils;
47 import org.apache.commons.lang3.StringUtils;
48 import org.apache.http.HttpResponse;
49 import org.apache.http.HttpStatus;
50 import org.apache.http.client.methods.HttpDelete;
51 import org.osgi.service.component.annotations.Component;
52 import org.osgi.service.component.annotations.Reference;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 import java.io.File;
57 import java.io.FileInputStream;
58 import java.io.IOException;
59 import java.io.InputStream;
60 import java.net.URI;
61 import java.util.ArrayList;
62 import java.util.List;
63 import java.util.Optional;
64
65
66
67
68 @Component(
69 immediate = true,
70 service = IngestDownloadService.class,
71 property = {
72 "service.description=Ingest download service",
73 "service.pid=org.opencastproject.ingestdownloadservice.impl.IngestDownloadServiceImpl"
74 }
75 )
76 public class IngestDownloadServiceImpl extends AbstractJobProducer implements IngestDownloadService {
77
78 public enum Operation {
79 Download
80 }
81
82
83
84
85 private static final Logger logger = LoggerFactory.getLogger(IngestDownloadServiceImpl.class);
86
87
88
89
90 private ServiceRegistry serviceRegistry = null;
91
92
93
94
95 private SecurityService securityService = null;
96
97
98
99
100 private UserDirectoryService userDirectoryService = null;
101
102
103
104
105 private OrganizationDirectoryService organizationDirectoryService = null;
106
107
108
109
110 private Workspace workspace;
111
112
113
114
115 private TrustedHttpClient client = null;
116
117
118
119
120
121 public IngestDownloadServiceImpl() {
122 super(JOB_TYPE);
123 }
124
125
126
127
128
129
130 @Reference
131 public void setWorkspace(Workspace workspace) {
132 this.workspace = workspace;
133 }
134
135
136
137
138
139
140 @Reference
141 public void setServiceRegistry(ServiceRegistry serviceRegistry) {
142 this.serviceRegistry = serviceRegistry;
143 }
144
145
146
147
148
149
150 @Override
151 protected ServiceRegistry getServiceRegistry() {
152 return serviceRegistry;
153 }
154
155
156
157
158
159
160 @Override
161 protected SecurityService getSecurityService() {
162 return securityService;
163 }
164
165
166
167
168
169
170 @Reference
171 public void setSecurityService(SecurityService securityService) {
172 this.securityService = securityService;
173 }
174
175
176
177
178
179
180 @Reference
181 public void setUserDirectoryService(UserDirectoryService userDirectoryService) {
182 this.userDirectoryService = userDirectoryService;
183 }
184
185
186
187
188
189
190 @Override
191 protected UserDirectoryService getUserDirectoryService() {
192 return userDirectoryService;
193 }
194
195
196
197
198
199
200 @Override
201 protected OrganizationDirectoryService getOrganizationDirectoryService() {
202 return organizationDirectoryService;
203 }
204
205
206
207
208
209
210 @Reference
211 public void setOrganizationDirectoryService(OrganizationDirectoryService organizationDirectory) {
212 this.organizationDirectoryService = organizationDirectory;
213 }
214
215 @Override
216 public Job ingestDownload(MediaPackage mediaPackage, String sourceFlavors, String sourceTags, boolean deleteExternal,
217 boolean tagsAndFlavor) throws ServiceRegistryException {
218
219 final List<String> paramList = new ArrayList<>(5);
220 paramList.add(MediaPackageParser.getAsXml(mediaPackage));
221 paramList.add(sourceFlavors);
222 paramList.add(sourceTags);
223 paramList.add(Boolean.toString(deleteExternal));
224 paramList.add(Boolean.toString(tagsAndFlavor));
225
226 return serviceRegistry.createJob(JOB_TYPE, Operation.Download.toString(), paramList);
227
228 }
229
230 @Override
231 protected String process(Job job) throws MediaPackageException, IOException {
232 final List<String> arguments = new ArrayList<>(job.getArguments());
233
234 final MediaPackage mediaPackage = MediaPackageParser.getFromXml(arguments.get(0));
235 final String sourceFlavors = arguments.get(1);
236 final String sourceTags = arguments.get(2);
237 final boolean deleteExternal = Boolean.parseBoolean(arguments.get(3));
238 final boolean tagsAndFlavor = Boolean.parseBoolean(arguments.get(4));
239
240
241 AbstractMediaPackageElementSelector<MediaPackageElement> elementSelector = new SimpleElementSelector();
242 for (String tag : StringUtils.split(sourceTags, ", ")) {
243 elementSelector.addTag(tag);
244 }
245 for (String flavor : StringUtils.split(sourceFlavors, ", ")) {
246 elementSelector.addFlavor(flavor);
247 }
248
249 final String baseUrl = workspace.getBaseUri().toString();
250
251 List<URI> externalUris = new ArrayList<>();
252 for (MediaPackageElement element : elementSelector.select(mediaPackage, tagsAndFlavor)) {
253 if (element.getURI() == null) {
254 continue;
255 }
256
257 if (element.getElementType() == MediaPackageElement.Type.Publication) {
258 logger.debug("Skipping publication {} from media package {}", element.getIdentifier(),
259 mediaPackage.getIdentifier());
260 continue;
261 }
262
263 if (element.getURI().toString().startsWith(baseUrl)) {
264 logger.info("Skipping already existing element {}", element.getURI());
265 continue;
266 }
267
268
269 File file;
270 try {
271 file = workspace.get(element.getURI());
272 } catch (NotFoundException e) {
273 logger.warn("Unable to download the external element {}", element.getURI());
274 continue;
275 }
276
277
278 final URI originalUri = element.getURI();
279 try (InputStream in = new FileInputStream(file)) {
280 final String filename = FilenameUtils.getName(element.getURI().getPath());
281 final URI uri = workspace.put(mediaPackage.getIdentifier().toString(), element.getIdentifier(), filename, in);
282 element.setURI(uri);
283 } finally {
284 try {
285 workspace.delete(originalUri);
286 } catch (Exception e) {
287 logger.warn("Unable to delete ingest-downloaded element {}", element.getURI(), e);
288 }
289 }
290
291 logger.info("Downloaded the external element {}", originalUri);
292
293
294 externalUris.add(originalUri);
295 }
296
297 if (!deleteExternal || externalUris.size() == 0)
298 return MediaPackageParser.getAsXml(mediaPackage);
299
300
301 logger.debug("Assembling list of external working file repositories");
302 List<String> externalWfrBaseUrls = new ArrayList<>();
303 try {
304 final String wfrServiceType = WorkingFileRepository.SERVICE_TYPE;
305 for (ServiceRegistration reg : serviceRegistry.getServiceRegistrationsByType(wfrServiceType)) {
306 if (baseUrl.startsWith(reg.getHost())) {
307 logger.trace("Skipping local working file repository");
308 continue;
309 }
310 externalWfrBaseUrls.add(UrlSupport.concat(reg.getHost(), reg.getPath()));
311 }
312 logger.debug("{} external working file repositories found", externalWfrBaseUrls.size());
313 } catch (ServiceRegistryException e) {
314 logger.error("Unable to load WFR services from service registry", e);
315 }
316
317
318 for (URI uri : externalUris) {
319
320 String elementUri = uri.toString();
321
322
323 Optional<String> wfrBaseUrl = externalWfrBaseUrls.parallelStream().filter(elementUri::startsWith).findAny();
324
325 if (!wfrBaseUrl.isPresent()) {
326 logger.debug("Unable to delete {}, no working file repository found for this URI", elementUri);
327 continue;
328 }
329
330 final String deleteUrl;
331 if (uri.getPath().startsWith(WorkingFileRepository.MEDIAPACKAGE_PATH_PREFIX)) {
332 deleteUrl = elementUri.substring(0, elementUri.lastIndexOf("/"));
333 } else if (uri.getPath().startsWith(WorkingFileRepository.COLLECTION_PATH_PREFIX)) {
334 deleteUrl = elementUri;
335 } else {
336 logger.info("Unable to handle working file repository URI {}", elementUri);
337 continue;
338 }
339 HttpDelete delete = new HttpDelete(deleteUrl);
340
341 HttpResponse response = null;
342 try {
343 response = client.execute(delete);
344 final int statusCode = response.getStatusLine().getStatusCode();
345 if (statusCode == HttpStatus.SC_NO_CONTENT || statusCode == HttpStatus.SC_OK) {
346 logger.info("Successfully deleted external URI {}", delete.getURI());
347 } else if (statusCode == HttpStatus.SC_NOT_FOUND) {
348 logger.debug("External URI {} has already been deleted", delete.getURI());
349 } else {
350 logger.warn("Unable to delete external URI {}, status code '{}' returned", delete.getURI(), statusCode);
351 }
352 } catch (TrustedHttpClientException e) {
353 logger.warn("Unable to execute DELETE request on external URI {}", delete.getURI());
354 } finally {
355 client.close(response);
356 }
357 }
358
359 return MediaPackageParser.getAsXml(mediaPackage);
360
361 }
362 }
363