/** * 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; } }