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.event.handler;
23
24 import static org.opencastproject.job.api.Job.Status.FINISHED;
25 import static org.opencastproject.mediapackage.MediaPackageElementParser.getFromXml;
26 import static org.opencastproject.mediapackage.MediaPackageElements.XACML_POLICY_EPISODE;
27 import static org.opencastproject.workflow.handler.distribution.EngagePublicationChannel.CHANNEL_ID;
28
29 import org.opencastproject.distribution.api.DistributionException;
30 import org.opencastproject.distribution.api.DistributionService;
31 import org.opencastproject.job.api.Job;
32 import org.opencastproject.job.api.JobBarrier;
33 import org.opencastproject.job.api.JobBarrier.Result;
34 import org.opencastproject.mediapackage.Attachment;
35 import org.opencastproject.mediapackage.Catalog;
36 import org.opencastproject.mediapackage.MediaPackage;
37 import org.opencastproject.mediapackage.MediaPackageElement;
38 import org.opencastproject.mediapackage.MediaPackageElements;
39 import org.opencastproject.mediapackage.MediaPackageException;
40 import org.opencastproject.message.broker.api.series.SeriesItem;
41 import org.opencastproject.metadata.dublincore.DublinCore;
42 import org.opencastproject.metadata.dublincore.DublinCoreCatalog;
43 import org.opencastproject.metadata.dublincore.DublinCoreCatalogService;
44 import org.opencastproject.metadata.dublincore.DublinCoreUtil;
45 import org.opencastproject.search.api.SearchException;
46 import org.opencastproject.search.api.SearchService;
47 import org.opencastproject.security.api.AclScope;
48 import org.opencastproject.security.api.AuthorizationService;
49 import org.opencastproject.security.api.Organization;
50 import org.opencastproject.security.api.OrganizationDirectoryService;
51 import org.opencastproject.security.api.SecurityService;
52 import org.opencastproject.security.api.UnauthorizedException;
53 import org.opencastproject.security.api.User;
54 import org.opencastproject.security.util.SecurityUtil;
55 import org.opencastproject.serviceregistry.api.ServiceRegistry;
56 import org.opencastproject.serviceregistry.api.ServiceRegistryException;
57 import org.opencastproject.util.NotFoundException;
58 import org.opencastproject.workspace.api.Workspace;
59
60 import org.apache.commons.io.FilenameUtils;
61 import org.osgi.framework.BundleContext;
62 import org.osgi.service.component.annotations.Activate;
63 import org.osgi.service.component.annotations.Component;
64 import org.osgi.service.component.annotations.Reference;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68 import java.io.IOException;
69 import java.net.URI;
70 import java.util.List;
71
72
73 @Component(
74 immediate = true,
75 service = {
76 SearchUpdatedEventHandler.class
77 },
78 property = {
79 "service.description=Search Updated Event Handler"
80 }
81 )
82 public class SearchUpdatedEventHandler {
83
84
85 protected static final Logger logger = LoggerFactory.getLogger(SearchUpdatedEventHandler.class);
86
87
88 protected ServiceRegistry serviceRegistry = null;
89
90
91 protected DistributionService distributionService = null;
92
93
94 protected SearchService searchService = null;
95
96
97 protected SecurityService securityService = null;
98
99
100 protected AuthorizationService authorizationService = null;
101
102
103 protected OrganizationDirectoryService organizationDirectoryService = null;
104
105
106 protected DublinCoreCatalogService dublinCoreService = null;
107
108
109 protected Workspace workspace = null;
110
111
112 protected String systemAccount = null;
113
114
115
116
117
118
119
120 @Activate
121 protected void activate(BundleContext bundleContext) {
122 this.systemAccount = bundleContext.getProperty("org.opencastproject.security.digest.user");
123 }
124
125
126
127
128
129 @Reference
130 public void setServiceRegistry(ServiceRegistry serviceRegistry) {
131 this.serviceRegistry = serviceRegistry;
132 }
133
134
135
136
137
138 @Reference
139 public void setWorkspace(Workspace workspace) {
140 this.workspace = workspace;
141 }
142
143
144
145
146
147 @Reference
148 public void setDublinCoreCatalogService(DublinCoreCatalogService dublinCoreService) {
149 this.dublinCoreService = dublinCoreService;
150 }
151
152
153
154
155
156 @Reference(target = "(distribution.channel=download)")
157 public void setDistributionService(DistributionService distributionService) {
158 this.distributionService = distributionService;
159 }
160
161
162
163
164
165 @Reference
166 public void setSearchService(SearchService searchService) {
167 this.searchService = searchService;
168 }
169
170
171
172
173
174 @Reference
175 public void setSecurityService(SecurityService securityService) {
176 this.securityService = securityService;
177 }
178
179
180
181
182
183 @Reference
184 public void setAuthorizationService(AuthorizationService authorizationService) {
185 this.authorizationService = authorizationService;
186 }
187
188
189
190
191
192 @Reference
193 public void setOrganizationDirectoryService(OrganizationDirectoryService organizationDirectoryService) {
194 this.organizationDirectoryService = organizationDirectoryService;
195 }
196
197 public void handleEvent(final SeriesItem seriesItem) {
198
199 logger.debug("Handling {}", seriesItem);
200 String seriesId = seriesItem.getSeriesId();
201
202
203 final User prevUser = securityService.getUser();
204 final Organization prevOrg = securityService.getOrganization();
205 try {
206 securityService.setUser(SecurityUtil.createSystemUser(systemAccount, prevOrg));
207
208 for (var seriesData: searchService.getSeries(seriesId)) {
209 var mp = seriesData.getRight();
210 Organization org = seriesData.getLeft();
211 securityService.setOrganization(org);
212
213
214
215 if (SeriesItem.Type.UpdateAcl.equals(seriesItem.getType())) {
216 if (Boolean.TRUE.equals(seriesItem.getOverrideEpisodeAcl())) {
217
218 MediaPackageElement[] distributedEpisodeAcls = mp.getElementsByFlavor(XACML_POLICY_EPISODE);
219 for (MediaPackageElement distributedEpisodeAcl : distributedEpisodeAcls) {
220 List<MediaPackageElement> mpes = distributionService.retractSync(CHANNEL_ID, mp,
221 distributedEpisodeAcl.getIdentifier());
222 if (mpes == null) {
223 logger.error("Unable to retract episode XACML {}", distributedEpisodeAcl.getIdentifier());
224 } else {
225 authorizationService.removeAcl(mp, AclScope.Episode);
226 }
227 }
228 }
229
230 Attachment fileRepoCopy = authorizationService.setAcl(mp, AclScope.Series, seriesItem.getAcl()).getB();
231
232
233 List<MediaPackageElement> mpes = distributionService.distributeSync(CHANNEL_ID, mp,
234 fileRepoCopy.getIdentifier());
235 if (mpes != null && mpes.size() == 1) {
236 mp.remove(fileRepoCopy);
237 mp.add(mpes.get(0));
238 } else {
239 logger.error("Unable to distribute series XACML {}", fileRepoCopy.getIdentifier());
240 continue;
241 }
242 }
243
244
245 if (SeriesItem.Type.UpdateCatalog.equals(seriesItem.getType())) {
246 DublinCoreCatalog seriesDublinCore = seriesItem.getMetadata();
247 mp.setSeriesTitle(seriesDublinCore.getFirst(DublinCore.PROPERTY_TITLE));
248
249
250 Catalog[] seriesCatalogs = mp.getCatalogs(MediaPackageElements.SERIES);
251 if (seriesCatalogs.length == 1) {
252 Catalog c = seriesCatalogs[0];
253 String filename = FilenameUtils.getName(c.getURI().toString());
254 URI uri = workspace.put(mp.getIdentifier().toString(), c.getIdentifier(), filename,
255 dublinCoreService.serialize(seriesDublinCore));
256 c.setURI(uri);
257
258 c.setChecksum(null);
259
260
261 List<MediaPackageElement> mpes = distributionService.distributeSync(CHANNEL_ID, mp, c.getIdentifier());
262 if (mpes != null && mpes.size() == 1) {
263 mp.remove(c);
264 mp.add(mpes.get(0));
265 } else {
266 logger.error("Unable to distribute series catalog {}", c.getIdentifier());
267 continue;
268 }
269 }
270 }
271
272
273 if (SeriesItem.Type.Delete.equals(seriesItem.getType())) {
274 mp.setSeries(null);
275 mp.setSeriesTitle(null);
276
277 boolean retractSeriesCatalog = retractSeriesCatalog(mp);
278 boolean updateEpisodeCatalog = updateEpisodeCatalog(mp);
279
280 if (!retractSeriesCatalog || !updateEpisodeCatalog) {
281 continue;
282 }
283 }
284
285
286 searchService.addSynchronously(mp);
287 }
288
289 if (SeriesItem.Type.Delete.equals(seriesItem.getType())) {
290 searchService.deleteSeries(seriesId);
291 }
292 } catch (SearchException e) {
293 logger.warn("Unable to find mediapackages for series {} in search: {}", seriesItem, e.getMessage());
294 } catch (UnauthorizedException | MediaPackageException | ServiceRegistryException
295 | NotFoundException | IOException | DistributionException e) {
296 logger.warn("Unable to update mediapackages for series {} for user {}: {} {}",
297 seriesId, prevUser.getUsername(), e.getClass().getSimpleName(), e.getMessage());
298 } finally {
299 securityService.setOrganization(prevOrg);
300 securityService.setUser(prevUser);
301 }
302 }
303
304 private boolean retractSeriesCatalog(MediaPackage mp) throws DistributionException {
305
306 for (Catalog c : mp.getCatalogs(MediaPackageElements.SERIES)) {
307 Job retractJob = distributionService.retract(CHANNEL_ID, mp, c.getIdentifier());
308 JobBarrier barrier = new JobBarrier(null, serviceRegistry, retractJob);
309 Result jobResult = barrier.waitForJobs();
310 if (jobResult.getStatus().get(retractJob).equals(FINISHED)) {
311 mp.remove(c);
312 } else {
313 logger.error("Unable to retract series catalog {}", c.getIdentifier());
314 return false;
315 }
316 }
317 return true;
318 }
319
320 private boolean updateEpisodeCatalog(MediaPackage mp) throws DistributionException, MediaPackageException,
321 NotFoundException, ServiceRegistryException, IllegalArgumentException, IOException {
322
323 for (Catalog episodeCatalog : mp.getCatalogs(MediaPackageElements.EPISODE)) {
324 DublinCoreCatalog episodeDublinCore = DublinCoreUtil.loadDublinCore(workspace, episodeCatalog);
325 episodeDublinCore.remove(DublinCore.PROPERTY_IS_PART_OF);
326 String filename = FilenameUtils.getName(episodeCatalog.getURI().toString());
327 URI uri = workspace.put(mp.getIdentifier().toString(), episodeCatalog.getIdentifier(), filename,
328 dublinCoreService.serialize(episodeDublinCore));
329 episodeCatalog.setURI(uri);
330
331 episodeCatalog.setChecksum(null);
332
333
334 Job distributionJob = distributionService.distribute(CHANNEL_ID, mp, episodeCatalog.getIdentifier());
335 JobBarrier barrier = new JobBarrier(null, serviceRegistry, distributionJob);
336 Result jobResult = barrier.waitForJobs();
337 if (jobResult.getStatus().get(distributionJob).equals(FINISHED)) {
338 mp.remove(episodeCatalog);
339 mp.add(getFromXml(serviceRegistry.getJob(distributionJob.getId()).getPayload()));
340 } else {
341 logger.error("Unable to distribute episode catalog {}", episodeCatalog.getIdentifier());
342 return false;
343 }
344 }
345 return true;
346 }
347 }