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.themes.persistence;
23
24 import static org.opencastproject.db.Queries.namedQuery;
25
26 import org.opencastproject.db.DBSession;
27 import org.opencastproject.db.DBSessionFactory;
28 import org.opencastproject.elasticsearch.api.SearchIndexException;
29 import org.opencastproject.elasticsearch.index.ElasticsearchIndex;
30 import org.opencastproject.elasticsearch.index.objects.theme.IndexTheme;
31 import org.opencastproject.elasticsearch.index.rebuild.AbstractIndexProducer;
32 import org.opencastproject.elasticsearch.index.rebuild.IndexProducer;
33 import org.opencastproject.elasticsearch.index.rebuild.IndexRebuildException;
34 import org.opencastproject.elasticsearch.index.rebuild.IndexRebuildService;
35 import org.opencastproject.security.api.Organization;
36 import org.opencastproject.security.api.OrganizationDirectoryService;
37 import org.opencastproject.security.api.SecurityService;
38 import org.opencastproject.security.api.User;
39 import org.opencastproject.security.api.UserDirectoryService;
40 import org.opencastproject.themes.Theme;
41 import org.opencastproject.themes.ThemesServiceDatabase;
42 import org.opencastproject.util.NotFoundException;
43
44 import org.apache.commons.lang3.StringUtils;
45 import org.apache.commons.lang3.tuple.Pair;
46 import org.osgi.service.component.ComponentContext;
47 import org.osgi.service.component.annotations.Activate;
48 import org.osgi.service.component.annotations.Component;
49 import org.osgi.service.component.annotations.Reference;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 import java.util.ArrayList;
54 import java.util.List;
55 import java.util.Optional;
56 import java.util.function.Function;
57 import java.util.stream.Collectors;
58
59 import javax.persistence.EntityManager;
60 import javax.persistence.EntityManagerFactory;
61
62
63
64
65 @Component(
66 immediate = true,
67 service = { ThemesServiceDatabase.class, IndexProducer.class },
68 property = {
69 "service.description=Themes Database Service"
70 }
71 )
72 public class ThemesServiceDatabaseImpl extends AbstractIndexProducer implements ThemesServiceDatabase {
73
74 public static final String PERSISTENCE_UNIT = "org.opencastproject.themes";
75
76
77 private static final Logger logger = LoggerFactory.getLogger(ThemesServiceDatabaseImpl.class);
78
79
80 protected EntityManagerFactory emf;
81
82 protected DBSessionFactory dbSessionFactory;
83
84 protected DBSession db;
85
86
87 protected SecurityService securityService;
88
89
90 protected UserDirectoryService userDirectoryService;
91
92
93 protected OrganizationDirectoryService organizationDirectoryService;
94
95
96 protected ElasticsearchIndex index;
97
98
99 private ComponentContext cc;
100
101
102
103
104
105
106 @Activate
107 public void activate(ComponentContext cc) {
108 logger.info("Activating persistence manager for themes");
109 this.cc = cc;
110 db = dbSessionFactory.createSession(emf);
111 }
112
113
114 @Reference(target = "(osgi.unit.name=org.opencastproject.themes)")
115 public void setEntityManagerFactory(EntityManagerFactory emf) {
116 this.emf = emf;
117 }
118
119 @Reference
120 public void setDBSessionFactory(DBSessionFactory dbSessionFactory) {
121 this.dbSessionFactory = dbSessionFactory;
122 }
123
124
125
126
127
128
129
130 @Reference
131 public void setSecurityService(SecurityService securityService) {
132 this.securityService = securityService;
133 }
134
135
136
137
138
139
140
141 @Reference
142 public void setUserDirectoryService(UserDirectoryService userDirectoryService) {
143 this.userDirectoryService = userDirectoryService;
144 }
145
146
147 @Reference
148 public void setOrganizationDirectoryService(OrganizationDirectoryService organizationDirectoryService) {
149 this.organizationDirectoryService = organizationDirectoryService;
150 }
151
152
153 @Reference
154 public void setIndex(ElasticsearchIndex index) {
155 this.index = index;
156 }
157
158 @Override
159 public Theme getTheme(long id) throws ThemesServiceDatabaseException, NotFoundException {
160 try {
161 return db.exec(getThemeDtoQuery(id))
162 .map(t -> t.toTheme(userDirectoryService))
163 .orElseThrow(() -> new NotFoundException("No theme with id=" + id + " exists"));
164 } catch (NotFoundException e) {
165 throw e;
166 } catch (Exception e) {
167 logger.error("Could not get theme", e);
168 throw new ThemesServiceDatabaseException(e);
169 }
170 }
171
172 private List<Theme> getThemes() throws ThemesServiceDatabaseException {
173 try {
174 String orgId = securityService.getOrganization().getId();
175 return db.exec(namedQuery.findAll(
176 "Themes.findByOrg",
177 ThemeDto.class,
178 Pair.of("org", orgId)
179 )).stream()
180 .map(t -> t.toTheme(userDirectoryService))
181 .collect(Collectors.toList());
182 } catch (Exception e) {
183 logger.error("Could not get themes", e);
184 throw new ThemesServiceDatabaseException(e);
185 }
186 }
187
188 @Override
189 public Theme updateTheme(final Theme theme) throws ThemesServiceDatabaseException {
190 try {
191 Theme newTheme = db.execTxChecked(em -> {
192 ThemeDto themeDto = null;
193 if (theme.getId().isSome()) {
194 themeDto = getThemeDtoQuery(theme.getId().get()).apply(em).orElse(null);
195 }
196
197 if (themeDto == null) {
198
199 themeDto = new ThemeDto();
200 themeDto.setOrganization(securityService.getOrganization().getId());
201 updateTheme(theme, themeDto);
202 em.persist(themeDto);
203 } else {
204 updateTheme(theme, themeDto);
205 em.merge(themeDto);
206 }
207
208 return themeDto.toTheme(userDirectoryService);
209 });
210
211
212 String orgId = securityService.getOrganization().getId();
213 User user = securityService.getUser();
214 updateThemeInIndex(newTheme, orgId, user);
215
216 return newTheme;
217 } catch (Exception e) {
218 logger.error("Could not update theme {}", theme, e);
219 throw new ThemesServiceDatabaseException(e);
220 }
221 }
222
223 private void updateTheme(Theme theme, ThemeDto themeDto) {
224 if (theme.getId().isSome()) {
225 themeDto.setId(theme.getId().get());
226 }
227 themeDto.setUsername(theme.getCreator().getUsername());
228 themeDto.setCreationDate(theme.getCreationDate());
229 themeDto.setDefault(theme.isDefault());
230 themeDto.setName(theme.getName());
231 themeDto.setDescription(theme.getDescription());
232 themeDto.setBumperActive(theme.isBumperActive());
233 themeDto.setBumperFile(theme.getBumperFile());
234 themeDto.setTrailerActive(theme.isTrailerActive());
235 themeDto.setTrailerFile(theme.getTrailerFile());
236 themeDto.setTitleSlideActive(theme.isTitleSlideActive());
237 themeDto.setTitleSlideBackground(theme.getTitleSlideBackground());
238 themeDto.setTitleSlideMetadata(theme.getTitleSlideMetadata());
239 themeDto.setLicenseSlideActive(theme.isLicenseSlideActive());
240 themeDto.setLicenseSlideBackground(theme.getLicenseSlideBackground());
241 themeDto.setLicenseSlideDescription(theme.getLicenseSlideDescription());
242 themeDto.setWatermarkActive(theme.isWatermarkActive());
243 themeDto.setWatermarkFile(theme.getWatermarkFile());
244 themeDto.setWatermarkPosition(theme.getWatermarkPosition());
245 }
246
247 @Override
248 public void deleteTheme(long id) throws ThemesServiceDatabaseException, NotFoundException {
249 try {
250 db.execTxChecked(em -> {
251 ThemeDto themeDto = getThemeDtoQuery(id).apply(em)
252 .orElseThrow(() -> new NotFoundException("No theme with id=" + id + " exists"));
253 namedQuery.remove(themeDto).accept(em);
254 });
255
256
257 String organization = securityService.getOrganization().getId();
258 removeThemeFromIndex(id, organization);
259 } catch (NotFoundException e) {
260 throw e;
261 } catch (Exception e) {
262 logger.error("Could not delete theme '{}'", id, e);
263 throw new ThemesServiceDatabaseException(e);
264 }
265 }
266
267 @Override
268 public int countThemes() throws ThemesServiceDatabaseException {
269 try {
270 String orgId = securityService.getOrganization().getId();
271 return db.exec(namedQuery.find(
272 "Themes.count",
273 Number.class,
274 Pair.of("org", orgId)
275 )).intValue();
276 } catch (Exception e) {
277 logger.error("Could not count themes", e);
278 throw new ThemesServiceDatabaseException(e);
279 }
280 }
281
282
283
284
285
286
287
288
289 private Function<EntityManager, Optional<ThemeDto>> getThemeDtoQuery(long id) {
290 String orgId = securityService.getOrganization().getId();
291 return namedQuery.findOpt(
292 "Themes.findById",
293 ThemeDto.class,
294 Pair.of("id", id),
295 Pair.of("org", orgId)
296 );
297 }
298
299 @Override
300 public void repopulate(IndexRebuildService.DataType type) throws IndexRebuildException {
301 try {
302 for (final Organization organization : organizationDirectoryService.getOrganizations()) {
303 try {
304 final List<Theme> themes = getThemes();
305 int total = themes.size();
306 logIndexRebuildBegin(logger, total, "themes", organization);
307 int current = 0;
308 int n = 20;
309 List<IndexTheme> updatedThemeRange = new ArrayList<>();
310
311 for (Theme theme : themes) {
312 current++;
313
314 var updatedThemeData = index.getTheme(theme.getId().get(), organization.toString(),
315 securityService.getUser());
316 updatedThemeData = getThemeUpdateFunction(theme, organization.toString()).apply(updatedThemeData);
317 updatedThemeRange.add(updatedThemeData.get());
318
319 if (updatedThemeRange.size() >= n || current >= themes.size()) {
320 index.bulkThemeUpdate(updatedThemeRange);
321 logIndexRebuildProgress(logger, total, current, n);
322 updatedThemeRange.clear();
323 }
324 }
325 } catch (ThemesServiceDatabaseException e) {
326 logger.error("Unable to get themes from the database", e);
327 throw new IllegalStateException(e);
328 }
329 }
330 } catch (Exception e) {
331 logIndexRebuildError(logger, e);
332 throw new IndexRebuildException(getService(), e);
333 }
334 }
335
336 @Override
337 public IndexRebuildService.Service getService() {
338 return IndexRebuildService.Service.Themes;
339 }
340
341
342
343
344
345
346
347
348
349 private void removeThemeFromIndex(long themeId, String orgId) {
350 logger.debug("Removing theme {} from the {} index.", themeId, index.getIndexName());
351
352 try {
353 index.deleteTheme(Long.toString(themeId), orgId);
354 logger.debug("Theme {} removed from the {} index", themeId, index.getIndexName());
355 } catch (SearchIndexException e) {
356 logger.error("Error deleting the theme {} from the {} index", themeId, index.getIndexName(), e);
357 }
358 }
359
360
361
362
363
364
365
366
367
368 private void updateThemeInIndex(Theme theme, String orgId,
369 User user) {
370 logger.debug("Updating the theme with id '{}', name '{}', description '{}', organization '{}' in the {} index.",
371 theme.getId(), theme.getName(), theme.getDescription(),
372 orgId, index.getIndexName());
373 try {
374 if (theme.getId().isNone()) {
375 throw new IllegalArgumentException("Can't put theme in index without valid id!");
376 }
377 Long id = theme.getId().get();
378
379
380 Function<Optional<IndexTheme>, Optional<IndexTheme>> updateFunction = (Optional<IndexTheme> indexThemeOpt) -> {
381 IndexTheme indexTheme;
382 indexTheme = indexThemeOpt.orElseGet(() -> new IndexTheme(id, orgId));
383 String creator = StringUtils.isNotBlank(theme.getCreator().getName())
384 ? theme.getCreator().getName() : theme.getCreator().getUsername();
385
386 indexTheme.setCreationDate(theme.getCreationDate());
387 indexTheme.setDefault(theme.isDefault());
388 indexTheme.setName(theme.getName());
389 indexTheme.setDescription(theme.getDescription());
390 indexTheme.setCreator(creator);
391 indexTheme.setBumperActive(theme.isBumperActive());
392 indexTheme.setBumperFile(theme.getBumperFile());
393 indexTheme.setTrailerActive(theme.isTrailerActive());
394 indexTheme.setTrailerFile(theme.getTrailerFile());
395 indexTheme.setTitleSlideActive(theme.isTitleSlideActive());
396 indexTheme.setTitleSlideBackground(theme.getTitleSlideBackground());
397 indexTheme.setTitleSlideMetadata(theme.getTitleSlideMetadata());
398 indexTheme.setLicenseSlideActive(theme.isLicenseSlideActive());
399 indexTheme.setLicenseSlideBackground(theme.getLicenseSlideBackground());
400 indexTheme.setLicenseSlideDescription(theme.getLicenseSlideDescription());
401 indexTheme.setWatermarkActive(theme.isWatermarkActive());
402 indexTheme.setWatermarkFile(theme.getWatermarkFile());
403 indexTheme.setWatermarkPosition(theme.getWatermarkPosition());
404 return Optional.of(indexTheme);
405 };
406
407 index.addOrUpdateTheme(id, updateFunction, orgId, user);
408 logger.debug("Updated the theme {} in the {} index", theme.getId(), index.getIndexName());
409 } catch (SearchIndexException e) {
410 logger.error("Error updating the theme {} in the {} index", theme.getId(), index.getIndexName(), e);
411 }
412 }
413
414
415
416
417
418
419
420
421
422 private Function<Optional<IndexTheme>, Optional<IndexTheme>> getThemeUpdateFunction(Theme theme, String orgId) {
423 return (Optional<IndexTheme> indexThemeOpt) -> {
424 IndexTheme indexTheme;
425 indexTheme = indexThemeOpt.orElseGet(() -> new IndexTheme(theme.getId().get(), orgId));
426 String creator = theme.getCreator() == null ? "?" : (
427 StringUtils.isNotBlank(theme.getCreator().getName())
428 ? theme.getCreator().getName()
429 : theme.getCreator().getUsername());
430
431 indexTheme.setCreationDate(theme.getCreationDate());
432 indexTheme.setDefault(theme.isDefault());
433 indexTheme.setName(theme.getName());
434 indexTheme.setDescription(theme.getDescription());
435 indexTheme.setCreator(creator);
436 indexTheme.setBumperActive(theme.isBumperActive());
437 indexTheme.setBumperFile(theme.getBumperFile());
438 indexTheme.setTrailerActive(theme.isTrailerActive());
439 indexTheme.setTrailerFile(theme.getTrailerFile());
440 indexTheme.setTitleSlideActive(theme.isTitleSlideActive());
441 indexTheme.setTitleSlideBackground(theme.getTitleSlideBackground());
442 indexTheme.setTitleSlideMetadata(theme.getTitleSlideMetadata());
443 indexTheme.setLicenseSlideActive(theme.isLicenseSlideActive());
444 indexTheme.setLicenseSlideBackground(theme.getLicenseSlideBackground());
445 indexTheme.setLicenseSlideDescription(theme.getLicenseSlideDescription());
446 indexTheme.setWatermarkActive(theme.isWatermarkActive());
447 indexTheme.setWatermarkFile(theme.getWatermarkFile());
448 indexTheme.setWatermarkPosition(theme.getWatermarkPosition());
449 return Optional.of(indexTheme);
450 };
451 }
452 }