/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * licenses this file to you under the Apache 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 the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>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.apereo.portal.events.aggr.dao.jpa; import com.google.common.base.Function; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.ParameterExpression; import javax.persistence.criteria.Root; import org.apereo.portal.events.aggr.AcademicTermDetail; import org.apereo.portal.events.aggr.AggregatedGroupConfig; import org.apereo.portal.events.aggr.AggregatedIntervalConfig; import org.apereo.portal.events.aggr.EventDateTimeUtils; import org.apereo.portal.events.aggr.IEventAggregatorStatus; import org.apereo.portal.events.aggr.IEventAggregatorStatus.ProcessingType; import org.apereo.portal.events.aggr.IPortalEventAggregator; import org.apereo.portal.events.aggr.QuarterDetail; import org.apereo.portal.events.aggr.dao.IEventAggregationManagementDao; import org.apereo.portal.jpa.BaseAggrEventsJpaDao; import org.apereo.portal.jpa.OpenEntityManager; import org.springframework.dao.support.DataAccessUtils; import org.springframework.stereotype.Repository; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; @Repository public class JpaEventAggregationManagementDao extends BaseAggrEventsJpaDao implements IEventAggregationManagementDao { private static final Class<IPortalEventAggregator> DEFAULT_AGGREGATOR_TYPE = IPortalEventAggregator.class; private CriteriaQuery<AggregatedGroupConfigImpl> findAllGroupConfigsQuery; private CriteriaQuery<AggregatedGroupConfigImpl> findGroupConfigForAggregatorQuery; private CriteriaQuery<AggregatedIntervalConfigImpl> findAllIntervalConfigsQuery; private CriteriaQuery<AggregatedIntervalConfigImpl> findIntervalConfigForAggregatorQuery; private CriteriaQuery<QuarterDetailImpl> findAllQuarterDetailsQuery; private CriteriaQuery<AcademicTermDetailImpl> findAllAcademicTermDetailsQuery; private ParameterExpression<Class> aggregatorTypeParameter; @Override public void afterPropertiesSet() throws Exception { this.aggregatorTypeParameter = this.createParameterExpression(Class.class, "aggregatorType"); this.findAllGroupConfigsQuery = this.createCriteriaQuery( new Function<CriteriaBuilder, CriteriaQuery<AggregatedGroupConfigImpl>>() { @Override public CriteriaQuery<AggregatedGroupConfigImpl> apply( CriteriaBuilder cb) { final CriteriaQuery<AggregatedGroupConfigImpl> criteriaQuery = cb.createQuery(AggregatedGroupConfigImpl.class); final Root<AggregatedGroupConfigImpl> entityRoot = criteriaQuery.from(AggregatedGroupConfigImpl.class); criteriaQuery.select(entityRoot); entityRoot.fetch( AggregatedGroupConfigImpl_.excludedGroups, JoinType.LEFT); entityRoot.fetch( AggregatedGroupConfigImpl_.includedGroups, JoinType.LEFT); return criteriaQuery; } }); this.findGroupConfigForAggregatorQuery = this.createCriteriaQuery( new Function<CriteriaBuilder, CriteriaQuery<AggregatedGroupConfigImpl>>() { @Override public CriteriaQuery<AggregatedGroupConfigImpl> apply( CriteriaBuilder cb) { final CriteriaQuery<AggregatedGroupConfigImpl> criteriaQuery = cb.createQuery(AggregatedGroupConfigImpl.class); final Root<AggregatedGroupConfigImpl> entityRoot = criteriaQuery.from(AggregatedGroupConfigImpl.class); criteriaQuery.select(entityRoot); entityRoot.fetch( AggregatedGroupConfigImpl_.excludedGroups, JoinType.LEFT); entityRoot.fetch( AggregatedGroupConfigImpl_.includedGroups, JoinType.LEFT); criteriaQuery.where( cb.equal( entityRoot.get( AggregatedGroupConfigImpl_.aggregatorType), aggregatorTypeParameter)); return criteriaQuery; } }); this.findAllIntervalConfigsQuery = this.createCriteriaQuery( new Function< CriteriaBuilder, CriteriaQuery<AggregatedIntervalConfigImpl>>() { @Override public CriteriaQuery<AggregatedIntervalConfigImpl> apply( CriteriaBuilder cb) { final CriteriaQuery<AggregatedIntervalConfigImpl> criteriaQuery = cb.createQuery(AggregatedIntervalConfigImpl.class); final Root<AggregatedIntervalConfigImpl> entityRoot = criteriaQuery.from(AggregatedIntervalConfigImpl.class); criteriaQuery.select(entityRoot); entityRoot.fetch( AggregatedIntervalConfigImpl_.excludedIntervals, JoinType.LEFT); entityRoot.fetch( AggregatedIntervalConfigImpl_.includedIntervals, JoinType.LEFT); return criteriaQuery; } }); this.findIntervalConfigForAggregatorQuery = this.createCriteriaQuery( new Function< CriteriaBuilder, CriteriaQuery<AggregatedIntervalConfigImpl>>() { @Override public CriteriaQuery<AggregatedIntervalConfigImpl> apply( CriteriaBuilder cb) { final CriteriaQuery<AggregatedIntervalConfigImpl> criteriaQuery = cb.createQuery(AggregatedIntervalConfigImpl.class); final Root<AggregatedIntervalConfigImpl> entityRoot = criteriaQuery.from(AggregatedIntervalConfigImpl.class); criteriaQuery.select(entityRoot); entityRoot.fetch( AggregatedIntervalConfigImpl_.excludedIntervals, JoinType.LEFT); entityRoot.fetch( AggregatedIntervalConfigImpl_.includedIntervals, JoinType.LEFT); criteriaQuery.where( cb.equal( entityRoot.get( AggregatedIntervalConfigImpl_ .aggregatorType), aggregatorTypeParameter)); return criteriaQuery; } }); this.findAllQuarterDetailsQuery = this.createCriteriaQuery( new Function<CriteriaBuilder, CriteriaQuery<QuarterDetailImpl>>() { @Override public CriteriaQuery<QuarterDetailImpl> apply(CriteriaBuilder cb) { final CriteriaQuery<QuarterDetailImpl> criteriaQuery = cb.createQuery(QuarterDetailImpl.class); final Root<QuarterDetailImpl> entityRoot = criteriaQuery.from(QuarterDetailImpl.class); criteriaQuery.select(entityRoot); criteriaQuery.orderBy( cb.asc(entityRoot.get(QuarterDetailImpl_.quarterId))); return criteriaQuery; } }); this.findAllAcademicTermDetailsQuery = this.createCriteriaQuery( new Function<CriteriaBuilder, CriteriaQuery<AcademicTermDetailImpl>>() { @Override public CriteriaQuery<AcademicTermDetailImpl> apply(CriteriaBuilder cb) { final CriteriaQuery<AcademicTermDetailImpl> criteriaQuery = cb.createQuery(AcademicTermDetailImpl.class); final Root<AcademicTermDetailImpl> entityRoot = criteriaQuery.from(AcademicTermDetailImpl.class); criteriaQuery.select(entityRoot); criteriaQuery.orderBy( cb.asc(entityRoot.get(AcademicTermDetailImpl_.start))); return criteriaQuery; } }); } @OpenEntityManager(unitName = PERSISTENCE_UNIT_NAME) @Override public IEventAggregatorStatus getEventAggregatorStatus( final ProcessingType processingType, boolean create) { final NaturalIdQuery<EventAggregatorStatusImpl> query = this.createNaturalIdQuery(EventAggregatorStatusImpl.class); query.using(EventAggregatorStatusImpl_.processingType, processingType); EventAggregatorStatusImpl status = query.load(); //Create the status object if it doesn't yet exist if (status == null && create) { status = this.getTransactionOperations() .execute( new TransactionCallback<EventAggregatorStatusImpl>() { @Override public EventAggregatorStatusImpl doInTransaction( TransactionStatus status) { final EventAggregatorStatusImpl eventAggregatorStatus = new EventAggregatorStatusImpl(processingType); getEntityManager().persist(eventAggregatorStatus); return eventAggregatorStatus; } }); } return status; } @Override @AggrEventsTransactional public void updateEventAggregatorStatus(IEventAggregatorStatus eventAggregatorStatus) { this.getEntityManager().persist(eventAggregatorStatus); } @Override public AggregatedGroupConfig getDefaultAggregatedGroupConfig() { AggregatedGroupConfig groupConfig = this.getAggregatedGroupConfig(DEFAULT_AGGREGATOR_TYPE); if (groupConfig == null) { groupConfig = this.getTransactionOperations() .execute( new TransactionCallback<AggregatedGroupConfig>() { @Override public AggregatedGroupConfig doInTransaction( TransactionStatus status) { return createAggregatedGroupConfig( DEFAULT_AGGREGATOR_TYPE); } }); } return groupConfig; } @Override public Set<AggregatedGroupConfig> getAggregatedGroupConfigs() { final TypedQuery<AggregatedGroupConfigImpl> query = this.createCachedQuery(this.findAllGroupConfigsQuery); final List<AggregatedGroupConfigImpl> results = query.getResultList(); return new LinkedHashSet<AggregatedGroupConfig>(results); } @Override public AggregatedGroupConfig getAggregatedGroupConfig( Class<? extends IPortalEventAggregator> aggregatorType) { final TypedQuery<AggregatedGroupConfigImpl> query = this.createCachedQuery(this.findGroupConfigForAggregatorQuery); query.setParameter(this.aggregatorTypeParameter, aggregatorType); return DataAccessUtils.uniqueResult(query.getResultList()); } @Override @AggrEventsTransactional public AggregatedGroupConfig createAggregatedGroupConfig( Class<? extends IPortalEventAggregator> aggregatorType) { final AggregatedGroupConfig aggregatedGroupConfig = new AggregatedGroupConfigImpl(aggregatorType); this.getEntityManager().persist(aggregatedGroupConfig); return aggregatedGroupConfig; } @Override @AggrEventsTransactional public void updateAggregatedGroupConfig(AggregatedGroupConfig aggregatedGroupConfig) { this.getEntityManager().persist(aggregatedGroupConfig); } @Override @AggrEventsTransactional public void deleteAggregatedGroupConfig(AggregatedGroupConfig aggregatedGroupConfig) { this.getEntityManager().remove(aggregatedGroupConfig); } @Override public Set<AggregatedIntervalConfig> getAggregatedIntervalConfigs() { final TypedQuery<AggregatedIntervalConfigImpl> query = this.createCachedQuery(this.findAllIntervalConfigsQuery); final List<AggregatedIntervalConfigImpl> results = query.getResultList(); return new LinkedHashSet<AggregatedIntervalConfig>(results); } @Override public AggregatedIntervalConfig getDefaultAggregatedIntervalConfig() { AggregatedIntervalConfig intervalConfig = this.getAggregatedIntervalConfig(DEFAULT_AGGREGATOR_TYPE); if (intervalConfig == null) { intervalConfig = this.getTransactionOperations() .execute( new TransactionCallback<AggregatedIntervalConfig>() { @Override public AggregatedIntervalConfig doInTransaction( TransactionStatus status) { return createAggregatedIntervalConfig( DEFAULT_AGGREGATOR_TYPE); } }); } return intervalConfig; } @Override public AggregatedIntervalConfig getAggregatedIntervalConfig( Class<? extends IPortalEventAggregator> aggregatorType) { final TypedQuery<AggregatedIntervalConfigImpl> query = this.createCachedQuery(this.findIntervalConfigForAggregatorQuery); query.setParameter(this.aggregatorTypeParameter, aggregatorType); return DataAccessUtils.uniqueResult(query.getResultList()); } @Override @AggrEventsTransactional public AggregatedIntervalConfig createAggregatedIntervalConfig( Class<? extends IPortalEventAggregator> aggregatorType) { final AggregatedIntervalConfig aggregatedIntervalConfig = new AggregatedIntervalConfigImpl(aggregatorType); this.getEntityManager().persist(aggregatedIntervalConfig); return aggregatedIntervalConfig; } @Override @AggrEventsTransactional public void updateAggregatedIntervalConfig(AggregatedIntervalConfig aggregatedIntervalConfig) { this.getEntityManager().persist(aggregatedIntervalConfig); } @Override @AggrEventsTransactional public void deleteAggregatedIntervalConfig(AggregatedIntervalConfig aggregatedIntervalConfig) { this.getEntityManager().remove(aggregatedIntervalConfig); } @Override public List<QuarterDetail> getQuartersDetails() { final TypedQuery<QuarterDetailImpl> query = this.createCachedQuery(this.findAllQuarterDetailsQuery); final List<QuarterDetailImpl> results = query.getResultList(); if (results.size() == 4) { return new ArrayList<QuarterDetail>(results); } //No valid quarters config in db, populate the standard quarters return this.getTransactionOperations() .execute( new TransactionCallback<List<QuarterDetail>>() { @Override public List<QuarterDetail> doInTransaction(TransactionStatus status) { final List<QuarterDetail> standardQuarters = EventDateTimeUtils.createStandardQuarters(); setQuarterDetails(standardQuarters); return standardQuarters; } }); } @Override @AggrEventsTransactional public void setQuarterDetails(List<QuarterDetail> newQuarterDetails) { newQuarterDetails = EventDateTimeUtils.validateQuarters(newQuarterDetails); final TypedQuery<QuarterDetailImpl> query = this.createCachedQuery(this.findAllQuarterDetailsQuery); final Set<QuarterDetailImpl> existingQuarterDetails = new HashSet<QuarterDetailImpl>(query.getResultList()); for (final Iterator<QuarterDetail> newQuarterDetailsItr = newQuarterDetails.iterator(); newQuarterDetailsItr.hasNext(); ) { final QuarterDetail quarterDetail = newQuarterDetailsItr.next(); //If QD exists in both new and existing remove it from both if (existingQuarterDetails.remove(quarterDetail)) { newQuarterDetailsItr.remove(); } } final EntityManager entityManager = this.getEntityManager(); //Delete all existing QDs that were not in the new list for (final QuarterDetailImpl existingQuarterDetail : existingQuarterDetails) { entityManager.remove(existingQuarterDetail); } entityManager.flush(); //Add all new QDs that were not in the existing set for (final QuarterDetail newQuarterDetail : newQuarterDetails) { entityManager.persist(newQuarterDetail); } } @Override public List<AcademicTermDetail> getAcademicTermDetails() { final TypedQuery<AcademicTermDetailImpl> query = this.createCachedQuery(this.findAllAcademicTermDetailsQuery); return new ArrayList<AcademicTermDetail>(query.getResultList()); } @Override @AggrEventsTransactional public void setAcademicTermDetails(List<AcademicTermDetail> newAcademicTermDetails) { newAcademicTermDetails = EventDateTimeUtils.validateAcademicTerms(newAcademicTermDetails); final TypedQuery<AcademicTermDetailImpl> query = this.createCachedQuery(this.findAllAcademicTermDetailsQuery); final Set<AcademicTermDetailImpl> existingAcademicTermDetails = new HashSet<AcademicTermDetailImpl>(query.getResultList()); for (final Iterator<AcademicTermDetail> newAcademicTermDetailsItr = newAcademicTermDetails.iterator(); newAcademicTermDetailsItr.hasNext(); ) { final AcademicTermDetail academicTermDetail = newAcademicTermDetailsItr.next(); //If ATD exists in both new and existing remove it from both if (existingAcademicTermDetails.remove(academicTermDetail)) { newAcademicTermDetailsItr.remove(); } } final EntityManager entityManager = this.getEntityManager(); //Delete all existing ATDs that were not in the new list for (final AcademicTermDetailImpl existingAcademicTermDetail : existingAcademicTermDetails) { entityManager.remove(existingAcademicTermDetail); } entityManager.flush(); //Add all new ATDs that were not in the existing set for (final AcademicTermDetail newAcademicTermDetail : newAcademicTermDetails) { entityManager.persist(newAcademicTermDetail); } } }