/**
* 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.controller;
import org.candlepin.audit.EventFactory;
import org.candlepin.audit.EventSink;
import org.candlepin.model.Consumer;
import org.candlepin.model.Entitlement;
import org.candlepin.model.EntitlementCertificate;
import org.candlepin.model.EntitlementCertificateCurator;
import org.candlepin.model.EntitlementCurator;
import org.candlepin.model.Environment;
import org.candlepin.model.Owner;
import org.candlepin.model.Pool;
import org.candlepin.model.PoolCurator;
import org.candlepin.model.Product;
import org.candlepin.model.ProductCurator;
import org.candlepin.service.EntitlementCertServiceAdapter;
import org.candlepin.util.CertificateSizeException;
import org.candlepin.version.CertVersionConflictException;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
/**
* The EntitlementCertificateGenerator class provides utility methods for regenerating the
* certificates for entitlements associated with various model objects.
* <p></p>
* It should be noted that due to the amount of graph traversal that can occur when performing these
* operations, the number of calls to methods provided by this class should be minimized. The majority
* are incredibly expensive and, in the case of immediate regeneration, can hold database locks for the
* duration. Usage of these methods should be carefully evaluated, and bulk operations should be
* preferred to singular ones.
*/
public class EntitlementCertificateGenerator {
private static Logger log = LoggerFactory.getLogger(EntitlementCertificateGenerator.class);
private EntitlementCertificateCurator entitlementCertificateCurator;
private EntitlementCertServiceAdapter entCertServiceAdapter;
private EntitlementCurator entitlementCurator;
private PoolCurator poolCurator;
private ProductCurator productCurator;
private EventSink eventSink;
private EventFactory eventFactory;
@Inject
public EntitlementCertificateGenerator(EntitlementCertificateCurator entitlementCertificateCurator,
EntitlementCertServiceAdapter entCertServiceAdapter, EntitlementCurator entitlementCurator,
PoolCurator poolCurator, EventSink eventSink, EventFactory eventFactory,
ProductCurator productCurator) {
this.entitlementCertificateCurator = entitlementCertificateCurator;
this.entCertServiceAdapter = entCertServiceAdapter;
this.entitlementCurator = entitlementCurator;
this.poolCurator = poolCurator;
this.eventSink = eventSink;
this.eventFactory = eventFactory;
this.productCurator = productCurator;
}
/**
* Generates new entitlement certificates for the given consumer using the provided products and
* entitlements.
*
* @param consumer
* The consumer for which to generate new entitlement certificates
*
* @param products
* A mapping of products, indexed by pool ID, to use when generating certificates
*
* @param entitlements
* A mapping of entitlements, indexed by pool ID, to use when generating certificates
*
* @return
* A map of generated entitlement certificates, indexed by pool ID
*/
@Transactional
public Map<String, EntitlementCertificate> generateEntitlementCertificates(Consumer consumer,
Map<String, Product> products, Map<String, Entitlement> entitlements) {
try {
return this.entCertServiceAdapter.generateEntitlementCerts(consumer, entitlements, products);
}
catch (CertVersionConflictException cvce) {
throw cvce;
}
catch (CertificateSizeException cse) {
throw cse;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
/**
* Generates a new entitlement certificate for the given entitlement and pool.
*
* @param pool
* The pool for which to generate an entitlement certificate
*
* @param entitlement
* The entitlement to use when generating the certificate
*
* @return
* The newly generate entitlement certificate
*/
@Transactional
public EntitlementCertificate generateEntitlementCertificate(Pool pool, Entitlement entitlement) {
Map<String, Product> products = new HashMap<String, Product>();
Map<String, Entitlement> entitlements = new HashMap<String, Entitlement>();
products.put(pool.getId(), pool.getProduct());
entitlements.put(pool.getId(), entitlement);
return this.generateEntitlementCertificates(entitlement.getConsumer(), products,
entitlements).get(pool.getId());
}
/**
* Regenerates the certificates for the specified entitlement.
*
* @param entitlement
* The entitlement for which to regenerate certificates
*
* @param lazy
* Whether or not to generate the certificate immediately, or mark it dirty and allow it to be
* regenerated on-demand
*/
@Transactional
public void regenerateCertificatesOf(Entitlement entitlement, boolean lazy) {
if (lazy) {
log.info("Marking certificates dirty for entitlement: {}", entitlement);
entitlement.setDirty(true);
return;
}
log.debug("Revoking entitlementCertificates of: {}", entitlement);
Entitlement tempE = new Entitlement();
tempE.setCertificates(entitlement.getCertificates());
entitlement.setCertificates(null);
// below call creates new certificates and saves it to the backend.
try {
EntitlementCertificate generated = this.generateEntitlementCertificate(
entitlement.getPool(), entitlement
);
entitlement.setDirty(false);
this.entitlementCurator.merge(entitlement);
for (EntitlementCertificate ec : tempE.getCertificates()) {
log.debug("Deleting entitlementCertificate: #{}", ec.getId());
this.entitlementCertificateCurator.delete(ec);
}
// send entitlement changed event.
this.eventSink.queueEvent(this.eventFactory.entitlementChanged(entitlement));
log.debug("Generated entitlementCertificate: #{}", generated.getId());
}
catch (CertificateSizeException cse) {
entitlement.setCertificates(tempE.getCertificates());
log.warn("The certificate cannot be regenerated at this time: {}", cse.getMessage());
}
}
/**
* Regenerates the certificates for the specified entitlements. This method is a utility method
* which individually regenerates certificates for each entitlement in the provided collection.
*
* @param entitlements
* An iterable collection of entitlements for which to regenerate certificates
*
* @param lazy
* Whether or not to generate the certificate immediately, or mark it dirty and allow it to be
* regenerated on-demand
*/
@Transactional
public void regenerateCertificatesOf(Iterable<Entitlement> entitlements, boolean lazy) {
for (Entitlement entitlement : entitlements) {
this.regenerateCertificatesOf(entitlement, lazy);
}
}
/**
* Regenerates the certificates for the specified entitlements. This method is a utility method
* which individually regenerates certificates for each entitlement in the provided collection.
*
* @param entitlementIds
* An iterable collection of entitlement IDs for which to regenerate certificates
*
* @param lazy
* Whether or not to generate the certificate immediately, or mark it dirty and allow it to be
* regenerated on-demand
*/
@Transactional
public void regenerateCertificatesByEntitlementIds(Iterable<String> entitlementIds, boolean lazy) {
// If we're regenerating these lazily, we can avoid loading all of them by just updating the
// DB directly.
if (lazy) {
this.entitlementCurator.markEntitlementsDirty(entitlementIds);
}
else {
for (String entitlementId : entitlementIds) {
Entitlement entitlement = entitlementCurator.find(entitlementId);
if (entitlement == null) {
// If it has been deleted, that's fine; one less to regenerate
log.info("Unable to load entitlement for regeneration: {}", entitlementId);
continue;
}
this.regenerateCertificatesOf(entitlement, false);
}
}
}
/**
* Regenerates all known certificates for the given consumer.
*
* @param consumer
* The consumer for which to regenerate entitlement certificates
*
* @param lazy
* Whether or not to generate the certificate immediately, or mark it dirty and allow it to be
* regenerated on-demand
*/
@Transactional
public void regenerateCertificatesOf(Consumer consumer, boolean lazy) {
log.info(
"Regenerating #{}, entitlement certificates for consumer: {}",
consumer.getEntitlements().size(), consumer
);
// TODO - Assumes only 1 entitlement certificate exists per entitlement
this.regenerateCertificatesOf(consumer.getEntitlements(), lazy);
}
/**
* Regenerates the certificates for the specified contents in a given environment.
*
* @param environment
* The environment in which the entitlements should be regenerated
*
* @param contentIds
* A collection of content Ids for which to regenerate entitlement certificates
*
* @param lazy
* Whether or not to generate the certificate immediately, or mark them dirty and allow them to
* be regenerated on-demand
*/
@Transactional
public void regenerateCertificatesOf(Environment environment, Collection<String> contentIds,
boolean lazy) {
log.info("Regenerating relevant certificates in environment: {}", environment);
Set<Entitlement> entsToRegen = new HashSet<Entitlement>();
entLoop: for (Entitlement entitlement : this.entitlementCurator.listByEnvironment(environment)) {
// Impl note:
// Since the entitlements came from the DB, we should be safe to traverse the graph as
// necessary without any sanity checks (so long as our model's restrictions aren't
// broken).
for (String contentId : contentIds) {
if (entitlement.getPool().getProduct().hasContent(contentId)) {
entsToRegen.add(entitlement);
continue entLoop;
}
Set<Product> providedProducts = productCurator
.getPoolProvidedProductsCached(entitlement.getPool().getId());
for (Product provided : providedProducts) {
if (provided.hasContent(contentId)) {
entsToRegen.add(entitlement);
continue entLoop;
}
}
}
}
log.info("Found {} certificates to regenerate.", entsToRegen.size());
this.regenerateCertificatesOf(entsToRegen, lazy);
}
/**
* Regenerates the entitlement certificates of all entitlements for pools using the specified
* product.
*
* @param owner
* The owner for which to regenerate entitlement certificates
*
* @param productId
* The Red Hat ID of the product for which to regenerate certificates
*
* @param lazy
* Whether or not to generate the certificate immediately, or mark it dirty and allow it to be
* regenerated on-demand
*/
@Transactional
public void regenerateCertificatesOf(Owner owner, String productId, boolean lazy) {
List<Pool> pools = this.poolCurator.listAvailableEntitlementPools(
null, owner, productId, new Date()
);
for (Pool pool : pools) {
this.regenerateCertificatesOf(pool.getEntitlements(), lazy);
}
}
/**
* Regenerates the entitlement certificates of all entitlements for pools using the specified
* product.
*
* @param owner
* The owner for which to regenerate entitlement certificates
*
* @param product
* The product for which to regenerate certificates
*
* @param lazy
* Whether or not to generate the certificate immediately, or mark it dirty and allow it to be
* regenerated on-demand
*/
@Transactional
public void regenerateCertificatesOf(Owner owner, Product product, boolean lazy) {
this.regenerateCertificatesOf(owner, product.getId(), lazy);
}
/**
* Regenerates the entitlement certificates for all pools using any of the the specified
* product(s), effective for the given owners.
*
* @param owners
* A collection of owners for which the certificates should be generated. Pools using the given
* products but not owned by an owner within this collection will not have their certificates
* regenerated.
*
* @param products
* A collection of products for which to regenerate affected certificates
*
* @param lazy
* Whether or not to generate the certificate immediately, or mark it dirty and allow it to be
* regenerated on-demand
*/
@Transactional
public void regenerateCertificatesOf(Collection<Owner> owners, Collection<Product> products,
boolean lazy) {
List<Pool> pools = new LinkedList<Pool>();
Set<String> productIds = new HashSet<String>();
Date now = new Date();
for (Product product : products) {
productIds.add(product.getId());
}
// TODO: This is a very expensive operation. Update pool curator with something to let us
// do this without hitting the DB several times over.
for (Owner owner : owners) {
if (lazy) {
poolCurator.markCertificatesDirtyForPoolsWithProducts(owner, productIds);
}
else {
pools.addAll(
this.poolCurator.listAvailableEntitlementPools(null, owner, productIds, now)
);
}
}
for (Pool pool : pools) {
this.regenerateCertificatesOf(pool.getEntitlements(), lazy);
}
}
}