/** * 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.sync; import org.candlepin.model.Branding; import org.candlepin.model.Cdn; import org.candlepin.model.CdnCurator; import org.candlepin.model.CertificateSerial; import org.candlepin.model.CertificateSerialCurator; import org.candlepin.model.Entitlement; import org.candlepin.model.EntitlementCertificate; import org.candlepin.model.Owner; import org.candlepin.model.Product; import org.candlepin.model.ProductCurator; import org.candlepin.model.ProvidedProduct; import org.candlepin.model.SubscriptionsCertificate; import org.candlepin.model.dto.ProductData; import org.candlepin.model.dto.Subscription; import org.candlepin.util.Util; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xnap.commons.i18n.I18n; import java.io.IOException; import java.io.Reader; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * EntitlementImporter - turn an upstream Entitlement into a local subscription */ public class EntitlementImporter { private static Logger log = LoggerFactory.getLogger(EntitlementImporter.class); private CertificateSerialCurator csCurator; private CdnCurator cdnCurator; private I18n i18n; private ProductCurator productCurator; public EntitlementImporter(CertificateSerialCurator csCurator, CdnCurator cdnCurator, I18n i18n, ProductCurator productCurator) { this.csCurator = csCurator; this.cdnCurator = cdnCurator; this.i18n = i18n; this.productCurator = productCurator; } public Subscription importObject(ObjectMapper mapper, Reader reader, Owner owner, Map<String, Product> productsById, ConsumerDto consumer, Meta meta) throws IOException, SyncDataFormatException { Entitlement entitlement = mapper.readValue(reader, Entitlement.class); Subscription subscription = new Subscription(); log.debug("Building subscription for owner: {}", owner); log.debug("Using pool from entitlement: {}", entitlement.getPool()); // Now that we no longer store Subscriptions in the on-site database, we need to // manually give the subscription a downstream ID. Note that this may later be // overwritten by reconciliation code if it determines this Subscription // should replace and existing one. subscription.setId(Util.generateDbUUID()); subscription.setUpstreamPoolId(entitlement.getPool().getId()); subscription.setUpstreamEntitlementId(entitlement.getId()); subscription.setUpstreamConsumerId(consumer.getUuid()); subscription.setOwner(owner); subscription.setStartDate(entitlement.getStartDate()); subscription.setEndDate(entitlement.getEndDate()); subscription.setAccountNumber(entitlement.getPool().getAccountNumber()); subscription.setContractNumber(entitlement.getPool().getContractNumber()); subscription.setOrderNumber(entitlement.getPool().getOrderNumber()); subscription.setQuantity(entitlement.getQuantity().longValue()); for (Branding b : entitlement.getPool().getBranding()) { subscription.getBranding().add(new Branding(b.getProductId(), b.getType(), b.getName())); } String cdnLabel = meta.getCdnLabel(); if (!StringUtils.isBlank(cdnLabel)) { Cdn cdn = cdnCurator.lookupByLabel(cdnLabel); if (cdn != null) { subscription.setCdn(cdn); } } Product product = this.findProduct(productsById, entitlement.getPool().getProductId()); subscription.setProduct(product.toDTO()); // Add any sub product data to the subscription. if (entitlement.getPool().getDerivedProductId() != null) { product = this.findProduct(productsById, entitlement.getPool().getDerivedProductId()); subscription.setDerivedProduct(product.toDTO()); } associateProvidedProducts(productsById, entitlement, subscription); Set<EntitlementCertificate> certs = entitlement.getCertificates(); // subscriptions have one cert int entcnt = 0; for (EntitlementCertificate cert : certs) { ++entcnt; CertificateSerial cs = new CertificateSerial(); cs.setCollected(cert.getSerial().isCollected()); cs.setExpiration(cert.getSerial().getExpiration()); cs.setUpdated(cert.getSerial().getUpdated()); cs.setCreated(cert.getSerial().getCreated()); SubscriptionsCertificate sc = new SubscriptionsCertificate(); sc.setKey(cert.getKey()); sc.setCertAsBytes(cert.getCertAsBytes()); sc.setSerial(cs); subscription.setCertificate(sc); } if (entcnt > 1) { log.error("More than one entitlement cert found for subscription"); } return subscription; } /* * Transfer associations to provided and derived provided products over to the * subscription. * * WARNING: This is a bit tricky due to backward compatibility issues. Prior to * candlepin 2.0, pools serialized a collection of disjoint provided product info, * an object with just a product ID and name, not directly linked to anything in the db. * * In candlepin 2.0 we have referential integrity and links to full product objects, * but we need to maintain both API and import compatibility with old manifests and * servers that may import new manifests. * * To do this, we serialize the providedProductDtos and derivedProvidedProductDtos * collections on pool which keeps the API/manifest JSON identical to what it was * before. On import we load into these transient collections, and here we transfer * to the actual persisted location. */ public void associateProvidedProducts(Map<String, Product> productsById, Entitlement entitlement, Subscription subscription) throws SyncDataFormatException { // Associate main provided products: Set<ProductData> providedProducts = new HashSet<ProductData>(); entitlement.getPool().populateAllTransientProvidedProducts(productCurator); for (ProvidedProduct providedProduct : entitlement.getPool().getProvidedProductDtos()) { Product product = this.findProduct(productsById, providedProduct.getProductId()); providedProducts.add(product.toDTO()); } subscription.setProvidedProducts(providedProducts); // Associate derived provided products: Set<ProductData> derivedProvidedProducts = new HashSet<ProductData>(); for (ProvidedProduct pp : entitlement.getPool().getDerivedProvidedProductDtos()) { Product product = this.findProduct(productsById, pp.getProductId()); derivedProvidedProducts.add(product.toDTO()); } subscription.setDerivedProvidedProducts(derivedProvidedProducts); log.debug("Subscription has {} provided products.", derivedProvidedProducts.size()); log.debug("Subscription has {} derived provided products.", derivedProvidedProducts.size()); } private Product findProduct(Map<String, Product> productsById, String productId) throws SyncDataFormatException { Product product = productsById.get(productId); if (product == null) { throw new SyncDataFormatException(i18n.tr("Unable to find product with ID: {0}", productId)); } return product; } }