Java tutorial
/** * Copyright (c) 2009 - 2012 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package org.candlepin.model; import org.candlepin.common.exceptions.BadRequestException; import org.candlepin.common.paging.Page; import org.candlepin.common.paging.PageRequest; import com.google.common.collect.Iterables; import com.google.inject.Inject; import com.google.inject.persist.Transactional; import org.hibernate.Criteria; import org.hibernate.Hibernate; import org.hibernate.ReplicationMode; import org.hibernate.criterion.CriteriaSpecification; import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions; import org.hibernate.sql.JoinType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.Query; /** * EntitlementCurator */ public class EntitlementCurator extends AbstractHibernateCurator<Entitlement> { private static Logger log = LoggerFactory.getLogger(EntitlementCurator.class); private OwnerProductCurator ownerProductCurator; /** * default ctor */ @Inject public EntitlementCurator(OwnerProductCurator ownerProductCurator) { super(Entitlement.class); this.ownerProductCurator = ownerProductCurator; } // TODO: handles addition of new entitlements only atm! /** * @param entitlements entitlements to update * @return updated entitlements. */ @Transactional public Set<Entitlement> bulkUpdate(Set<Entitlement> entitlements) { Set<Entitlement> toReturn = new HashSet<Entitlement>(); for (Entitlement toUpdate : entitlements) { Entitlement found = find(toUpdate.getId()); if (found != null) { toReturn.add(found); continue; } toReturn.add(create(toUpdate)); } return toReturn; } private Criteria createCriteriaFromFilters(EntitlementFilterBuilder filterBuilder) { Criteria criteria = createSecureCriteria(); criteria.createAlias("pool", "p"); // Add the required aliases for the filter builder only if required. if (filterBuilder != null && filterBuilder.hasMatchFilters()) { criteria.createAlias("p.product", "product"); criteria.createAlias("p.providedProducts", "provProd", CriteriaSpecification.LEFT_JOIN); criteria.createAlias("provProd.productContent", "ppcw", CriteriaSpecification.LEFT_JOIN); criteria.createAlias("ppcw.content", "ppContent", CriteriaSpecification.LEFT_JOIN); criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); } // Never show a consumer expired entitlements criteria.add(Restrictions.ge("p.endDate", new Date())); filterBuilder.applyTo(criteria); return criteria; } /** * This must return a sorted list in order to avoid deadlocks * * @param consumer * @return list of entitlements belonging to the consumer, ordered by pool id */ @SuppressWarnings("unchecked") public List<Entitlement> listByConsumer(Consumer consumer) { return listByConsumer(consumer, new EntitlementFilterBuilder()); } @SuppressWarnings("unchecked") public List<Entitlement> listByConsumer(Consumer consumer, EntitlementFilterBuilder filters) { Criteria criteria = createCriteriaFromFilters(filters); criteria.add(Restrictions.eq("consumer", consumer)); criteria.addOrder(Order.asc("p.id")); return criteria.list(); } @SuppressWarnings("unchecked") public List<Entitlement> listByConsumerAndPoolId(Consumer consumer, String poolId) { Criteria query = currentSession().createCriteria(Entitlement.class).add(Restrictions.eq("pool.id", poolId)); query.add(Restrictions.eq("consumer", consumer)); return listByCriteria(query); } public Page<List<Entitlement>> listByConsumer(Consumer consumer, String productId, EntitlementFilterBuilder filters, PageRequest pageRequest) { return listFilteredPages(consumer, "consumer", productId, filters, pageRequest); } public Page<List<Entitlement>> listByOwner(Owner owner, String productId, EntitlementFilterBuilder filters, PageRequest pageRequest) { return listFilteredPages(owner, "owner", productId, filters, pageRequest); } public Page<List<Entitlement>> listAll(EntitlementFilterBuilder filters, PageRequest pageRequest) { return listFilteredPages(null, null, null, filters, pageRequest); } private Page<List<Entitlement>> listFilteredPages(AbstractHibernateObject object, String objectType, String productId, EntitlementFilterBuilder filters, PageRequest pageRequest) { Page<List<Entitlement>> entitlementsPage; Owner owner = null; if (object != null) { owner = (object instanceof Owner) ? (Owner) object : ((Consumer) object).getOwner(); } // No need to add filters when matching by product. if (object != null && productId != null) { Product p = this.ownerProductCurator.getProductById(owner, productId); if (p == null) { throw new BadRequestException(i18n.tr("Product with ID ''{0}'' could not be found.", productId)); } entitlementsPage = listByProduct(object, objectType, productId, pageRequest); } else { // Build up any provided entitlement filters from query params. Criteria criteria = createCriteriaFromFilters(filters); if (object != null) { criteria.add(Restrictions.eq(objectType, object)); } entitlementsPage = listByCriteria(criteria, pageRequest); } return entitlementsPage; } public List<Entitlement> listByOwner(Owner owner) { Criteria query = currentSession().createCriteria(Entitlement.class).add(Restrictions.eq("owner", owner)); return listByCriteria(query); } public List<Entitlement> listByEnvironment(Environment environment) { Criteria criteria = currentSession().createCriteria(Entitlement.class).createCriteria("consumer") .add(Restrictions.eq("environment", environment)); return criteria.list(); } /** * List entitlements for a consumer which are valid for a specific date. * * @param consumer Consumer to list entitlements for. * @param activeOn The date we want to see entitlements which are active on. * @return List of entitlements. */ public List<Entitlement> listByConsumerAndDate(Consumer consumer, Date activeOn) { /* * Essentially the opposite of the above query which searches for entitlement * overlap with a "modifying" entitlement being granted. This query is used to * search for modifying entitlements which overlap with a regular entitlement * being granted. As such the logic is basically reversed. * */ Criteria criteria = currentSession().createCriteria(Entitlement.class) .add(Restrictions.eq("consumer", consumer)).createCriteria("pool") .add(Restrictions.le("startDate", activeOn)).add(Restrictions.ge("endDate", activeOn)); List<Entitlement> entitlements = criteria.list(); return entitlements; } /** * Lists dirty entitlements for the given consumer. If the consumer does not have any dirty * entitlements, this method returns an empty collection. * * @param consumer * The consumer for which to find dirty entitlements * * @return * a collection of dirty entitlements for the given consumer */ public List<Entitlement> listDirty(Consumer consumer) { Criteria criteria = this.currentSession().createCriteria(Entitlement.class) .add(Restrictions.eq("consumer", consumer)).add(Restrictions.eq("dirty", true)); return criteria.list(); } /** * List all entitled product IDs from entitlements which overlap the given date range. * * i.e. given start date must be within the entitlements start/end dates, or * the given end date must be within the entitlements start/end dates, * or the given start date must be before the entitlement *and* the given end date * must be after entitlement. (i.e. we are looking for *any* overlap) * * @param c * @param startDate * @param endDate * @return entitled product IDs */ public Set<String> listEntitledProductIds(Consumer c, Date startDate, Date endDate) { // FIXME Either address the TODO below, or move this method out of the curator. // TODO: Swap this to a db query if we're worried about memory: Set<String> entitledProductIds = new HashSet<String>(); for (Entitlement e : c.getEntitlements()) { Pool p = e.getPool(); if (!poolOverlapsRange(p, startDate, endDate)) { // Skip this entitlement: continue; } entitledProductIds.add(p.getProduct().getId()); for (Product pp : p.getProvidedProducts()) { entitledProductIds.add(pp.getId()); } // A distributor should technically be entitled to derived products and // will need to be able to sync content downstream. if (c.getType().isManifest() && p.getDerivedProduct() != null) { entitledProductIds.add(p.getDerivedProduct().getId()); if (p.getDerivedProvidedProducts() != null) { for (Product dpp : p.getDerivedProvidedProducts()) { entitledProductIds.add(dpp.getId()); } } } } return entitledProductIds; } private boolean poolOverlapsRange(Pool p, Date startDate, Date endDate) { Date poolStart = p.getStartDate(); Date poolEnd = p.getEndDate(); // If pool start is within the range we're looking for: if (poolStart.compareTo(startDate) >= 0 && poolStart.compareTo(endDate) <= 0) { return true; } // If pool end is within the range we're looking for: if (poolEnd.compareTo(startDate) >= 0 && poolEnd.compareTo(endDate) <= 0) { return true; } // If pool completely encapsulates the range we're looking for: if (poolStart.compareTo(startDate) <= 0 && poolEnd.compareTo(endDate) >= 0) { return true; } return false; } /** * A version of list Modifying that finds Entitlements that modify * input entitlements. * When dealing with large amount of entitlements for which it is necessary * to determine their modifier products. * @param entitlement * @return Entitlements that are being modified by the input entitlements */ public Collection<String> batchListModifying(Iterable<Entitlement> entitlements) { List<String> eids = new LinkedList<String>(); if (entitlements != null && entitlements.iterator().hasNext()) { String hql = "SELECT DISTINCT eOut.id" + " FROM Entitlement eOut" + " JOIN eOut.pool outPool" + " JOIN outPool.providedProducts outProvided" + " JOIN outProvided.productContent outProvContent" + " JOIN outProvContent.content outContent" + " JOIN outContent.modifiedProductIds outModProdId" + " WHERE" + " outPool.endDate >= current_date AND" + " eOut NOT IN (:ein) AND" + " EXISTS (" + " SELECT eIn" + " FROM Entitlement eIn" + " JOIN eIn.consumer inConsumer" + " JOIN eIn.pool inPool" + " JOIN inPool.product inMktProd" + " LEFT JOIN inPool.providedProducts inProvidedProd" + " WHERE eIn in (:ein) AND inConsumer = eOut.consumer AND" + " inPool.endDate >= outPool.startDate AND" + " inPool.startDate <= outPool.endDate AND" + " (inProvidedProd.id = outModProdId OR inMktProd.id = outModProdId)" + " )"; Query query = this.getEntityManager().createQuery(hql); Iterable<List<Entitlement>> blocks = Iterables.partition(entitlements, AbstractHibernateCurator.IN_OPERATOR_BLOCK_SIZE); for (List<Entitlement> block : blocks) { eids.addAll(query.setParameter("ein", block).getResultList()); } } return eids; } public Collection<String> listModifying(Entitlement entitlement) { return batchListModifying(java.util.Arrays.asList(entitlement)); } public Collection<String> listModifying(Collection entitlements) { return batchListModifying(entitlements); } public Map<Consumer, List<Entitlement>> getDistinctConsumers(List<Entitlement> entsToRevoke) { Map<Consumer, List<Entitlement>> result = new HashMap<Consumer, List<Entitlement>>(); for (Entitlement ent : entsToRevoke) { List<Entitlement> ents = result.get(ent.getConsumer()); if (ents == null) { ents = new ArrayList<Entitlement>(); result.put(ent.getConsumer(), ents); } ents.add(ent); } return result; } public Page<List<Entitlement>> listByConsumerAndProduct(Consumer consumer, String productId, PageRequest pageRequest) { return listByProduct(consumer, "consumer", productId, pageRequest); } @Transactional private Page<List<Entitlement>> listByProduct(AbstractHibernateObject object, String objectType, String productId, PageRequest pageRequest) { Criteria query = createSecureCriteria().add(Restrictions.eq(objectType, object)).createAlias("pool", "p") .createAlias("p.product", "prod") .createAlias("p.providedProducts", "pp", CriteriaSpecification.LEFT_JOIN) // Never show a consumer expired entitlements .add(Restrictions.ge("p.endDate", new Date())) .add(Restrictions.or(Restrictions.eq("prod.id", productId), Restrictions.eq("pp.id", productId))); Page<List<Entitlement>> page = listByCriteria(query, pageRequest); return page; } /** * Deletes the given entitlement. * * @param entity * The entitlement entity to delete */ @Transactional public void delete(Entitlement entity) { Entitlement toDelete = find(entity.getId()); if (toDelete != null) { this.deleteImpl(toDelete); // Maintain runtime consistency. entity.getCertificates().clear(); entity.getConsumer().getEntitlements().remove(entity); entity.getPool().getEntitlements().remove(entity); } } /** * Deletes the given collection of entitlements. * <p/></p> * Note: Unlike the standard delete method, this method does not perform a lookup on an entity * before deleting it. * * @param entitlements * The collection of entitlement entities to delete */ public void batchDelete(Collection<Entitlement> entitlements) { for (Entitlement entitlement : entitlements) { this.deleteImpl(entitlement); // Maintain runtime consistency. entitlement.getCertificates().clear(); if (Hibernate.isInitialized(entitlement.getConsumer().getEntitlements())) { entitlement.getConsumer().getEntitlements().remove(entitlement); } if (Hibernate.isInitialized(entitlement.getPool().getEntitlements())) { entitlement.getPool().getEntitlements().remove(entitlement); } } } private void deleteImpl(Entitlement entity) { log.debug("Deleting entitlement: {}", entity); EntityManager entityManager = this.getEntityManager(); if (entity.getCertificates() != null) { log.debug("certs.size = {}", entity.getCertificates().size()); for (EntitlementCertificate cert : entity.getCertificates()) { entityManager.remove(cert); } } entityManager.remove(entity); } @Transactional public Entitlement findByCertificateSerial(Long serial) { return (Entitlement) currentSession().createCriteria(Entitlement.class).createCriteria("certificates") .add(Restrictions.eq("serial.id", serial)).uniqueResult(); } @Transactional public Entitlement replicate(Entitlement ent) { for (EntitlementCertificate ec : ent.getCertificates()) { ec.setEntitlement(ent); CertificateSerial cs = ec.getSerial(); if (cs != null) { this.currentSession().replicate(cs, ReplicationMode.EXCEPTION); } } this.currentSession().replicate(ent, ReplicationMode.EXCEPTION); return ent; } /** * Find the entitlements for the given consumer that are part of the specified stack. * * @param consumer the consumer * @param stackId the ID of the stack * @return the list of entitlements for the consumer that are in the stack. */ @SuppressWarnings("unchecked") public List<Entitlement> findByStackId(Consumer consumer, String stackId) { return findByStackIds(consumer, Arrays.asList(stackId)); } /** * Find the entitlements for the given consumer that are part of the * specified stacks. * * @param consumer the consumer * @param stackIds the IDs of the stacks * @return the list of entitlements for the consumer that are in the stack. */ @SuppressWarnings("unchecked") public List<Entitlement> findByStackIds(Consumer consumer, Collection stackIds) { Criteria activeNowQuery = currentSession().createCriteria(Entitlement.class) .add(Restrictions.eq("consumer", consumer)).createAlias("pool", "ent_pool") .createAlias("ent_pool.product", "product").createAlias("product.attributes", "attrs") .add(Restrictions.eq("attrs.name", "stacking_id")) .add(unboundedInCriterion("attrs.value", stackIds)) .add(Restrictions.isNull("ent_pool.sourceEntitlement")) .createAlias("ent_pool.sourceStack", "ss", JoinType.LEFT_OUTER_JOIN) .add(Restrictions.isNull("ss.id")); return activeNowQuery.list(); } @SuppressWarnings("unchecked") public List<Entitlement> findByPoolAttribute(Consumer consumer, String attributeName, String value) { Criteria criteria = currentSession().createCriteria(Entitlement.class).createAlias("pool", "ent_pool") .createAlias("ent_pool.attributes", "attrs").add(Restrictions.eq("attrs.name", attributeName)) .add(Restrictions.eq("attrs.value", value)); if (consumer != null) { criteria.add(Restrictions.eq("consumer", consumer)); } return criteria.list(); } @SuppressWarnings("unchecked") public List<Entitlement> findByPoolAttribute(String attributeName, String value) { return findByPoolAttribute(null, attributeName, value); } /** * For a given stack, find the eldest active entitlement with a subscription ID. * This is used to look up the upstream subscription certificate to use to talk to * the CDN. * * @param consumer the consumer * @param stackId the ID of the stack * @return the eldest active entitlement with a subscription ID, or null if none can * be found. */ public Entitlement findUpstreamEntitlementForStack(Consumer consumer, String stackId) { Date currentDate = new Date(); Criteria activeNowQuery = currentSession().createCriteria(Entitlement.class) .add(Restrictions.eq("consumer", consumer)).createAlias("pool", "ent_pool") .createAlias("ent_pool.product", "product").createAlias("product.attributes", "attrs") .add(Restrictions.le("ent_pool.startDate", currentDate)) .add(Restrictions.ge("ent_pool.endDate", currentDate)) .add(Restrictions.eq("attrs.name", "stacking_id")).add(Restrictions.eq("attrs.value", stackId)) .add(Restrictions.isNull("ent_pool.sourceEntitlement")) .createAlias("ent_pool.sourceSubscription", "sourceSub").add(Restrictions.isNotNull("sourceSub.id")) .addOrder(Order.asc("created")) // eldest entitlement .setMaxResults(1); return (Entitlement) activeNowQuery.uniqueResult(); } /** * Marks the given entitlements as dirty; forcing a regeneration the next time it is requested. * * @param entitlementIds * A collection of IDs of the entitlements to mark dirty * * @return * The number of certificates updated */ @Transactional public int markEntitlementsDirty(Iterable<String> entitlementIds) { int count = 0; if (entitlementIds != null && entitlementIds.iterator().hasNext()) { Iterable<List<String>> blocks = Iterables.partition(entitlementIds, AbstractHibernateCurator.IN_OPERATOR_BLOCK_SIZE); String hql = "UPDATE Entitlement SET dirty = true WHERE id IN (:entIds)"; Query query = this.getEntityManager().createQuery(hql); for (List<String> block : blocks) { count += query.setParameter("entIds", block).executeUpdate(); } } return count; } }