/**
* 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.authorization.xacml.manager.impl.persistence;
import static org.opencastproject.util.data.Option.none;
import static org.opencastproject.util.data.Option.option;
import static org.opencastproject.util.data.Option.some;
import static org.opencastproject.util.data.Prelude.unexhaustiveMatch;
import org.opencastproject.authorization.xacml.manager.api.AclServiceNoReferenceException;
import org.opencastproject.authorization.xacml.manager.api.EpisodeACLTransition;
import org.opencastproject.authorization.xacml.manager.api.SeriesACLTransition;
import org.opencastproject.authorization.xacml.manager.api.TransitionQuery;
import org.opencastproject.authorization.xacml.manager.api.TransitionResult;
import org.opencastproject.authorization.xacml.manager.impl.AclTransitionDb;
import org.opencastproject.authorization.xacml.manager.impl.AclTransitionDbDuplicatedException;
import org.opencastproject.authorization.xacml.manager.impl.AclTransitionDbException;
import org.opencastproject.authorization.xacml.manager.impl.TransitionResultImpl;
import org.opencastproject.security.api.AclScope;
import org.opencastproject.security.api.Organization;
import org.opencastproject.util.NotFoundException;
import org.opencastproject.util.data.Collections;
import org.opencastproject.util.data.Option;
import org.opencastproject.util.data.functions.Misc;
import org.opencastproject.workflow.api.ConfiguredWorkflowRef;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Date;
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;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
public final class OsgiJpaAclTransitionDb implements AclTransitionDb {
/** Logging utilities */
private static final Logger logger = LoggerFactory.getLogger(OsgiJpaAclTransitionDb.class);
/** JPA persistence unit name */
public static final String PERSISTENCE_UNIT = "org.opencastproject.authorization.xacml.manager";
/** Factory used to create {@link EntityManager}s for transactions */
private EntityManagerFactory emf;
/** OSGi DI */
public void setEntityManagerFactory(EntityManagerFactory emf) {
this.emf = emf;
}
/**
* Creates {@link EntityManagerFactory} using persistence provider and properties passed via OSGi.
*
* @param cc
*/
public void activate(ComponentContext cc) {
logger.info("Activating persistence manager for ACL manager");
}
@Override
public EpisodeACLTransition storeEpisodeAclTransition(Organization org, String episodeId, Date applicationDate,
Option<Long> managedAclId, Option<ConfiguredWorkflowRef> workflow) throws AclTransitionDbException {
EntityManager em = null;
EntityTransaction tx = null;
try {
em = emf.createEntityManager();
tx = em.getTransaction();
tx.begin();
EpisodeAclTransitionEntity entity = new EpisodeAclTransitionEntity().update(episodeId, org.getId(),
applicationDate, getManagedAcl(em, managedAclId, org), workflow);
em.persist(entity);
tx.commit();
return entity;
} catch (AclTransitionDbException e) {
throw e;
} catch (Exception e) {
if (tx != null && tx.isActive())
tx.rollback();
boolean isConstraintViolation = Util.isConstraintViolationException(e);
if (isConstraintViolation)
throw new AclTransitionDbDuplicatedException();
else {
logger.error("Could not store the scheduled episode ACL: {}", e.getMessage());
throw new AclTransitionDbException(e);
}
} finally {
if (em != null)
em.close();
}
}
@Override
public SeriesACLTransition storeSeriesAclTransition(Organization org, String seriesId, Date applicationDate,
long managedAclId, boolean override, Option<ConfiguredWorkflowRef> workflow) throws AclTransitionDbException {
EntityManager em = null;
EntityTransaction tx = null;
try {
em = emf.createEntityManager();
tx = em.getTransaction();
tx.begin();
SeriesAclTransitionEntity entity = new SeriesAclTransitionEntity().update(seriesId, org.getId(), applicationDate,
getManagedAcl(em, some(managedAclId), org).get(), workflow, override);
em.persist(entity);
tx.commit();
return entity;
} catch (AclTransitionDbException e) {
throw e;
} catch (Exception e) {
if (tx != null && tx.isActive())
tx.rollback();
boolean isContraintViolation = Util.isConstraintViolationException(e);
if (isContraintViolation)
throw new AclTransitionDbDuplicatedException();
else {
logger.error("Could not store the scheduled series ACL: {}", e.getMessage());
throw new AclTransitionDbException(e);
}
} finally {
if (em != null)
em.close();
}
}
@Override
public EpisodeACLTransition updateEpisodeAclTransition(Organization org, long transitionId, Date applicationDate,
Option<Long> managedAclId, Option<ConfiguredWorkflowRef> workflow) throws AclTransitionDbException,
NotFoundException {
EntityManager em = null;
EntityTransaction tx = null;
try {
em = emf.createEntityManager();
tx = em.getTransaction();
tx.begin();
EpisodeAclTransitionEntity entity = getEpisodeEntity(transitionId, org.getId(), em);
if (entity == null)
throw new NotFoundException("Episode transition " + transitionId + " not found!");
entity.update(entity.getEpisodeId(), org.getId(), applicationDate, getManagedAcl(em, managedAclId, org), workflow);
em.merge(entity);
tx.commit();
return entity;
} catch (NotFoundException e) {
throw e;
} catch (AclTransitionDbException e) {
throw e;
} catch (Exception e) {
if (tx != null && tx.isActive())
tx.rollback();
boolean isContraintViolation = Util.isConstraintViolationException(e);
if (isContraintViolation)
throw new AclTransitionDbDuplicatedException();
else {
logger.error("Could not update the scheduled episode ACL: {}", e.getMessage());
throw new AclTransitionDbException(e);
}
} finally {
if (em != null)
em.close();
}
}
@Override
public SeriesACLTransition updateSeriesAclTransition(Organization org, long transitionId, Date applicationDate,
long managedAclId, boolean override, Option<ConfiguredWorkflowRef> workflow) throws AclTransitionDbException,
NotFoundException {
EntityManager em = null;
EntityTransaction tx = null;
try {
em = emf.createEntityManager();
tx = em.getTransaction();
tx.begin();
SeriesAclTransitionEntity entity = getSeriesEntity(transitionId, org.getId(), em);
if (entity == null)
throw new NotFoundException("Series transition " + transitionId + " not found!");
entity.update(entity.getSeriesId(), org.getId(), applicationDate, getManagedAcl(em, some(managedAclId), org)
.get(), workflow, override);
em.merge(entity);
tx.commit();
return entity;
} catch (NotFoundException e) {
throw e;
} catch (AclTransitionDbException e) {
throw e;
} catch (Exception e) {
if (tx != null && tx.isActive())
tx.rollback();
boolean isContraintViolation = Util.isConstraintViolationException(e);
if (isContraintViolation)
throw new AclTransitionDbDuplicatedException();
else {
logger.error("Could not update the scheduled series ACL: {}", e.getMessage());
throw new AclTransitionDbException(e);
}
} finally {
if (em != null)
em.close();
}
}
@Override
public void deleteEpisodeAclTransition(Organization org, long transitionId) throws NotFoundException {
EntityManager em = null;
EntityTransaction tx = null;
try {
em = emf.createEntityManager();
tx = em.getTransaction();
tx.begin();
EpisodeAclTransitionEntity entity = getEpisodeEntity(transitionId, org.getId(), em);
if (entity == null)
throw new NotFoundException();
em.remove(entity);
tx.commit();
} finally {
if (em != null)
em.close();
}
}
@Override
public void deleteSeriesAclTransition(Organization org, long transitionId) throws NotFoundException {
EntityManager em = null;
EntityTransaction tx = null;
try {
em = emf.createEntityManager();
tx = em.getTransaction();
tx.begin();
SeriesAclTransitionEntity entity = getSeriesEntity(transitionId, org.getId(), em);
if (entity == null)
throw new NotFoundException();
em.remove(entity);
tx.commit();
} finally {
if (em != null)
em.close();
}
}
@Override
public List<EpisodeACLTransition> getEpisodeAclTransitions(Organization org, String episodeId)
throws AclTransitionDbException {
EntityManager em = null;
try {
em = emf.createEntityManager();
return Misc.<EpisodeACLTransition> widen(getEpisodeEntities(episodeId, org.getId(), em));
} catch (Exception e) {
logger.warn("Error parsing episode ACL: {}", e);
throw new AclTransitionDbException(e);
} finally {
if (em != null)
em.close();
}
}
@Override
public List<SeriesACLTransition> getSeriesAclTransitions(Organization org, String seriesId)
throws AclTransitionDbException {
EntityManager em = null;
try {
em = emf.createEntityManager();
return Misc.<SeriesACLTransition> widen(getSeriesEntities(seriesId, org.getId(), em));
} catch (Exception e) {
logger.warn("Error parsing episode ACL: {}", e);
throw new AclTransitionDbException(e);
} finally {
if (em != null)
em.close();
}
}
@Override
public TransitionResult getByQuery(Organization org, TransitionQuery query) throws AclTransitionDbException {
for (AclScope scope : query.getScope()) {
switch (scope) {
case Episode:
return getEpisodeResult(query, org.getId());
case Series:
return getSeriesResult(query, org.getId());
default:
unexhaustiveMatch();
}
}
// none
return new TransitionResultImpl(getEpisodeResult(query, org.getId()).getEpisodeTransistions(), getSeriesResult(
query, org.getId()).getSeriesTransistions());
}
@Override
public SeriesACLTransition markSeriesTransitionAsCompleted(Organization org, long transitionId)
throws AclTransitionDbException, NotFoundException {
EntityManager em = null;
EntityTransaction tx = null;
try {
em = emf.createEntityManager();
tx = em.getTransaction();
tx.begin();
SeriesAclTransitionEntity entity = getSeriesEntity(transitionId, org.getId(), em);
if (entity == null)
throw new NotFoundException("Series transition " + transitionId + " not found!");
entity.setDone(true);
em.merge(entity);
tx.commit();
return entity;
} catch (NotFoundException e) {
throw e;
} catch (Exception e) {
logger.error("Could not update the scheduled series ACL: {}", e.getMessage());
if (tx != null && tx.isActive())
tx.rollback();
throw new AclTransitionDbException(e);
} finally {
if (em != null)
em.close();
}
}
@Override
public EpisodeACLTransition markEpisodeTransitionAsCompleted(Organization org, long transitionId)
throws AclTransitionDbException, NotFoundException {
EntityManager em = null;
EntityTransaction tx = null;
try {
em = emf.createEntityManager();
tx = em.getTransaction();
tx.begin();
EpisodeAclTransitionEntity entity = getEpisodeEntity(transitionId, org.getId(), em);
if (entity == null)
throw new NotFoundException("Episode transition " + transitionId + " not found!");
entity.setDone(true);
em.merge(entity);
tx.commit();
return entity;
} catch (NotFoundException e) {
throw e;
} catch (Exception e) {
logger.error("Could not update the scheduled episode ACL: {}", e.getMessage());
if (tx != null && tx.isActive())
tx.rollback();
throw new AclTransitionDbException(e);
} finally {
if (em != null)
em.close();
}
}
/**
* Search episode transitions with the given transition query and return it in a transition result
*
* @param query
* the transition query
* @return the episode transition result
* @throws AclTransitionDbException
* if exception occurs during reading/storing from the persistence layer
*/
private TransitionResult getEpisodeResult(TransitionQuery query, String orgId) throws AclTransitionDbException {
for (long transitionId : query.getTransitionId()) {
EntityManager em = null;
try {
em = emf.createEntityManager();
return new TransitionResultImpl(Misc.<EpisodeACLTransition> widen(option(
getEpisodeEntity(transitionId, orgId, em)).list()), Collections.<SeriesACLTransition> nil());
} catch (Exception e) {
logger.warn("Error parsing episode ACL: {}", e);
throw new AclTransitionDbException(e);
} finally {
if (em != null)
em.close();
}
}
EntityManager em = null;
try {
em = emf.createEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<EpisodeAclTransitionEntity> q = cb.createQuery(EpisodeAclTransitionEntity.class);
Root<EpisodeAclTransitionEntity> c = q.from(EpisodeAclTransitionEntity.class);
q.select(c);
// create predicates joined in an "and" expression
final List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(cb.equal(c.get("organizationId"), orgId));
for (String p : query.getId())
predicates.add(cb.equal(c.get("episodeId"), p));
for (Boolean p : query.getDone()) {
if (p)
predicates.add(cb.isTrue(c.get("done").as(Boolean.class)));
else
predicates.add(cb.isFalse(c.get("done").as(Boolean.class)));
}
for (Long p : query.getAclId())
predicates.add(cb.equal(c.get("managedAcl").get("id").as(Long.class), p));
for (Date p : query.getAfter())
predicates.add(cb.greaterThanOrEqualTo(c.get("applicationDate").as(Date.class), p));
for (Date p : query.getBefore())
predicates.add(cb.lessThanOrEqualTo(c.get("applicationDate").as(Date.class), p));
q.where(cb.and(toArray(predicates)));
q.orderBy(cb.asc(c.get("applicationDate")));
TypedQuery<EpisodeAclTransitionEntity> typedQuery = em.createQuery(q);
return new TransitionResultImpl(Misc.<EpisodeACLTransition> widen(typedQuery.getResultList()),
Collections.<SeriesACLTransition> nil());
} finally {
if (em != null)
em.close();
}
}
/**
* Search series transitions with the given transition query and return it in a transition result
*
* @param query
* the transition query
* @return the series transition result
* @throws AclTransitionDbException
* if exception occurs during reading/storing from the persistence layer
*/
private TransitionResult getSeriesResult(TransitionQuery query, String orgId) throws AclTransitionDbException {
for (long transitionId : query.getTransitionId()) {
EntityManager em = null;
try {
em = emf.createEntityManager();
return new TransitionResultImpl(Collections.<EpisodeACLTransition> nil(),
Misc.<SeriesACLTransition> widen(option(getSeriesEntity(transitionId, orgId, em)).list()));
} catch (Exception e) {
logger.warn("Error parsing episode ACL: {}", e);
throw new AclTransitionDbException(e);
} finally {
if (em != null)
em.close();
}
}
EntityManager em = null;
try {
em = emf.createEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<SeriesAclTransitionEntity> q = cb.createQuery(SeriesAclTransitionEntity.class);
Root<SeriesAclTransitionEntity> c = q.from(SeriesAclTransitionEntity.class);
q.select(c);
// create predicates joined in an "and" expression
final List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(cb.equal(c.get("organizationId"), orgId));
for (String p : query.getId())
predicates.add(cb.equal(c.get("seriesId"), p));
for (Boolean p : query.getDone()) {
if (p)
predicates.add(cb.isTrue(c.get("done").as(Boolean.class)));
else
predicates.add(cb.isFalse(c.get("done").as(Boolean.class)));
}
for (Long p : query.getAclId())
predicates.add(cb.equal(c.get("managedAcl").get("id").as(Long.class), p));
for (Date p : query.getAfter())
predicates.add(cb.greaterThanOrEqualTo(c.get("applicationDate").as(Date.class), p));
for (Date p : query.getBefore())
predicates.add(cb.lessThanOrEqualTo(c.get("applicationDate").as(Date.class), p));
q.where(cb.and(toArray(predicates)));
q.orderBy(cb.asc(c.get("applicationDate")));
TypedQuery<SeriesAclTransitionEntity> typedQuery = em.createQuery(q);
return new TransitionResultImpl(Collections.<EpisodeACLTransition> nil(),
Misc.<SeriesACLTransition> widen(typedQuery.getResultList()));
} finally {
if (em != null)
em.close();
}
}
/**
* Gets all series ACL entities by the series id as a list, using the current organizational context.
*
* @param seriesId
* the series id
* @param em
* an open entity manager
* @return the series ACL entity list
*/
@SuppressWarnings("unchecked")
private List<SeriesAclTransitionEntity> getSeriesEntities(String seriesId, String orgId, EntityManager em) {
Query q = em.createNamedQuery("SeriesAcl.findBySeriesId");
q.setParameter("id", seriesId);
q.setParameter("organizationId", orgId);
return q.getResultList();
}
/**
* Gets all episode ACL entities by the episode id as a list, using the current organizational context.
*
* @param episodeId
* the episode id
* @param em
* an open entity manager
* @return the episode ACL entity list
*/
@SuppressWarnings("unchecked")
private List<EpisodeAclTransitionEntity> getEpisodeEntities(String episodeId, String orgId, EntityManager em) {
Query q = em.createNamedQuery("EpisodeAcl.findByEpisodeId");
q.setParameter("id", episodeId);
q.setParameter("organizationId", orgId);
return q.getResultList();
}
/**
* Gets a series ACL entity by its ID, using the current organizational context.
*
* @param id
* the transition identifier
* @param em
* an open entity manager
* @return the series ACL entity, or null if not found
*/
private SeriesAclTransitionEntity getSeriesEntity(long id, String orgId, EntityManager em) {
Query q = em.createNamedQuery("SeriesAcl.findByTransitionId");
q.setParameter("id", id);
q.setParameter("organizationId", orgId);
try {
return (SeriesAclTransitionEntity) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* Gets a episode ACL entity by its ID, using the current organizational context.
*
* @param id
* the transition identifier
* @param em
* an open entity manager
* @return the episode ACL entity, or null if not found
*/
private EpisodeAclTransitionEntity getEpisodeEntity(long id, String orgId, EntityManager em) {
Query q = em.createNamedQuery("EpisodeAcl.findByTransitionId");
q.setParameter("id", id);
q.setParameter("organizationId", orgId);
try {
return (EpisodeAclTransitionEntity) q.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
private static Predicate[] toArray(List<Predicate> predicates) {
return predicates.toArray(new Predicate[predicates.size()]);
}
/** Return none if <code>aclId</code> is none, throw NotFoundException if acl cannot be found. */
private static Option<ManagedAclEntity> getManagedAcl(EntityManager em, Option<Long> aclId, Organization org)
throws AclServiceNoReferenceException {
for (Long id : aclId) {
for (ManagedAclEntity e : ManagedAclEntity.findByIdAndOrg(org.getId(), id).apply(em)) {
return some(e);
}
throw new AclServiceNoReferenceException("Cannot find ACL " + aclId);
}
return none();
}
}