protected ValidationResult invokeGlobalPreEntitlementRule(JsContext context) { // No method for this product, try to find a global function, if // neither exists this is ok and we'll just carry on. try { String resultJson = jsRules.invokeMethod(GLOBAL_PRE_FUNCTION, context); log.debug("Ran rule: " + GLOBAL_PRE_FUNCTION); return objectMapper.toObject(resultJson, ValidationResult.class); } catch (NoSuchMethodException ex) { // This is fine, I hope... log.warn("No default rule found: " + GLOBAL_PRE_FUNCTION); } catch (Exception ex) { throw new RuleExecutionException(ex); } return new ValidationResult(); }
protected ValidationResult callPreEntitlementRules(List<Rule> matchingRules, JsContext context) { ValidationResult result = new ValidationResult(); for (Rule rule : matchingRules) { String validationJson = jsRules.invokeRule(PRE_PREFIX + rule.getRuleName(), context); // If the resulting validation json is empty, either the method // did not exist in the rules, or the method did not return // anything. In this case we skip the result. if (validationJson == null) { continue; } result.add(objectMapper.toObject(validationJson, ValidationResult.class)); } return result; }
/** Enforces the Javascript Rules definition. */ public abstract class AbstractEntitlementRules implements Enforcer { protected Logger log = null; protected DateSource dateSource; protected ProductCache productCache; protected I18n i18n; protected Map<String, Set<Rule>> attributesToRules; protected JsRunner jsRules; protected Config config; protected ConsumerCurator consumerCurator; protected PoolCurator poolCurator; protected RulesObjectMapper objectMapper = RulesObjectMapper.instance(); protected static final String PROD_ARCHITECTURE_SEPARATOR = ","; protected static final String PRE_PREFIX = "pre_"; protected static final String POST_PREFIX = "post_"; protected static final String GLOBAL_PRE_FUNCTION = PRE_PREFIX + "global"; protected static final String GLOBAL_POST_FUNCTION = POST_PREFIX + "global"; protected void rulesInit() { String mappings; try { mappings = jsRules.invokeMethod("attribute_mappings"); this.attributesToRules = parseAttributeMappings(mappings); } catch (RhinoException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public List<Rule> rulesForAttributes(Set<String> attributes, Map<String, Set<Rule>> rules) { Set<Rule> possibleMatches = new HashSet<Rule>(); for (String attribute : attributes) { if (rules.containsKey(attribute)) { possibleMatches.addAll(rules.get(attribute)); } } List<Rule> matches = new LinkedList<Rule>(); for (Rule rule : possibleMatches) { if (attributes.containsAll(rule.getAttributes())) { matches.add(rule); } } // Always run the global rule, and run it first matches.add(new Rule("global", 0, new HashSet<String>())); Collections.sort(matches, new RuleOrderComparator()); return matches; } public Map<String, Set<Rule>> parseAttributeMappings(String mappings) { Map<String, Set<Rule>> toReturn = new HashMap<String, Set<Rule>>(); if (mappings.trim().isEmpty()) { return toReturn; } String[] separatedMappings = mappings.split(","); for (String mapping : separatedMappings) { Rule rule = parseRule(mapping); for (String attribute : rule.getAttributes()) { if (!toReturn.containsKey(attribute)) { toReturn.put(attribute, new HashSet<Rule>(Collections.singletonList(rule))); } toReturn.get(attribute).add(rule); } } return toReturn; } public Rule parseRule(String toParse) { String[] tokens = toParse.split(":"); if (tokens.length < 3) { throw new IllegalArgumentException( i18n.tr("''{0}'' Should contain name, priority and at least one attribute", toParse)); } Set<String> attributes = new HashSet<String>(); for (int i = 2; i < tokens.length; i++) { attributes.add(tokens[i].trim()); } try { return new Rule(tokens[0].trim(), Integer.parseInt(tokens[1]), attributes); } catch (NumberFormatException e) { throw new IllegalArgumentException( i18n.tr("second parameter should be the priority number.", e)); } } protected ValidationResult callPreEntitlementRules(List<Rule> matchingRules, JsContext context) { ValidationResult result = new ValidationResult(); for (Rule rule : matchingRules) { String validationJson = jsRules.invokeRule(PRE_PREFIX + rule.getRuleName(), context); // If the resulting validation json is empty, either the method // did not exist in the rules, or the method did not return // anything. In this case we skip the result. if (validationJson == null) { continue; } result.add(objectMapper.toObject(validationJson, ValidationResult.class)); } return result; } protected void callPostEntitlementRules(List<Rule> matchingRules) { for (Rule rule : matchingRules) { jsRules.invokeRule(POST_PREFIX + rule.getRuleName()); } } protected void invokeGlobalPostEntitlementRule(JsContext context) { // No method for this product, try to find a global function, if // neither exists this is ok and we'll just carry on. try { jsRules.invokeMethod(GLOBAL_POST_FUNCTION, context); log.debug("Ran rule: " + GLOBAL_POST_FUNCTION); } catch (NoSuchMethodException ex) { // This is fine, I hope... log.warn("No default rule found: " + GLOBAL_POST_FUNCTION); } catch (RhinoException ex) { throw new RuleExecutionException(ex); } } protected ValidationResult invokeGlobalPreEntitlementRule(JsContext context) { // No method for this product, try to find a global function, if // neither exists this is ok and we'll just carry on. try { String resultJson = jsRules.invokeMethod(GLOBAL_PRE_FUNCTION, context); log.debug("Ran rule: " + GLOBAL_PRE_FUNCTION); return objectMapper.toObject(resultJson, ValidationResult.class); } catch (NoSuchMethodException ex) { // This is fine, I hope... log.warn("No default rule found: " + GLOBAL_PRE_FUNCTION); } catch (Exception ex) { throw new RuleExecutionException(ex); } return new ValidationResult(); } protected void callPostUnbindRules(List<Rule> matchingRules) { for (Rule rule : matchingRules) { jsRules.invokeRule(POST_PREFIX + rule.getRuleName()); } } protected void invokeGlobalPostUnbindRule(JsContext context) { // No method for this product, try to find a global function, if // neither exists this is ok and we'll just carry on. try { jsRules.invokeMethod(GLOBAL_POST_FUNCTION, context); log.debug("Ran rule: " + GLOBAL_POST_FUNCTION); } catch (NoSuchMethodException ex) { // This is fine, I hope... log.warn("No default rule found: " + GLOBAL_POST_FUNCTION); } catch (RhinoException ex) { throw new RuleExecutionException(ex); } } // Always ensure that we do not over consume. // FIXME for auto sub stacking, we need to be able to pull across multiple // pools eventually, so this would need to go away in that case protected void validatePoolQuantity(ValidationResult result, Pool pool, int quantity) { if (!pool.entitlementsAvailable(quantity)) { result.addError("rulefailed.no.entitlements.available"); } } /** RuleOrderComparator */ public static class RuleOrderComparator implements Comparator<Rule>, Serializable { @Override public int compare(Rule o1, Rule o2) { return Integer.valueOf(o2.getOrder()).compareTo(Integer.valueOf(o1.getOrder())); } } /** Rule */ public static class Rule { private final String ruleName; private final int order; private final Set<String> attributes; public Rule(String ruleName, int order, Set<String> attributes) { this.ruleName = ruleName; this.order = order; this.attributes = attributes; } public String getRuleName() { return ruleName; } public int getOrder() { return order; } public Set<String> getAttributes() { return attributes; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((attributes == null) ? 0 : attributes.hashCode()); result = prime * result + order; result = prime * result + ((ruleName == null) ? 0 : ruleName.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Rule other = (Rule) obj; if (attributes == null) { if (other.attributes != null) { return false; } } else if (!attributes.equals(other.attributes)) { return false; } if (order != other.order) { return false; } if (ruleName == null) { if (other.ruleName != null) { return false; } } else if (!ruleName.equals(other.ruleName)) { return false; } return true; } public String toString() { return "'" + ruleName + "':" + order + ":" + attributes.toString(); } } protected void runPostEntitlement(PoolHelper postHelper, Entitlement entitlement) { Pool pool = entitlement.getPool(); Consumer c = entitlement.getConsumer(); Map<String, String> attributes = postHelper.getFlattenedAttributes(pool); // Perform pool management based on the attributes of the pool: // TODO: should really be cleaned up, this used to be post rules but // because // it actually manages pools we pulled back to engine. Needs re-org. if (attributes.containsKey("user_license")) { postBindUserLicense(postHelper, pool, c, attributes); } if (attributes.containsKey("virt_limit")) { postBindVirtLimit(postHelper, entitlement, pool, c, attributes); } } protected void runPostUnbind(PoolHelper postHelper, Entitlement entitlement) { Pool pool = entitlement.getPool(); Consumer c = entitlement.getConsumer(); Map<String, String> attributes = postHelper.getFlattenedAttributes(pool); if (attributes.containsKey("virt_limit")) { postUnbindVirtLimit(postHelper, entitlement, pool, c, attributes); } } private void postUnbindVirtLimit( PoolHelper postHelper, Entitlement entitlement, Pool pool, Consumer c, Map<String, String> attributes) { log.debug("Running virt_limit post unbind."); if (!config.standalone() && c.isManifest()) { String virtLimit = attributes.get("virt_limit"); if (!"unlimited".equals(virtLimit)) { // As we have unbound an entitlement from a physical pool that // was previously // exported, we need to add back the reduced bonus pool // quantity. int virtQuantity = Integer.parseInt(virtLimit) * entitlement.getQuantity(); if (virtQuantity > 0) { List<Pool> pools = postHelper.lookupBySubscriptionId(pool.getSubscriptionId()); for (int idex = 0; idex < pools.size(); idex++) { Pool derivedPool = pools.get(idex); if (derivedPool.getAttributeValue("pool_derived") != null) { postHelper.updatePoolQuantity(derivedPool, virtQuantity); } } } } else { // As we have unbound an entitlement from a physical pool that // was previously // exported, we need to set the unlimited bonus pool quantity to // -1. List<Pool> pools = postHelper.lookupBySubscriptionId(pool.getSubscriptionId()); for (int idex = 0; idex < pools.size(); idex++) { Pool derivedPool = pools.get(idex); if (derivedPool.getAttributeValue("pool_derived") != null) { if (derivedPool.getQuantity() == 0) { postHelper.setPoolQuantity(derivedPool, -1); } } } } } } private void postBindUserLicense( PoolHelper postHelper, Pool pool, Consumer c, Map<String, String> attributes) { log.debug("Running user_license post-bind."); if (!c.isManifest()) { // Default to using the same product from the pool. String productId = pool.getProductId(); // Check if the sub-pool should be for a different product: if (attributes.containsKey("user_license_product")) { productId = attributes.get("user_license_product"); } // Create a sub-pool for this user postHelper.createUserRestrictedPool(productId, pool, attributes.get("user_license")); } } private void postBindVirtLimit( PoolHelper postHelper, Entitlement entitlement, Pool pool, Consumer c, Map<String, String> attributes) { log.debug("Running virt_limit post-bind."); if (!c.isManifest() && (config.standalone() || attributes.containsKey("host_limited"))) { String productId = pool.getProductId(); String virtLimit = attributes.get("virt_limit"); if ("unlimited".equals(virtLimit)) { postHelper.createHostRestrictedPool(productId, pool, "unlimited"); } else { int virtQuantity = Integer.parseInt(virtLimit) * entitlement.getQuantity(); if (virtQuantity > 0) { postHelper.createHostRestrictedPool(productId, pool, String.valueOf(virtQuantity)); } } } else { if (!config.standalone() && c.isManifest()) { String virtLimit = attributes.get("virt_limit"); if (!"unlimited".equals(virtLimit)) { // if the bonus pool is not unlimited, then the bonus pool // quantity // needs to be adjusted based on the virt limit int virtQuantity = Integer.parseInt(virtLimit) * entitlement.getQuantity(); if (virtQuantity > 0) { List<Pool> pools = postHelper.lookupBySubscriptionId(pool.getSubscriptionId()); for (int idex = 0; idex < pools.size(); idex++) { Pool derivedPool = pools.get(idex); if (derivedPool.getAttributeValue("pool_derived") != null) { derivedPool = postHelper.updatePoolQuantity(derivedPool, -1 * virtQuantity); } } } } else { // if the bonus pool is unlimited, then the quantity needs // to go to 0 // when the physical pool is exhausted completely by export. // A quantity of 0 will block future binds, whereas -1 does // not. if (pool.getQuantity().equals(pool.getExported())) { // getting all pools matching the sub id. Filtering out // the 'parent'. List<Pool> pools = postHelper.lookupBySubscriptionId(pool.getSubscriptionId()); for (int idex = 0; idex < pools.size(); idex++) { Pool derivedPool = pools.get(idex); if (derivedPool.getAttributeValue("pool_derived") != null) { derivedPool = postHelper.setPoolQuantity(derivedPool, 0); } } } } } } } public PoolHelper postEntitlement(Consumer consumer, PoolHelper postEntHelper, Entitlement ent) { runPostEntitlement(postEntHelper, ent); return postEntHelper; } public PoolHelper postUnbind(Consumer c, PoolHelper postHelper, Entitlement ent) { runPostUnbind(postHelper, ent); return postHelper; } @Override public ValidationResult preEntitlement( Consumer consumer, Pool entitlementPool, Integer quantity) { jsRules.reinitTo("entitlement_name_space"); rulesInit(); return new ValidationResult(); } protected void logResult(ValidationResult result) { if (log.isDebugEnabled()) { for (ValidationError error : result.getErrors()) { log.debug(" Rule error: " + error.getResourceKey()); } for (ValidationWarning warning : result.getWarnings()) { log.debug(" Rule warning: " + warning.getResourceKey()); } } } }