/** * 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.jackson.HateoasInclude; import org.candlepin.jackson.CandlepinAttributeDeserializer; import org.candlepin.jackson.CandlepinLegacyAttributeSerializer; import org.candlepin.util.DateSource; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.annotation.JsonFilter; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.lang.StringUtils; import org.hibernate.annotations.BatchSize; import org.hibernate.annotations.Cascade; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollectionOption; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.MapKeyColumn; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.Table; import javax.persistence.Transient; import javax.persistence.Version; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; /** * Represents a pool of products eligible to be consumed (entitled). * For every Product there will be a corresponding Pool. */ @XmlRootElement(name = "pool") @XmlAccessorType(XmlAccessType.PROPERTY) @Entity @Table(name = Pool.DB_TABLE) @JsonFilter("PoolFilter") public class Pool extends AbstractHibernateObject implements Persisted, Owned, Named, Comparable<Pool>, Eventful { /** Name of the table backing this object in the database */ public static final String DB_TABLE = "cp_pool"; /** * Common pool attributes */ public static final class Attributes { /** Attribute used to determine whether or not the pool is derived from the use of an entitlement */ public static final String DERIVED_POOL = "pool_derived"; /** Attribute used to determine whether or not the pool was created for a development entitlement */ public static final String DEVELOPMENT_POOL = "dev_pool"; /** Attribute used to specify consumer types allowed to consume this pool */ public static final String ENABLED_CONSUMER_TYPES = "enabled_consumer_types"; /** Attribute used to identify multi-entitlement enabled pools. */ public static final String MULTI_ENTITLEMENT = "multi-entitlement"; /** Attribute specifying the product family of a given pool */ public static final String PRODUCT_FAMILY = "product_family"; /** Attribute for specifying the pool is only available to physical systems */ public static final String PHYSICAL_ONLY = "physical_only"; /** Attribute used to determine which specific consumer the pool was created for */ public static final String REQUIRES_CONSUMER = "requires_consumer"; /** Attribute used to determine which specific consumer type the pool was created for */ public static final String REQUIRES_CONSUMER_TYPE = "requires_consumer_type"; /** Attribute used to determine which specific host the pool was created for */ public static final String REQUIRES_HOST = "requires_host"; /** Attribute used to determine if a pool was created by sharing */ public static final String SHARE = "share_derived"; /** Attribute for specifying the source pool from which a derived pool originates */ public static final String SOURCE_POOL_ID = "source_pool_id"; /** Attribute for specifying the pool is only available to guests */ public static final String VIRT_ONLY = "virt_only"; /** Attribute used to identify unmapped guest pools. Pool must also be a derived pool */ public static final String UNMAPPED_GUESTS_ONLY = "unmapped_guests_only"; } /** * PoolType * * Pools can be of several major types which can radically alter how they behave. * * NORMAL - A regular pool. Usually created 1-1 with a subscription. * * ENTITLEMENT_DERIVED - A pool created as the result of a consumer's use of an * entitlement. Will be cleaned up when the source entitlement is revoked. * * STACK_DERIVED - A pool created as a result of the consumer's use of a stack of * entitlements. Will be cleaned up when the last entitlement in the stack is revoked. * This type of pool can have certain fields change as a result of adding or removing * entitlements to the stack. * * BONUS - A virt-only pool created only in hosted environments when a subscription * has a virt_limit attribute but no host_limited attribute. * * UNMAPPED_GUEST - TODO * * DEVELOPMENT = TODO */ public enum PoolType { NORMAL, ENTITLEMENT_DERIVED, STACK_DERIVED, SHARE_DERIVED, BONUS, UNMAPPED_GUEST, DEVELOPMENT; /** * Checks if this type represents a derived pool type * * @return * True if this PoolType instance represents a derived pool type; false otherwise */ public boolean isDerivedType() { switch (this) { case ENTITLEMENT_DERIVED: case STACK_DERIVED: case SHARE_DERIVED: return true; default: return false; } } } /** * PoolComplianceType * * Indicates how a pool can be used */ public enum PoolComplianceType { UNKNOWN("Other"), STANDARD("Standard"), INSTANCE_BASED("Instance Based"), STACKABLE("Stackable"), UNIQUE_STACKABLE("Stackable only with other subscriptions"), MULTI_ENTITLEMENT("Multi-Entitleable"); private final String description; PoolComplianceType(String description) { if (description == null || description.length() < 1) { throw new IllegalArgumentException("description is null or empty"); } this.description = description; } public String getDescription() { return this.description; } } @Id @GeneratedValue(generator = "system-uuid") @GenericGenerator(name = "system-uuid", strategy = "uuid") @Column(length = 32) @NotNull private String id; @Enumerated(EnumType.STRING) @NotNull private PoolType type; @ManyToOne @JoinColumn(nullable = false) @NotNull private Owner owner; private Boolean activeSubscription; /** Indicates this pool was created as a result of granting an entitlement. * Allows us to know that we need to clean this pool up if that entitlement * if ever revoked. */ @ManyToOne @JoinColumn(nullable = true) private Entitlement sourceEntitlement; /** * Signifies that this pool is a derived pool linked to this stack (only one * sub pool per stack allowed) */ @OneToOne(mappedBy = "derivedPool", targetEntity = SourceStack.class) @Cascade({org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN}) @XmlTransient private SourceStack sourceStack; /** * Signifies that this pool was created from this subscription (only one * pool per subscription id/subkey is allowed) */ @OneToOne(mappedBy = "pool", targetEntity = SourceSubscription.class) @Cascade({org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN}) @XmlTransient private SourceSubscription sourceSubscription; @Column(nullable = false) @NotNull private Long quantity; @Column(nullable = false) @NotNull private Date startDate; @Column(nullable = false) @NotNull private Date endDate; /* * After Jackson version is upgraded: * @JsonProperty(access = Access.WRITE_ONLY) */ @ManyToOne @JoinColumn(name = "product_uuid", nullable = false) @NotNull private Product product; /* * After Jackson version is upgraded: * @JsonProperty(access = Access.WRITE_ONLY) */ @ManyToOne @JoinColumn(name = "derived_product_uuid") private Product derivedProduct; @ManyToMany @JoinTable( name = "cp2_pool_provided_products", joinColumns = {@JoinColumn(name = "pool_id", insertable = false, updatable = false)}, inverseJoinColumns = {@JoinColumn(name = "product_uuid")}) @BatchSize(size = 1000) private Set<Product> providedProducts; @ManyToMany @JoinTable( name = "cp2_pool_derprov_products", joinColumns = {@JoinColumn(name = "pool_id", insertable = false, updatable = false)}, inverseJoinColumns = {@JoinColumn(name = "product_uuid")}) @BatchSize(size = 1000) private Set<Product> derivedProvidedProducts; @ElementCollection @BatchSize(size = 1000) @CollectionTable(name = "cp_pool_attribute", joinColumns = @JoinColumn(name = "pool_id")) @MapKeyColumn(name = "name") @Column(name = "value") @Cascade({ org.hibernate.annotations.CascadeType.ALL }) @Fetch(FetchMode.SUBSELECT) @JsonSerialize(using = CandlepinLegacyAttributeSerializer.class) @JsonDeserialize(using = CandlepinAttributeDeserializer.class) private Map<String, String> attributes; @OneToMany(mappedBy = "pool") @LazyCollection(LazyCollectionOption.EXTRA) private Set<Entitlement> entitlements; @Size(max = 255) private String restrictedToUsername; @Size(max = 255) private String contractNumber; @Size(max = 255) private String accountNumber; @Size(max = 255) private String orderNumber; @Column(name = "quantity_consumed") @NotNull private Long consumed; @Column(name = "quantity_exported") @NotNull private Long exported; @Column(name = "quantity_shared") @NotNull @Min(0) private Long shared; @OneToMany @JoinTable(name = "cp_pool_branding", joinColumns = @JoinColumn(name = "pool_id"), inverseJoinColumns = @JoinColumn(name = "branding_id")) @Cascade({org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN}) @BatchSize(size = 1000) private Set<Branding> branding; @Version private int version; // Impl note: // These properties are only used as temporary stores to hold information that's only present // in the pool JSON due to the product itself not being serialized with it. These will be // ignored if a product or derived product object is present. @Transient private String importedProductId; @Transient private String importedDerivedProductId; @Transient private Set<ProvidedProduct> providedProductDtos; @Transient private Set<ProvidedProduct> derivedProvidedProductDtos; /** * Transient property that holds derived provided products from database. It is * populated before serialization happens. */ @Transient private Set<ProvidedProduct> derivedProvidedProductDtosCached; @Transient @JsonSerialize(using = CandlepinLegacyAttributeSerializer.class) @JsonDeserialize(using = CandlepinAttributeDeserializer.class) @JsonProperty("productAttributes") private Map<String, String> importedProductAttributes; @Transient @JsonSerialize(using = CandlepinLegacyAttributeSerializer.class) @JsonDeserialize(using = CandlepinAttributeDeserializer.class) @JsonProperty("derivedProductAttributes") private Map<String, String> importedDerivedProductAttributes; // End legacy manifest/pool JSON properties @Transient private Map<String, String> calculatedAttributes; @Transient private boolean markedForDelete = false; @Column(name = "upstream_pool_id") @Size(max = 255) private String upstreamPoolId; @Column(name = "upstream_entitlement_id") @Size(max = 37) private String upstreamEntitlementId; @Column(name = "upstream_consumer_id") @Size(max = 255) private String upstreamConsumerId; @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "certificate_id") private SubscriptionsCertificate cert; @OneToOne @JoinColumn(name = "cdn_id") @JsonIgnore private Cdn cdn; public Pool() { this.activeSubscription = Boolean.TRUE; this.providedProducts = new HashSet<Product>(); this.derivedProvidedProducts = new HashSet<Product>(); this.attributes = new HashMap<String, String>(); this.branding = new HashSet<Branding>(); this.entitlements = new HashSet<Entitlement>(); // TODO: // This set of properties is used entirely to deal with a strange case that occurs during // while using entitlements kicked back from the rules as JSON. Since the JSON for a pool // does not encode the product object itself, we have to temporarily store product // information to return if, and only if, we do not have an authoritative product object. // These values can be set via setters, but will be ignored if a product is present. this.importedProductId = null; this.importedProductAttributes = null; this.importedDerivedProductId = null; this.importedDerivedProductAttributes = null; this.providedProductDtos = null; this.derivedProvidedProductDtos = null; this.derivedProvidedProductDtosCached = null; this.markedForDelete = false; this.setExported(0L); this.setConsumed(0L); this.setShared(0L); } public Pool(Owner ownerIn, Product product, Collection<Product> providedProducts, Long quantityIn, Date startDateIn, Date endDateIn, String contractNumber, String accountNumber, String orderNumber) { this(); this.product = product; this.owner = ownerIn; this.quantity = quantityIn; this.startDate = startDateIn; this.endDate = endDateIn; this.contractNumber = contractNumber; this.accountNumber = accountNumber; this.orderNumber = orderNumber; this.setProvidedProducts(providedProducts); } /** {@inheritDoc} */ @Override @HateoasInclude public String getId() { return id; } /** * @param id new db id. */ public void setId(String id) { this.id = id; } /** * @return when the pool became active. */ public Date getStartDate() { return startDate; } /** * @param startDate set the pool active date. */ public void setStartDate(Date startDate) { this.startDate = startDate; } /** * @return when the pool expires. */ public Date getEndDate() { return endDate; } /** * @param endDate set the pool expiration date. */ public void setEndDate(Date endDate) { this.endDate = endDate; } /** * @return quantity */ public Long getQuantity() { return quantity; } /** * @param quantity quantity */ public void setQuantity(Long quantity) { this.quantity = quantity; } /** * @return quantity currently consumed. */ public Long getConsumed() { return consumed == null ? 0 : consumed; } /** * @param consumed set the activate uses. */ public void setConsumed(Long consumed) { // Even though this is calculated at DB fetch time, we allow // setting it for changes in a single transaction this.consumed = consumed; } /** * @return quantity currently exported. */ public Long getExported() { return exported == null ? 0 : exported; } /** * @param exported set the activate uses. */ public void setExported(Long exported) { // Even though this is calculated at DB fetch time, we allow // setting it for changes in a single transaction this.exported = exported; } /** * @return the quantity of entitlements in this pool shared to another org. */ public Long getShared() { return (shared == null) ? 0 : shared; } /** * @param shared the number to set the share count to */ public void setShared(Long shared) { this.shared = shared; } /** * @return owner of the pool. */ @Override public Owner getOwner() { return owner; } /** * @param owner changes the owner of the pool. */ public void setOwner(Owner owner) { this.owner = owner; } /** * The Marketing/Operations product name for the * <code>productId</code>. * * @return the productName */ @HateoasInclude public String getProductName() { return (this.getProduct() != null ? this.getProduct().getName() : null); } public String getDerivedProductName() { return (this.getDerivedProduct() != null ? this.getDerivedProduct().getName() : null); } /** * Return the contract for this pool's subscription. * * @return the contract number */ public String getContractNumber() { return contractNumber; } /** * Set the contract number. * * @param contractNumber */ public void setContractNumber(String contractNumber) { this.contractNumber = contractNumber; } public String getAccountNumber() { return accountNumber; } public void setAccountNumber(String accountNumber) { this.accountNumber = accountNumber; } public String getOrderNumber() { return orderNumber; } public void setOrderNumber(String orderNumber) { this.orderNumber = orderNumber; } /** * Retrieves the attributes for this pool. If this pool does not have any attributes, * this method returns an empty map. * * @return * a map containing the attributes for this pool */ public Map<String, String> getAttributes() { return Collections.unmodifiableMap(this.attributes); } /** * Retrieves the value associated with the given attribute. If the attribute is not set, this * method returns null. * * @param key * The key (name) of the attribute to lookup * * @throws IllegalArgumentException * if key is null * * @return * the value set for the given attribute, or null if the attribute is not set */ @XmlTransient public String getAttributeValue(String key) { if (key == null) { throw new IllegalArgumentException("key is null"); } return this.attributes.get(key); } /** * Checks if the given attribute has been defined on this pool. * * @param key * The key (name) of the attribute to lookup * * @throws IllegalArgumentException * if key is null * * @return * true if the attribute is defined for this pool; false otherwise */ @XmlTransient public boolean hasAttribute(String key) { if (key == null) { throw new IllegalArgumentException("key is null"); } return this.attributes.containsKey(key); } /** * Sets the specified attribute for this pool. If the attribute has already been set for * this pool, the existing value will be overwritten. If the given attribute value is null * or empty, the attribute will be removed. * * @param key * The name or key of the attribute to set * * @param value * The value to assign to the attribute, or null to remove the attribute * * @throws IllegalArgumentException * if key is null * * @return * a reference to this pool */ public Pool setAttribute(String key, String value) { if (key == null) { throw new IllegalArgumentException("key is null"); } // Impl note: // We can't standardize the value at all here; some attributes allow null, some expect // empty strings, and others have their own sential values. Unless we make a concerted // effort to fix all of these inconsistencies with a massive database update, we can't // perform any input sanitation/massaging. this.attributes.put(key, value); return this; } /** * Removes the attribute with the given attribute key from this pool. * * @param key * The name/key of the attribute to remove * * @throws IllegalArgumentException * if key is null * * @return * true if the attribute was removed successfully; false otherwise */ public boolean removeAttribute(String key) { if (key == null) { throw new IllegalArgumentException("key is null"); } boolean present = this.attributes.containsKey(key); this.attributes.remove(key); return present; } /** * Clears all attributes currently set for this pool. * * @return * a reference to this pool */ public Pool clearAttributes() { this.attributes.clear(); return this; } /** * Sets the attributes for this pool. * * @param attributes * A map of attribute key, value pairs to assign to this pool, or null to clear the * attributes * * @return * a reference to this pool */ public Pool setAttributes(Map<String, String> attributes) { this.attributes.clear(); if (attributes != null) { this.attributes.putAll(attributes); } return this; } /** * Checks if the given attribute is defined on this pool or its product. * * @param key * The key (name) of the attribute to check * * @throws IllegalArgumentException * if key is null * * @return * true if the attribute is set on this pool or its product; false otherwise */ public boolean hasMergedAttribute(String key) { if (key == null) { throw new IllegalArgumentException("key is null"); } return this.attributes.containsKey(key) || (this.product != null && this.product.hasAttribute(key)); } /** * Retrieves the value for the given attribute on this pool. If the attribute is not set on * this pool, its product, if available, will be checked instead. * * @param key * The key (name) of the attribute for which to fetch the value * * @throws IllegalArgumentException * if key is null * * @return * the value of the attribute for this pool or its product, or null if the attribute is not * set on either */ public String getMergedAttribute(String key) { if (key == null) { throw new IllegalArgumentException("key is null"); } String value = this.attributes.get(key); return (value == null && this.product != null) ? this.product.getAttributeValue(key) : value; } /** * returns true if the pool is considered expired based on the given date. * @param dateSource date to compare to. * @return true if the pool is considered expired based on the given date. */ @XmlTransient public boolean isExpired(DateSource dateSource) { return getEndDate().before(dateSource.currentDate()); } /** * Returns true if there are entitlements available in this pool, basically * if 'consumed' is less than the 'quantity'. * @return true if entitlements are available. */ public boolean entitlementsAvailable(Integer quantityToConsume) { if (isUnlimited()) { return true; } if (getConsumed() + quantityToConsume.intValue() <= getQuantity()) { return true; } return false; } /** * @return True if entitlement pool is unlimited. */ @XmlTransient public boolean isUnlimited() { return this.getQuantity() < 0; } /** * @return source entitlement. */ public Entitlement getSourceEntitlement() { return sourceEntitlement; } /** * @param sourceEntitlement source entitlement */ public void setSourceEntitlement(Entitlement sourceEntitlement) { this.sourceEntitlement = sourceEntitlement; } /** * @return subscription id associated with this pool. */ @JsonProperty("subscriptionId") public String getSubscriptionId() { if (this.getSourceSubscription() != null) { return this.getSourceSubscription().getSubscriptionId(); } return null; } @XmlTransient public SourceStack getSourceStack() { return sourceStack; } public void setSourceStack(SourceStack sourceStack) { if (sourceStack != null) { sourceStack.setDerivedPool(this); // Setting source Stack should invalidate source subscription this.setSourceSubscription(null); } this.sourceStack = sourceStack; } /** * @return true if this pool represents an active subscription. */ public Boolean getActiveSubscription() { return activeSubscription; } /** * @param activeSubscription TODO */ public void setActiveSubscription(Boolean activeSubscription) { this.activeSubscription = activeSubscription; } public String toString() { return String.format( "Pool [id=%s, type=%s, product=%s, productName=%s, quantity=%s]", this.getId(), this.getType(), this.getProductId(), this.getProductName(), this.getQuantity() ); } @JsonIgnore public Set<Product> getProvidedProducts() { return this.providedProducts; } public void addProvidedProduct(Product provided) { this.providedProducts.add(provided); } public void setProvidedProducts(Collection<Product> providedProducts) { this.providedProducts.clear(); if (providedProducts != null) { this.providedProducts.addAll(providedProducts); } } /* * Always exported as a DTO for API/import backward compatibility. */ @JsonProperty("providedProducts") public Set<ProvidedProduct> getProvidedProductDtos() { return providedProductDtos; } /** * This is a helper method to fill in transient fields in this class. * The transient fields are providedProductDtos, derivedProvidedProductDtosCached * * The reason we need to fill transient properties is, that various parts of code * that rely on serialization expect Pool object. * * From style point of view, this method could be moved to other class as well * (the ProductManager). * * * @param productCurator */ public void populateAllTransientProvidedProducts(ProductCurator productCurator) { Set<ProvidedProduct> prods = new HashSet<ProvidedProduct>(); if (this.providedProductDtos != null) { prods.addAll(this.providedProductDtos); } for (Product p : productCurator.getPoolProvidedProductsCached(id)) { prods.add(new ProvidedProduct(p)); } providedProductDtos = prods; derivedProvidedProductDtosCached = new HashSet<ProvidedProduct>(); for (Product p : productCurator.getPoolDerivedProvidedProductsCached(id)) { ProvidedProduct product = new ProvidedProduct(p); derivedProvidedProductDtosCached.add(product); } } /* * Used temporarily while importing a manifest. */ public void setProvidedProductDtos(Set<ProvidedProduct> dtos) { providedProductDtos = dtos; } /** * Return the "top level" product this pool is for. * Note that pools can also provide access to other products. * See getProvidedProductIds(). * @return Top level product ID. */ @HateoasInclude public String getProductId() { return this.product != null ? this.product.getId() : this.importedProductId; } public void setProductId(String productId) { this.importedProductId = productId; } public void setDerivedProductId(String productId) { this.importedDerivedProductId = productId; } /** * Retrieves the Product representing the top-level product for this pool. * * @return * the top-level product for this pool. */ @JsonIgnore public Product getProduct() { return this.product; } /** * Sets the Product to represent the top-level product for this pool. * * @param product * The Product to assign as the top-level product for this pool. */ @JsonProperty @JsonInclude(JsonInclude.Include.NON_NULL) public void setProduct(Product product) { this.product = product; if (product != null) { this.importedProductId = null; this.importedProductAttributes = null; this.importedDerivedProductId = null; this.importedDerivedProductAttributes = null; } } /** * Gets the entitlements for this instance. * * @return The entitlements. */ @XmlTransient public Set<Entitlement> getEntitlements() { return this.entitlements; } /** * Sets the entitlements for this instance. * * @param entitlements The entitlements. */ public void setEntitlements(Set<Entitlement> entitlements) { this.entitlements = entitlements; } public String getRestrictedToUsername() { return restrictedToUsername; } public void setRestrictedToUsername(String restrictedToUsername) { this.restrictedToUsername = restrictedToUsername; } /** * Check whether {@link #consumed} is greater than {@link #quantity} * * @return true if consumed>quantity else false. */ @XmlTransient public boolean isOverflowing() { // Unlimited pools can't be overflowing: if (this.quantity == -1) { return false; } return getConsumed() > this.quantity; } @HateoasInclude public String getHref() { return "/pools/" + getId(); } /** * @return the subscriptionSubKey */ @JsonProperty("subscriptionSubKey") public String getSubscriptionSubKey() { if (this.getSourceSubscription() != null) { return this.getSourceSubscription().getSubscriptionSubKey(); } return null; } public Map<String, String> getCalculatedAttributes() { return calculatedAttributes; } public void setCalculatedAttributes(Map<String, String> calculatedAttributes) { this.calculatedAttributes = calculatedAttributes; } @JsonIgnore public Product getDerivedProduct() { return this.derivedProduct; } public String getDerivedProductId() { return this.derivedProduct != null ? this.derivedProduct.getId() : this.importedDerivedProductId; } /** * Retrieves the product attributes for this pool. The product attributes will be identical to * those retrieved from getProduct().getAttributes(), except the set returned by this method is * not modifiable. * * @return * The attributes associated with the marketing product (SKU) for this pool */ public Map<String, String> getProductAttributes() { if (this.product != null) { return this.product.getAttributes(); } if (this.importedProductAttributes != null) { return Collections.unmodifiableMap(this.importedProductAttributes); } return Collections.<String, String>emptyMap(); } /** * Dummy method used to allow us to ignore the productAttributes field during JSON * deserialization. This method does nothing. * * @param attributes * ignored * * @deprecated * This method should be removed when we upgrade to Jackson 2.6, as it provides functionality * for doing this type of filtering without requiring dummy methods. */ @Deprecated public void setProductAttributes(Map<String, String> attributes) { if (this.product == null && this.derivedProduct == null) { this.importedProductAttributes = new HashMap<String, String>(attributes); } } /** * Retrieves the derived product attributes for this pool. The derived product attributes will * be identical to those retrieved from getDerivedProduct().getAttributes(), except the set * returned by this method is not modifiable. * * @return * The attributes associated with the derived product for this pool */ public Map<String, String> getDerivedProductAttributes() { if (this.derivedProduct != null) { return this.derivedProduct.getAttributes(); } if (this.importedDerivedProductAttributes != null) { return Collections.unmodifiableMap(this.importedDerivedProductAttributes); } return Collections.<String, String>emptyMap(); } /** * Dummy method used to allow us to ignore the derivedProductAttributes field during JSON * deserialization. This method does nothing. * * @param attributes * ignored * * @deprecated * This method should be removed when we upgrade to Jackson 2.6, as it provides functionality * for doing this type of filtering without requiring dummy methods. */ @Deprecated public void setDerivedProductAttributes(Map<String, String> attributes) { if (this.product == null) { this.importedDerivedProductAttributes = new HashMap<String, String>(attributes); } } @XmlTransient public String getProductAttributeValue(String key) { return this.getProductAttributes().get(key); } @JsonProperty @JsonInclude(JsonInclude.Include.NON_NULL) public void setDerivedProduct(Product derived) { this.derivedProduct = derived; if (derived != null) { this.importedProductId = null; this.importedProductAttributes = null; this.importedDerivedProductId = null; this.importedDerivedProductAttributes = null; } } @JsonIgnore public Set<Product> getDerivedProvidedProducts() { return derivedProvidedProducts; } public void addDerivedProvidedProduct(Product provided) { this.derivedProvidedProducts.add(provided); } public void setDerivedProvidedProducts(Collection<Product> derivedProvidedProducts) { this.derivedProvidedProducts.clear(); if (derivedProvidedProducts != null) { this.derivedProvidedProducts.addAll(derivedProvidedProducts); } } /* * Always exported as a DTO for API/import backward compatibility. */ @JsonProperty("derivedProvidedProducts") public Set<ProvidedProduct> getDerivedProvidedProductDtos() { Set<ProvidedProduct> pp = new HashSet<ProvidedProduct>(); Set<String> added = new HashSet<String>(); if (this.derivedProvidedProductDtosCached != null) { for (ProvidedProduct p : this.derivedProvidedProductDtosCached) { pp.add(p); added.add(p.getProductId()); } } if (this.derivedProvidedProductDtos != null) { for (ProvidedProduct p : this.derivedProvidedProductDtos) { if (!added.contains(p.getProductId())) { pp.add(p); } } } return pp; } /* * Used temporarily while importing a manifest. */ public void setDerivedProvidedProductDtos(Set<ProvidedProduct> dtos) { derivedProvidedProductDtos = dtos; } /* * Keeping getSourceStackId to avoid breaking the api */ @JsonProperty("sourceStackId") public String getSourceStackId() { if (this.getSourceStack() != null) { return this.getSourceStack().getSourceStackId(); } return null; } /** * There are a number of radically different types of pools. This field is * a quick indicator of what type of pool you are looking at. * See PoolType comments for descriptions of types. * * @return pool type */ public PoolType getType() { if (hasAttribute(Attributes.DERIVED_POOL)) { if (hasAttribute(Attributes.UNMAPPED_GUESTS_ONLY)) { return PoolType.UNMAPPED_GUEST; } else if (hasAttribute(Attributes.SHARE)) { return PoolType.SHARE_DERIVED; } else if (getSourceEntitlement() != null) { return PoolType.ENTITLEMENT_DERIVED; } else if (getSourceStack() != null) { return PoolType.STACK_DERIVED; } else { return PoolType.BONUS; } } else if (hasAttribute(Attributes.DEVELOPMENT_POOL)) { return PoolType.DEVELOPMENT; } return PoolType.NORMAL; } @JsonIgnore public PoolComplianceType getComplianceType() { Product product = this.getProduct(); if (product != null) { boolean isStacking = product.hasAttribute(Product.Attributes.STACKING_ID); boolean isMultiEnt = "yes".equalsIgnoreCase( product.getAttributeValue(Attributes.MULTI_ENTITLEMENT)); if (product.hasAttribute(Product.Attributes.INSTANCE_MULTIPLIER)) { if (isStacking && isMultiEnt) { return PoolComplianceType.INSTANCE_BASED; } } else { if (isStacking) { return isMultiEnt ? PoolComplianceType.STACKABLE : PoolComplianceType.UNIQUE_STACKABLE; } return isMultiEnt ? PoolComplianceType.MULTI_ENTITLEMENT : PoolComplianceType.STANDARD; } } return PoolComplianceType.UNKNOWN; } public boolean isStacked() { return (this.getProduct() != null ? this.getProduct().hasAttribute(Product.Attributes.STACKING_ID) : false); } public String getStackId() { return (this.getProduct() != null ? this.getProduct().getAttributeValue(Product.Attributes.STACKING_ID) : null); } @JsonIgnore public boolean isUnmappedGuestPool() { return "true".equalsIgnoreCase(this.getAttributeValue(Attributes.UNMAPPED_GUESTS_ONLY)); } public boolean isDevelopmentPool() { return "true".equalsIgnoreCase(this.getAttributeValue(Attributes.DEVELOPMENT_POOL)); } public Set<Branding> getBranding() { return branding; } public void setBranding(Set<Branding> branding) { this.branding = branding; } @XmlTransient public SourceSubscription getSourceSubscription() { return sourceSubscription; } public void setSourceSubscription(SourceSubscription sourceSubscription) { if (sourceSubscription != null) { sourceSubscription.setPool(this); } this.sourceSubscription = sourceSubscription; } public void setSubscriptionId(String subid) { if (sourceSubscription == null && !StringUtils.isBlank(subid)) { setSourceSubscription(new SourceSubscription()); } if (sourceSubscription != null) { sourceSubscription.setSubscriptionId(subid); if (StringUtils.isBlank(sourceSubscription.getSubscriptionId()) && StringUtils.isBlank(sourceSubscription.getSubscriptionSubKey())) { sourceSubscription = null; } } } public void setSubscriptionSubKey(String subkey) { if (sourceSubscription == null && !StringUtils.isBlank(subkey)) { setSourceSubscription(new SourceSubscription()); } if (sourceSubscription != null) { sourceSubscription.setSubscriptionSubKey(subkey); if (StringUtils.isBlank(sourceSubscription.getSubscriptionId()) && StringUtils.isBlank(sourceSubscription.getSubscriptionSubKey())) { sourceSubscription = null; } } } @JsonIgnore public static Long parseQuantity(String quantity) { Long q; if (quantity.equalsIgnoreCase("unlimited")) { q = -1L; } else { try { q = Long.parseLong(quantity); } catch (NumberFormatException e) { q = 0L; } } return q; } @Override public int compareTo(Pool other) { return (this.getId() == null ^ other.getId() == null) ? (this.getId() == null ? -1 : 1) : this.getId() == other.getId() ? 0 : this.getId().compareTo(other.getId()); } @Override @XmlTransient public String getName() { return this.getProductName(); } @XmlTransient public boolean isMarkedForDelete() { return this.markedForDelete; } public void setMarkedForDelete(boolean markedForDelete) { this.markedForDelete = markedForDelete; } @Override protected void onCreate() { super.onCreate(); this.type = this.getType(); } @Override protected void onUpdate() { super.onCreate(); this.type = this.getType(); } public String getUpstreamPoolId() { return upstreamPoolId; } public void setUpstreamPoolId(String upstreamPoolId) { this.upstreamPoolId = upstreamPoolId; } public String getUpstreamEntitlementId() { return upstreamEntitlementId; } public void setUpstreamEntitlementId(String upstreamEntitlementId) { this.upstreamEntitlementId = upstreamEntitlementId; } public String getUpstreamConsumerId() { return upstreamConsumerId; } public void setUpstreamConsumerId(String upstreamConsumerId) { this.upstreamConsumerId = upstreamConsumerId; } public Cdn getCdn() { return cdn; } public void setCdn(Cdn cdn) { this.cdn = cdn; } @XmlTransient public SubscriptionsCertificate getCertificate() { return this.cert; } public void setCertificate(SubscriptionsCertificate cert) { this.cert = cert; } }