PlaylistDatabaseServiceImpl.java
/*
* 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.playlists.persistence;
import static org.opencastproject.db.Queries.namedQuery;
import org.opencastproject.db.DBSession;
import org.opencastproject.db.DBSessionFactory;
import org.opencastproject.playlists.Playlist;
import org.opencastproject.security.api.SecurityService;
import org.opencastproject.util.NotFoundException;
import org.opencastproject.util.requests.SortCriterion;
import org.apache.commons.lang3.tuple.Pair;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
@Component(
immediate = true,
service = { PlaylistDatabaseService.class },
property = {
"service.description=Playlist Database Service"
}
)
public class PlaylistDatabaseServiceImpl implements PlaylistDatabaseService {
/** JPA persistence unit name */
public static final String PERSISTENCE_UNIT = "org.opencastproject.playlists";
private static final Logger logger = LoggerFactory.getLogger(PlaylistDatabaseServiceImpl.class);
/** Factory used to create {@link EntityManager}s for transactions */
private EntityManagerFactory emf;
private DBSessionFactory dbSessionFactory;
private DBSession db;
/** The security service */
protected SecurityService securityService;
/** OSGi DI */
@Reference(target = "(osgi.unit.name=org.opencastproject.playlists)")
public void setEntityManagerFactory(EntityManagerFactory emf) {
this.emf = emf;
}
@Reference
public void setDBSessionFactory(DBSessionFactory dbSessionFactory) {
this.dbSessionFactory = dbSessionFactory;
}
/**
* OSGi callback to set the security service.
*
* @param securityService
* the securityService to set
*/
@Reference(name = "security-service")
public void setSecurityService(SecurityService securityService) {
this.securityService = securityService;
}
@Activate
public void activate(ComponentContext cc) {
logger.info("Activating persistence manager for playlists");
db = dbSessionFactory.createSession(emf);
}
/**
* {@inheritDoc}
* @see PlaylistDatabaseService#getPlaylist(String)
*/
@Override
public Playlist getPlaylist(String playlistId) throws NotFoundException, PlaylistDatabaseException {
return getPlaylist(playlistId, securityService.getOrganization().getId());
}
/**
* {@inheritDoc}
* @see PlaylistDatabaseService#getPlaylist(String, String)
*/
@Override
public Playlist getPlaylist(String playlistId, String orgId) throws NotFoundException, PlaylistDatabaseException {
try {
return db.execTxChecked(em -> {
Optional<Playlist> playlist = getPlaylistById(playlistId, orgId).apply(em);
if (playlist.isEmpty()) {
throw new NotFoundException("No playlist with id=" + playlistId + " exists");
}
return playlist.get();
});
} catch (NotFoundException e) {
throw e;
} catch (Exception e) {
logger.error("Could not retrieve playlist with ID '{}'", playlistId, e);
throw new PlaylistDatabaseException(e);
}
}
/**
* {@inheritDoc}
* @see PlaylistDatabaseService#getPlaylists(int, int, SortCriterion)
*/
@Override
public List<Playlist> getPlaylists(int limit, int offset, SortCriterion sortCriterion)
throws PlaylistDatabaseException {
try {
return db.exec(em -> {
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Playlist> criteriaQuery = criteriaBuilder.createQuery(Playlist.class);
Root<Playlist> from = criteriaQuery.from(Playlist.class);
CriteriaQuery<Playlist> select = criteriaQuery.select(from)
.where(criteriaBuilder.isNull(from.get("deletionDate")));
if (sortCriterion.getOrder().equals(SortCriterion.Order.Ascending)) {
criteriaQuery.orderBy(criteriaBuilder.asc(from.get(sortCriterion.getFieldName())));
} else if (sortCriterion.getOrder().equals(SortCriterion.Order.Descending)) {
criteriaQuery.orderBy(criteriaBuilder.desc(from.get(sortCriterion.getFieldName())));
}
TypedQuery<Playlist> allQuery = em.createQuery(select);
allQuery.setMaxResults(limit);
allQuery.setFirstResult(offset);
return allQuery.getResultList();
});
} catch (Exception e) {
throw new PlaylistDatabaseException("Error fetching playlists from database", e);
}
}
public List<Playlist> getAllForAdministrativeRead(Date startDate, Date endDate, int limit)
throws PlaylistDatabaseException {
try {
return db.exec(em -> {
final var criteriaBuilder = em.getCriteriaBuilder();
final var criteriaQuery = criteriaBuilder.createQuery(Playlist.class);
final var from = criteriaQuery.from(Playlist.class);
final var org = securityService.getOrganization().getId();
final var select = criteriaQuery.select(from)
.where(
criteriaBuilder.greaterThanOrEqualTo(from.get("updated"), startDate),
criteriaBuilder.lessThan(from.get("updated"), endDate),
criteriaBuilder.equal(from.get("organization"), org)
)
.orderBy(criteriaBuilder.asc(from.get("updated")));
return em.createQuery(select)
.setMaxResults(limit)
.getResultList();
});
} catch (Exception e) {
throw new PlaylistDatabaseException("Error fetching playlists from database", e);
}
}
/**
* {@inheritDoc}
* @see PlaylistDatabaseService#updatePlaylist(Playlist, String)
*/
@Override
public Playlist updatePlaylist(Playlist playlist, String orgId) throws PlaylistDatabaseException {
try {
return db.execTx(em -> {
Optional<Playlist> fromDb = getPlaylistById(playlist.getId(), orgId).apply(em);
playlist.setUpdated(new Date());
if (fromDb.isEmpty()) {
em.persist(playlist);
} else {
return em.merge(playlist);
}
return playlist;
});
} catch (Exception e) {
throw new PlaylistDatabaseException("Could not update playlist with ID '" + playlist.getId() + "'", e);
}
}
/**
* {@inheritDoc}
* @see PlaylistDatabaseService#deletePlaylist(Playlist, String)
*/
@Override
public Playlist deletePlaylist(Playlist playlist, String orgId) throws PlaylistDatabaseException {
try {
return db.execTx(em -> {
Optional<Playlist> fromDb = getPlaylistById(playlist.getId(), orgId).apply(em);
if (fromDb.isPresent()) {
Date now = new Date();
playlist.setUpdated(now);
playlist.setDeletionDate(now);
em.merge(playlist);
}
logger.debug("Playlist with id {} was deleted.", playlist.getId());
return playlist;
});
} catch (Exception e) {
throw new PlaylistDatabaseException("Could not delete playlist with ID '" + playlist.getId() + "'", e);
}
}
/**
* Gets a non-deleted playlist by its id
*
* @param playlistId the playlist identifier
* @param orgId the organisation identifier
* @return the playlist in an optional
*/
protected Function<EntityManager, Optional<Playlist>> getPlaylistById(String playlistId, String orgId) {
return em -> getPotentiallyDeletedPlaylist(playlistId, orgId).apply(em).filter(e -> !e.isDeleted());
}
/**
* Gets a potentially deleted series by its ID, using the current organizational context.
*
* @param playlistId the playlist identifier
* @param orgId the organisation identifier
* @return the playlist in an optional
*/
protected Function<EntityManager, Optional<Playlist>> getPotentiallyDeletedPlaylist(String playlistId, String orgId) {
return namedQuery.findOpt(
"Playlist.findById",
Playlist.class,
Pair.of("id", playlistId),
Pair.of("organizationId", orgId)
);
}
}