/**
* Licensed to The Apereo Foundation under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
*
* The Apereo Foundation licenses this file to you under the Educational
* Community License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License
* at:
*
* http://opensource.org/licenses/ecl2.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
*/
package org.opencastproject.themes.persistence;
import org.opencastproject.index.IndexProducer;
import org.opencastproject.message.broker.api.MessageReceiver;
import org.opencastproject.message.broker.api.MessageSender;
import org.opencastproject.message.broker.api.index.AbstractIndexProducer;
import org.opencastproject.message.broker.api.index.IndexRecreateObject;
import org.opencastproject.message.broker.api.index.IndexRecreateObject.Service;
import org.opencastproject.message.broker.api.theme.SerializableTheme;
import org.opencastproject.message.broker.api.theme.ThemeItem;
import org.opencastproject.security.api.DefaultOrganization;
import org.opencastproject.security.api.Organization;
import org.opencastproject.security.api.OrganizationDirectoryService;
import org.opencastproject.security.api.SecurityService;
import org.opencastproject.security.api.UserDirectoryService;
import org.opencastproject.security.util.SecurityUtil;
import org.opencastproject.themes.Theme;
import org.opencastproject.themes.ThemesServiceDatabase;
import org.opencastproject.util.NotFoundException;
import org.opencastproject.util.data.Effect0;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
/**
* Implements {@link ThemesServiceDatabase}. Defines permanent storage for themes.
*/
public class ThemesServiceDatabaseImpl extends AbstractIndexProducer implements ThemesServiceDatabase {
public static final String PERSISTENCE_UNIT = "org.opencastproject.themes";
/** Logging utilities */
private static final Logger logger = LoggerFactory.getLogger(ThemesServiceDatabaseImpl.class);
/** Factory used to create {@link EntityManager}s for transactions */
protected EntityManagerFactory emf;
/** The security service */
protected SecurityService securityService;
/** The user directory service */
protected UserDirectoryService userDirectoryService;
/** The message broker sender service */
protected MessageSender messageSender;
/** The message broker receiver service */
protected MessageReceiver messageReceiver;
/** The organization directory service to get organizations */
protected OrganizationDirectoryService organizationDirectoryService;
/** The component context for this themes service database */
private ComponentContext cc;
/**
* Creates {@link EntityManagerFactory} using persistence provider and properties passed via OSGi.
*
* @param cc
*/
public void activate(ComponentContext cc) {
logger.info("Activating persistence manager for themes");
this.cc = cc;
super.activate();
}
/**
* Closes entity manager factory.
*
* @param cc
*/
public void deactivate(ComponentContext cc) {
super.deactivate();
}
/** OSGi DI */
public void setEntityManagerFactory(EntityManagerFactory emf) {
this.emf = emf;
}
/**
* OSGi callback to set the security service.
*
* @param securityService
* the security service
*/
public void setSecurityService(SecurityService securityService) {
this.securityService = securityService;
}
/**
* OSGi callback to set the user directory service
*
* @param userDirectoryService
* the user directory service
*/
public void setUserDirectoryService(UserDirectoryService userDirectoryService) {
this.userDirectoryService = userDirectoryService;
}
/** OSGi DI */
public void setMessageSender(MessageSender messageSender) {
this.messageSender = messageSender;
}
/** OSGi DI */
public void setMessageReceiver(MessageReceiver messageReceiver) {
this.messageReceiver = messageReceiver;
}
/** OSGi DI */
public void setOrganizationDirectoryService(OrganizationDirectoryService organizationDirectoryService) {
this.organizationDirectoryService = organizationDirectoryService;
}
@Override
public Theme getTheme(long id) throws ThemesServiceDatabaseException, NotFoundException {
EntityManager em = null;
try {
em = emf.createEntityManager();
ThemeDto themeDto = getThemeDto(id, em);
if (themeDto == null)
throw new NotFoundException("No theme with id=" + id + " exists");
return themeDto.toTheme(userDirectoryService);
} catch (NotFoundException e) {
throw e;
} catch (Exception e) {
logger.error("Could not get theme: {}", ExceptionUtils.getStackTrace(e));
throw new ThemesServiceDatabaseException(e);
} finally {
if (em != null)
em.close();
}
}
private List<Theme> getThemes() throws ThemesServiceDatabaseException {
EntityManager em = null;
try {
em = emf.createEntityManager();
String orgId = securityService.getOrganization().getId();
TypedQuery<ThemeDto> q = em.createNamedQuery("Themes.findByOrg", ThemeDto.class).setParameter("org", orgId);
List<ThemeDto> themeDtos = q.getResultList();
List<Theme> themes = new ArrayList<>();
for (ThemeDto themeDto : themeDtos) {
themes.add(themeDto.toTheme(userDirectoryService));
}
return themes;
} catch (Exception e) {
logger.error("Could not get themes: {}", ExceptionUtils.getStackTrace(e));
throw new ThemesServiceDatabaseException(e);
} finally {
if (em != null)
em.close();
}
}
@Override
public Theme updateTheme(Theme theme) throws ThemesServiceDatabaseException {
EntityManager em = null;
EntityTransaction tx = null;
try {
em = emf.createEntityManager();
tx = em.getTransaction();
tx.begin();
ThemeDto themeDto = null;
if (theme.getId().isSome())
themeDto = getThemeDto(theme.getId().get(), em);
if (themeDto == null) {
// no theme stored, create new entity
themeDto = new ThemeDto();
themeDto.setOrganization(securityService.getOrganization().getId());
updateTheme(theme, themeDto);
em.persist(themeDto);
} else {
updateTheme(theme, themeDto);
em.merge(themeDto);
}
tx.commit();
theme = themeDto.toTheme(userDirectoryService);
messageSender.sendObjectMessage(ThemeItem.THEME_QUEUE, MessageSender.DestinationType.Queue,
ThemeItem.update(toSerializableTheme(theme)));
return theme;
} catch (Exception e) {
logger.error("Could not update theme {}: {}", theme, ExceptionUtils.getStackTrace(e));
if (tx.isActive()) {
tx.rollback();
}
throw new ThemesServiceDatabaseException(e);
} finally {
if (em != null) {
em.close();
}
}
}
private void updateTheme(Theme theme, ThemeDto themeDto) {
themeDto.setUsername(theme.getCreator().getUsername());
themeDto.setCreationDate(theme.getCreationDate());
themeDto.setDefault(theme.isDefault());
themeDto.setName(theme.getName());
themeDto.setDescription(theme.getDescription());
themeDto.setBumperActive(theme.isBumperActive());
themeDto.setBumperFile(theme.getBumperFile());
themeDto.setTrailerActive(theme.isTrailerActive());
themeDto.setTrailerFile(theme.getTrailerFile());
themeDto.setTitleSlideActive(theme.isTitleSlideActive());
themeDto.setTitleSlideBackground(theme.getTitleSlideBackground());
themeDto.setTitleSlideMetadata(theme.getTitleSlideMetadata());
themeDto.setLicenseSlideActive(theme.isLicenseSlideActive());
themeDto.setLicenseSlideBackground(theme.getLicenseSlideBackground());
themeDto.setLicenseSlideDescription(theme.getLicenseSlideDescription());
themeDto.setWatermarkActive(theme.isWatermarkActive());
themeDto.setWatermarkFile(theme.getWatermarkFile());
themeDto.setWatermarkPosition(theme.getWatermarkPosition());
}
@Override
public void deleteTheme(long id) throws ThemesServiceDatabaseException, NotFoundException {
EntityManager em = null;
EntityTransaction tx = null;
try {
em = emf.createEntityManager();
ThemeDto themeDto = getThemeDto(id, em);
if (themeDto == null)
throw new NotFoundException("No theme with id=" + id + " exists");
tx = em.getTransaction();
tx.begin();
em.remove(themeDto);
tx.commit();
messageSender.sendObjectMessage(ThemeItem.THEME_QUEUE, MessageSender.DestinationType.Queue, ThemeItem.delete(id));
} catch (NotFoundException e) {
throw e;
} catch (Exception e) {
logger.error("Could not delete theme '{}': {}", id, ExceptionUtils.getStackTrace(e));
if (tx.isActive())
tx.rollback();
throw new ThemesServiceDatabaseException(e);
} finally {
if (em != null)
em.close();
}
}
@Override
public int countThemes() throws ThemesServiceDatabaseException {
EntityManager em = null;
try {
em = emf.createEntityManager();
String orgId = securityService.getOrganization().getId();
Query q = em.createNamedQuery("Themes.count").setParameter("org", orgId);
Number countResult = (Number) q.getSingleResult();
return countResult.intValue();
} catch (Exception e) {
logger.error("Could not count themes: {}", ExceptionUtils.getStackTrace(e));
throw new ThemesServiceDatabaseException(e);
} finally {
if (em != null) {
em.close();
}
}
}
/**
* Gets a theme by its ID, using the current organizational context.
*
* @param id
* the theme identifier
* @param em
* an open entity manager
* @return the theme entity, or null if not found
*/
private ThemeDto getThemeDto(long id, EntityManager em) {
String orgId = securityService.getOrganization().getId();
Query q = em.createNamedQuery("Themes.findById").setParameter("id", id).setParameter("org", orgId);
try {
return (ThemeDto) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* Converts a theme to a {@link SerializableTheme} for using by the theme message queue
*
* @param theme
* the theme
* @return the {@link SerializableTheme}
*/
private SerializableTheme toSerializableTheme(Theme theme) {
String creator = StringUtils.isNotBlank(theme.getCreator().getName()) ? theme.getCreator().getName() : theme
.getCreator().getUsername();
return new SerializableTheme(theme.getId().getOrElse(org.apache.commons.lang3.math.NumberUtils.LONG_MINUS_ONE),
theme.getCreationDate(), theme.isDefault(), creator, theme.getName(), theme.getDescription(),
theme.isBumperActive(), theme.getBumperFile(), theme.isTrailerActive(), theme.getTrailerFile(),
theme.isTitleSlideActive(), theme.getTitleSlideMetadata(), theme.getTitleSlideBackground(),
theme.isLicenseSlideActive(), theme.getLicenseSlideBackground(), theme.getLicenseSlideDescription(),
theme.isWatermarkActive(), theme.getWatermarkFile(), theme.getWatermarkPosition());
}
@Override
public void repopulate(final String indexName) {
final String destinationId = ThemeItem.THEME_QUEUE_PREFIX + WordUtils.capitalize(indexName);
for (final Organization organization : organizationDirectoryService.getOrganizations()) {
SecurityUtil.runAs(securityService, organization, SecurityUtil.createSystemUser(cc, organization), new Effect0() {
@Override
protected void run() {
try {
final List<Theme> themes = getThemes();
int total = themes.size();
int current = 1;
logger.info(
"Re-populating '{}' index with themes from organization {}. There are {} theme(s) to add to the index.",
new Object[] { indexName, securityService.getOrganization().getId(), total });
for (Theme theme : themes) {
messageSender.sendObjectMessage(destinationId, MessageSender.DestinationType.Queue,
ThemeItem.update(toSerializableTheme(theme)));
messageSender.sendObjectMessage(IndexProducer.RESPONSE_QUEUE, MessageSender.DestinationType.Queue,
IndexRecreateObject.update(indexName, IndexRecreateObject.Service.Themes, total, current));
current++;
}
} catch (ThemesServiceDatabaseException e) {
logger.error("Unable to get themes from the database because: {}", ExceptionUtils.getStackTrace(e));
throw new IllegalStateException(e);
}
}
});
}
Organization organization = new DefaultOrganization();
SecurityUtil.runAs(securityService, organization, SecurityUtil.createSystemUser(cc, organization), new Effect0() {
@Override
protected void run() {
messageSender.sendObjectMessage(destinationId, MessageSender.DestinationType.Queue,
IndexRecreateObject.end(indexName, IndexRecreateObject.Service.Themes));
}
});
}
@Override
public MessageReceiver getMessageReceiver() {
return messageReceiver;
}
@Override
public Service getService() {
return IndexRecreateObject.Service.Themes;
}
@Override
public String getClassName() {
return ThemesServiceDatabaseImpl.class.getName();
}
@Override
public MessageSender getMessageSender() {
return messageSender;
}
@Override
public SecurityService getSecurityService() {
return securityService;
}
@Override
public String getSystemUserName() {
return SecurityUtil.getSystemUserName(cc);
}
}