/**
* 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.session;
import com.google.common.base.Function;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Root;
import org.apereo.portal.events.LoginEvent;
import org.apereo.portal.events.PortalEvent;
import org.apereo.portal.events.aggr.groups.AggregatedGroupLookupDao;
import org.apereo.portal.events.aggr.groups.AggregatedGroupMapping;
import org.apereo.portal.groups.ICompositeGroupService;
import org.apereo.portal.groups.IEntityGroup;
import org.apereo.portal.groups.IGroupMember;
import org.apereo.portal.jpa.BaseAggrEventsJpaDao;
import org.apereo.portal.jpa.cache.EntityManagerCache;
import org.apereo.portal.security.IPerson;
import org.apereo.portal.utils.cache.CacheKey;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
/**
*/
@Repository("eventSessionDao")
public class JpaEventSessionDao extends BaseAggrEventsJpaDao implements EventSessionDao {
private static final String EVENT_SESSION_CACHE_SOURCE =
JpaEventSessionDao.class.getName() + "_EVENT_SESSION";
private int maxPurgeBatchSize;
private String deleteByEventSessionIdQuery;
private CriteriaQuery<EventSessionImpl> findExpiredEventSessionsQuery;
private CriteriaQuery<Long> countExpiredEventSessionsQuery;
private ParameterExpression<String> eventSessionIdParameter;
private ParameterExpression<DateTime> dateTimeParameter;
private AggregatedGroupLookupDao aggregatedGroupLookupDao;
private ICompositeGroupService compositeGroupService;
private EntityManagerCache entityManagerCache;
@Autowired
@Value("${org.apereo.portal.events.aggr.session.JpaEventSessionDao.maxPurgeBatchSize:100000}")
public void setMaxPurgeBatchSize(int maxPurgeBatchSize) {
this.maxPurgeBatchSize = maxPurgeBatchSize;
}
@Autowired
public void setEntityManagerCache(EntityManagerCache entityManagerCache) {
this.entityManagerCache = entityManagerCache;
}
@Autowired
public void setCompositeGroupService(ICompositeGroupService compositeGroupService) {
this.compositeGroupService = compositeGroupService;
}
@Autowired
public void setAggregatedGroupLookupDao(AggregatedGroupLookupDao aggregatedGroupLookupDao) {
this.aggregatedGroupLookupDao = aggregatedGroupLookupDao;
}
@Override
public void afterPropertiesSet() throws Exception {
this.eventSessionIdParameter =
this.createParameterExpression(String.class, "eventSessionId");
this.dateTimeParameter = this.createParameterExpression(DateTime.class, "dateTime");
this.findExpiredEventSessionsQuery =
this.createCriteriaQuery(
new Function<CriteriaBuilder, CriteriaQuery<EventSessionImpl>>() {
@Override
public CriteriaQuery<EventSessionImpl> apply(CriteriaBuilder cb) {
final CriteriaQuery<EventSessionImpl> criteriaQuery =
cb.createQuery(EventSessionImpl.class);
final Root<EventSessionImpl> root =
criteriaQuery.from(EventSessionImpl.class);
criteriaQuery.select(root);
criteriaQuery.where(
cb.lessThanOrEqualTo(
root.get(EventSessionImpl_.lastAccessed),
dateTimeParameter));
return criteriaQuery;
}
});
this.countExpiredEventSessionsQuery =
this.createCriteriaQuery(
new Function<CriteriaBuilder, CriteriaQuery<Long>>() {
@Override
public CriteriaQuery<Long> apply(CriteriaBuilder cb) {
final CriteriaQuery<Long> criteriaQuery =
cb.createQuery(Long.class);
final Root<EventSessionImpl> root =
criteriaQuery.from(EventSessionImpl.class);
criteriaQuery.select(cb.count(root));
criteriaQuery.where(
cb.lessThanOrEqualTo(
root.get(EventSessionImpl_.lastAccessed),
dateTimeParameter));
return criteriaQuery;
}
});
this.deleteByEventSessionIdQuery =
"DELETE FROM "
+ EventSessionImpl.class.getName()
+ " e "
+ "WHERE e."
+ EventSessionImpl_.eventSessionId.getName()
+ " = :"
+ this.eventSessionIdParameter.getName();
}
@AggrEventsTransactional
@Override
public void storeEventSession(EventSession eventSession) {
this.getEntityManager().persist(eventSession);
}
@AggrEventsTransactional
@Override
public EventSession getEventSession(PortalEvent event) {
final String eventSessionId = event.getEventSessionId();
final CacheKey key = CacheKey.build(EVENT_SESSION_CACHE_SOURCE, eventSessionId);
EventSessionImpl eventSession = this.entityManagerCache.get(PERSISTENCE_UNIT_NAME, key);
if (eventSession != null) {
return eventSession;
}
final NaturalIdQuery<EventSessionImpl> naturalIdQuery =
this.createNaturalIdQuery(EventSessionImpl.class);
naturalIdQuery.using(EventSessionImpl_.eventSessionId, eventSessionId);
eventSession = naturalIdQuery.load();
if (eventSession == null) {
//No event session, somehow we missed the login event. Look at the groups the user is currently a member of
final Set<AggregatedGroupMapping> groupMappings = this.getGroupsForEvent(event);
final DateTime eventDate = event.getTimestampAsDate();
eventSession = new EventSessionImpl(eventSessionId, eventDate, groupMappings);
this.getEntityManager().persist(eventSession);
this.entityManagerCache.put(PERSISTENCE_UNIT_NAME, key, eventSession);
}
return eventSession;
}
@AggrEventsTransactional
@Override
public void deleteEventSession(String eventSessionId) {
final Query query = this.getEntityManager().createQuery(this.deleteByEventSessionIdQuery);
query.setParameter(this.eventSessionIdParameter.getName(), eventSessionId);
query.executeUpdate();
}
private void purgeEventList(int batchSize, DateTime lastAggregatedEventDate) {
final TypedQuery<EventSessionImpl> query =
this.createQuery(this.findExpiredEventSessionsQuery);
query.setParameter(this.dateTimeParameter, lastAggregatedEventDate);
query.setMaxResults(batchSize);
final List<EventSessionImpl> resultList = query.getResultList();
for (final EventSessionImpl eventSession : resultList) {
this.getEntityManager().remove(eventSession);
}
}
@AggrEventsTransactional
@Override
public int purgeEventSessionsBefore(DateTime lastAggregatedEventDate) {
final TypedQuery<Long> countQuery = this.createQuery(this.countExpiredEventSessionsQuery);
countQuery.setParameter(this.dateTimeParameter, lastAggregatedEventDate);
final int totalRows = countQuery.getSingleResult().intValue();
if (totalRows > 0) {
final int numberBatches = totalRows / maxPurgeBatchSize;
for (int i = 0; i <= numberBatches; i++) {
purgeEventList(maxPurgeBatchSize, lastAggregatedEventDate);
}
}
return totalRows;
}
/** Get groups for the event */
protected Set<AggregatedGroupMapping> getGroupsForEvent(PortalEvent event) {
final Set<AggregatedGroupMapping> groupMappings =
new LinkedHashSet<AggregatedGroupMapping>();
if (event instanceof LoginEvent) {
for (final String groupKey : ((LoginEvent) event).getGroups()) {
final AggregatedGroupMapping groupMapping =
this.aggregatedGroupLookupDao.getGroupMapping(groupKey);
if (groupMapping != null) {
groupMappings.add(groupMapping);
}
}
} else {
final String userName = event.getUserName();
final IGroupMember groupMember =
this.compositeGroupService.getGroupMember(userName, IPerson.class);
for (@SuppressWarnings("unchecked")
final Iterator<IEntityGroup> containingGroups =
this.compositeGroupService.findParentGroups(groupMember);
containingGroups.hasNext();
) {
final IEntityGroup group = containingGroups.next();
final AggregatedGroupMapping groupMapping =
this.aggregatedGroupLookupDao.getGroupMapping(
group.getServiceName().toString(), group.getName());
groupMappings.add(groupMapping);
}
}
return groupMappings;
}
}