/* * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.groupbasedpolicy.resolver; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicReference; import javax.annotation.concurrent.Immutable; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContractId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubjectName; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.ConsumerSelectionRelator; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Matcher.MatchType; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.ProviderSelectionRelator; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRefBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.condition.matchers.ConditionMatcher; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.conditions.Condition; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.target.selector.QualityMatcher; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Contract; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Clause; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Subject; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Target; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.GroupIdentificationConstraints; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.group.identification.constraints.GroupRequirementConstraintCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.group.identification.constraints.group.requirement.constraint._case.RequirementMatcher; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.group.identification.constraints.GroupCapabilityConstraintCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.group.identification.constraints.group.capability.constraint._case.CapabilityMatcher; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.Rule; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.RuleBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ConsumerNamedSelector; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ConsumerTargetSelector; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderNamedSelector; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderTargetSelector; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.HashBasedTable; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Ordering; import com.google.common.collect.Sets; import com.google.common.collect.Table; import com.google.common.collect.Table.Cell; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; /** * The policy resolver is a utility for renderers to help in resolving * group-based policy into a form that is easier to apply to the actual network. * *

For any pair of endpoint groups, there is a set of rules that could apply * to the endpoints on that group based on the policy configuration. The exact * list of rules that apply to a given pair of endpoints depends on the * conditions that are active on the endpoints. * *

We need to be able to query against this policy model, enumerate the * relevant classes of traffic and endpoints, and notify renderers when there * are changes to policy as it applies to active sets of endpoints and * endpoint groups. * *

The policy resolver will maintain the necessary state for all tenants * in its control domain, which is the set of tenants for which * policy listeners have been registered. * * @author readams */ public class PolicyResolver implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(PolicyResolver.class); private final DataBroker dataProvider; private final ScheduledExecutorService executor; /** * Keep track of the current relevant policy scopes. */ protected CopyOnWriteArrayList policyListenerScopes; protected ConcurrentMap resolvedTenants; /** * Store a policy object for each endpoint group pair. The table * is stored with the key as (consumer, provider). Two endpoints could * appear in both roles at the same time, in which case both policies would * apply. */ AtomicReference policy = new AtomicReference<>(); public PolicyResolver(DataBroker dataProvider, ScheduledExecutorService executor) { super(); this.dataProvider = dataProvider; this.executor = executor; policyListenerScopes = new CopyOnWriteArrayList<>(); resolvedTenants = new ConcurrentHashMap<>(); LOG.debug("Initialized renderer common policy resolver"); } // ************* // AutoCloseable // ************* @Override public void close() throws Exception { for (TenantContext ctx : resolvedTenants.values()) { if (ctx.registration != null) ctx.registration.close(); } } // ************************* // PolicyResolver public API // ************************* /** * Get a snapshot of the current policy * @return the {@link PolicyInfo} object representing an immutable * snapshot of the policy state */ public PolicyInfo getCurrentPolicy() { return policy.get(); } /** * Get the normalized tenant for the given ID * @param tenant the tenant ID * @return the {@link Tenant} */ public IndexedTenant getTenant(TenantId tenant) { TenantContext tc = resolvedTenants.get(tenant); if (tc == null) return null; return tc.tenant.get(); } /** * Register a listener to receive update events. * @param listener the {@link PolicyListener} object to receive the update * events */ public PolicyScope registerListener(PolicyListener listener) { PolicyScope ps = new PolicyScope(this, listener); policyListenerScopes.add(ps); return ps; } /** * Remove the listener registered for the given {@link PolicyScope}. * @param scope the scope to remove * @see PolicyResolver#registerListener(PolicyListener) */ public void removeListener(PolicyScope scope) { policyListenerScopes.remove(scope); } // ************** // Implementation // ************** /** * Atomically update the active policy and notify policy listeners * of relevant changes * @param policyMap the new policy to set * @param egConditions the map of endpoint groups to relevant condition sets * @return the set of groups with updated policy */ protected Set updatePolicy(Table policyMap, Map> egConditions, List policyListenerScopes) { PolicyInfo newPolicy = new PolicyInfo(policyMap, egConditions); PolicyInfo oldPolicy = policy.getAndSet(newPolicy); HashSet notifySet = new HashSet<>(); for (Cell cell : newPolicy.getPolicyMap().cellSet()) { Policy newp = cell.getValue(); Policy oldp = null; if (oldPolicy != null) oldp = oldPolicy.getPolicyMap().get(cell.getRowKey(), cell.getColumnKey()); if (oldp == null || !newp.equals(oldp)) { notifySet.add(cell.getRowKey()); notifySet.add(cell.getColumnKey()); } } if (oldPolicy != null) { for (Cell cell : oldPolicy.getPolicyMap().cellSet()) { if (!newPolicy.getPolicyMap().contains(cell.getRowKey(), cell.getColumnKey())) { notifySet.add(cell.getRowKey()); notifySet.add(cell.getColumnKey()); } } } return notifySet; } /** * Notify the policy listeners about a set of updated groups */ private void notifyListeners(Set updatedGroups) { for (final PolicyScope scope : policyListenerScopes) { Set filtered = Sets.filter(updatedGroups, new Predicate() { @Override public boolean apply(EgKey input) { return scope.contains(input.getTenantId(), input.getEgId()); } }); if (!filtered.isEmpty()) { scope.getListener().policyUpdated(filtered); } } } /** * Subscribe the resolver to updates related to a particular tenant * Make sure that this can't be called concurrently with subscribe * @param tenantId the tenant ID to subscribe to */ protected void subscribeTenant(TenantId tenantId) { if (!resolvedTenants.containsKey(tenantId)) updateTenant(tenantId); } /** * Unsubscribe the resolver from updates related to a particular tenant * Make sure that this can't be called concurrently with subscribe * @param tenantId the tenant ID to subscribe to */ protected void unsubscribeTenant(TenantId tenantId) { TenantContext context = resolvedTenants.get(tenantId); if (context != null) { resolvedTenants.remove(tenantId); context.registration.close(); } } private void updateTenant(final TenantId tenantId) { if (dataProvider == null) return; TenantContext context = resolvedTenants.get(tenantId); if (context == null) { ListenerRegistration registration = null; registration = dataProvider .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, TenantUtils.tenantIid(tenantId), new PolicyChangeListener(tenantId), DataChangeScope.SUBTREE); context = new TenantContext(registration); TenantContext oldContext = resolvedTenants.putIfAbsent(tenantId, context); if (oldContext != null) { // already registered in a different thread; just use the other // context registration.close(); context = oldContext; } else { LOG.info("Added tenant {} to policy scope", tenantId); } } // Resolve the new tenant and update atomically final AtomicReference tenantRef = context.tenant; final IndexedTenant ot = tenantRef.get(); ReadOnlyTransaction transaction = dataProvider.newReadOnlyTransaction(); final InstanceIdentifier tiid = TenantUtils.tenantIid(tenantId); ListenableFuture> unresolved; unresolved = transaction.read(LogicalDatastoreType.CONFIGURATION, tiid); Futures.addCallback(unresolved, new FutureCallback>() { @Override public void onSuccess(Optional result) { if (!result.isPresent()) { LOG.warn("Tenant {} not found", tenantId); } Tenant t = InheritanceUtils.resolveTenant((Tenant)result.get()); IndexedTenant it = new IndexedTenant(t); if (!tenantRef.compareAndSet(ot, it)) { // concurrent update of tenant policy. Retry updateTenant(tenantId); } else { // Update the policy cache and notify listeners WriteTransaction wt = dataProvider.newWriteOnlyTransaction(); wt.put(LogicalDatastoreType.OPERATIONAL, tiid, t, true); wt.submit(); updatePolicy(); } } @Override public void onFailure(Throwable t) { LOG.error("Count not get tenant {}", tenantId, t); } }, executor); } protected void updatePolicy() { try { Map> egConditions = new HashMap<>(); Table policyMap = resolvePolicy(resolvedTenants.values(), egConditions); Set updatedGroups = updatePolicy(policyMap, egConditions, policyListenerScopes); notifyListeners(updatedGroups); } catch (Exception e) { LOG.error("Failed to update policy", e); } } /** * Resolve the policy in three phases: * (1) select contracts that in scope based on contract selectors. * (2) select subjects that are in scope for each contract based on * matchers in clauses * (3) resolve the set of in-scope contracts into a list of subjects that * apply for each pair of endpoint groups and the conditions that can * apply for for each endpoint in those groups. */ protected Table resolvePolicy(Collection tenants, Map> egConditions) { // select contracts that apply for the given tenant Table> contractMatches = selectContracts(tenants); // select subjects for the matching contracts and resolve the policy // for endpoint group pairs. This does phase (2) and (3) as one step return selectSubjects(contractMatches, egConditions); } /** * Choose the contracts that are in scope for each pair of endpoint * groups, then perform subject selection for the pair */ protected Table> selectContracts(Collection tenants) { Table> consumerMatches = HashBasedTable.create(); Table> contractMatches = HashBasedTable.create(); for (TenantContext tenant : tenants) { IndexedTenant t = tenant.tenant.get(); if (t == null) continue; selectContracts(consumerMatches, contractMatches, t.getTenant()); } return contractMatches; } protected void selectContracts(Table> consumerMatches, Table> contractMatches, Tenant tenant) { // For each endpoint group, match consumer selectors // against contracts to get a set of matching consumer selectors if (tenant.getEndpointGroup() == null) return; for (EndpointGroup group : tenant.getEndpointGroup()) { List r = matchConsumerContracts(tenant, group); for (ConsumerContractMatch ccm : r) { List cms = consumerMatches.get(tenant.getId(), ccm.contract.getId()); if (cms == null) { cms = new ArrayList<>(); consumerMatches.put(tenant.getId(), ccm.contract.getId(), cms); } cms.add(ccm); } } // Match provider selectors, and check each match for a corresponding // consumer selector match. for (EndpointGroup group : tenant.getEndpointGroup()) { List matches = matchProviderContracts(tenant, group, consumerMatches); for (ContractMatch cm : matches) { EgKey consumerKey = new EgKey(cm.consumerTenant.getId(), cm.consumer.getId()); EgKey providerKey = new EgKey(cm.providerTenant.getId(), cm.provider.getId()); List egPairMatches = contractMatches.get(consumerKey, providerKey); if (egPairMatches == null) { egPairMatches = new ArrayList<>(); contractMatches.put(consumerKey, providerKey, egPairMatches); } egPairMatches.add(cm); } } } private boolean clauseMatches(Clause clause, ContractMatch match) { if (clause.getConsumerMatchers() != null) { GroupIdentificationConstraints groupIdentificationConstraintsConsumer = clause.getConsumerMatchers() .getGroupIdentificationConstraints(); if (groupIdentificationConstraintsConsumer instanceof GroupRequirementConstraintCase) { List reqMatchers = ((GroupRequirementConstraintCase) groupIdentificationConstraintsConsumer) .getRequirementMatcher(); if (reqMatchers != null) { for (RequirementMatcher reqMatcher : reqMatchers) { if (!MatcherUtils.applyReqMatcher(reqMatcher, match.consumerRelator)) { return false; } } } } } if (clause.getProviderMatchers() != null) { org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.GroupIdentificationConstraints groupIdentificationConstraintsProvider = clause .getProviderMatchers().getGroupIdentificationConstraints(); if (groupIdentificationConstraintsProvider instanceof GroupCapabilityConstraintCase) { List capMatchers = ((GroupCapabilityConstraintCase) groupIdentificationConstraintsProvider) .getCapabilityMatcher(); if (capMatchers != null) { for (CapabilityMatcher capMatcher : capMatchers) { if (!MatcherUtils.applyCapMatcher(capMatcher, match.providerRelator)) { return false; } } } } } return true; } private ConditionSet buildConditionSet(List condMatchers) { if (condMatchers == null) return ConditionSet.EMPTY; ImmutableSet.Builder allb = ImmutableSet.builder(); ImmutableSet.Builder noneb = ImmutableSet.builder(); ImmutableSet.Builder> anyb = ImmutableSet.builder(); for (ConditionMatcher condMatcher : condMatchers) { if (condMatcher.getCondition() == null) continue; MatchType type = condMatcher.getMatchType(); if (type == null) type = MatchType.All; if (type.equals(MatchType.Any)) { ImmutableSet.Builder a = ImmutableSet.builder(); for (Condition c : condMatcher.getCondition()) { a.add(c.getName()); } anyb.add(a.build()); } else { for (Condition c : condMatcher.getCondition()) { switch (type) { case Any: break; case None: noneb.add(c.getName()); break; case All: default: allb.add(c.getName()); break; } } } } return new ConditionSet(allb.build(), noneb.build(), anyb.build()); } private ConditionSet buildConsConditionSet(Clause clause) { if (clause.getConsumerMatchers() != null) { List condMatchers = clause.getConsumerMatchers().getConditionMatcher(); return buildConditionSet(condMatchers); } return ConditionSet.EMPTY; } private ConditionSet buildProvConditionSet(Clause clause) { if (clause.getProviderMatchers() != null) { List condMatchers = clause.getProviderMatchers().getConditionMatcher(); return buildConditionSet(condMatchers); } return ConditionSet.EMPTY; } private Policy resolvePolicy(Tenant contractTenant, Contract contract, boolean reverse, Policy merge, Table> subjectMap) { Table> ruleMap = HashBasedTable.create(); if (merge != null) { ruleMap.putAll(merge.ruleMap); } for (Cell> entry : subjectMap.cellSet()) { List rules = new ArrayList<>(); ConditionSet rowKey = entry.getRowKey(); ConditionSet columnKey = entry.getColumnKey(); if (reverse) { rowKey = columnKey; columnKey = entry.getRowKey(); } List oldrules = ruleMap.get(rowKey, columnKey); if (oldrules != null) { rules.addAll(oldrules); } for (Subject s : entry.getValue()) { if (s.getRule() == null) continue; List srules; if (reverse) srules = reverseRules(s.getRule()); else srules = Ordering .from(TenantUtils.RULE_COMPARATOR) .immutableSortedCopy(s.getRule()); RuleGroup rg = new RuleGroup(srules, s.getOrder(), contractTenant, contract, s.getName()); rules.add(rg); } Collections.sort(rules); ruleMap.put(rowKey, columnKey, Collections.unmodifiableList(rules)); } return new Policy(ruleMap); } private List reverseRules(List rules) { ArrayList nrules = new ArrayList<>(); for (Rule input : rules) { if (input.getClassifierRef() == null || input.getClassifierRef().size() == 0) { nrules.add(input); continue; } List classifiers = new ArrayList<>(); for (ClassifierRef clr : input.getClassifierRef()) { Direction nd = Direction.Bidirectional; if (clr.getDirection() != null) { switch (clr.getDirection()) { case In: nd = Direction.Out; break; case Out: nd = Direction.In; break; case Bidirectional: default: nd = Direction.Bidirectional; } } classifiers.add(new ClassifierRefBuilder(clr) .setDirection(nd).build()); } nrules.add(new RuleBuilder(input) .setClassifierRef(Collections.unmodifiableList(classifiers)) .build()); } Collections.sort(nrules, TenantUtils.RULE_COMPARATOR); return Collections.unmodifiableList(nrules); } /** * Get the "natural" direction for the policy for the given pair of * endpoint groups. * @param one The first endpoint group * @param two The second endpoint group * @return true if the order should be reversed in the index */ protected static boolean shouldReverse(EgKey one, EgKey two) { if (one.compareTo(two) < 0) { return true; } return false; } private void addConditionSet(EgKey eg, ConditionSet cs, Map> egConditions) { if (egConditions == null) return; Set cset = egConditions.get(eg); if (cset == null) { egConditions.put(eg, cset = new HashSet<>()); } cset.add(cs); } /** * Choose the set of subjects that in scope for each possible set of * endpoint conditions */ protected Table selectSubjects(Table> contractMatches, Map> egConditions) { // Note that it's possible to further simplify the resulting policy // in the case of things like repeated rules, condition sets that // cover other condition sets, etc. This would be a good thing to do // at some point Table policy = HashBasedTable.create(); for (List matches : contractMatches.values()) { for (ContractMatch match : matches) { List clauses = match.contract.getClause(); if (clauses == null) continue; List subjectList = match.contract.getSubject(); if (subjectList == null) continue; EgKey ckey = new EgKey(match.consumerTenant.getId(), match.consumer.getId()); EgKey pkey = new EgKey(match.providerTenant.getId(), match.provider.getId()); EgKey one = ckey; EgKey two = pkey; boolean reverse = shouldReverse(ckey, pkey); if (reverse) { one = pkey; two = ckey; } Policy existing = policy.get(one, two); HashMap subjects = new HashMap<>(); for (Subject s : subjectList) { subjects.put(s.getName(), s); } Table> subjectMap = HashBasedTable.create(); for (Clause clause : clauses) { if (clause.getSubjectRefs() != null && clauseMatches(clause, match)) { ConditionSet consCSet = buildConsConditionSet(clause); addConditionSet(ckey, consCSet, egConditions); ConditionSet provCSet = buildProvConditionSet(clause); addConditionSet(pkey, provCSet, egConditions); List clauseSubjects = subjectMap.get(consCSet, provCSet); if (clauseSubjects == null) { clauseSubjects = new ArrayList<>(); subjectMap.put(consCSet, provCSet, clauseSubjects); } for (SubjectName sn : clause.getSubjectRefs()) { Subject s = subjects.get(sn); if (s != null) clauseSubjects.add(s); } } } policy.put(one, two, resolvePolicy(match.contractTenant, match.contract, reverse, existing, subjectMap)); } } return policy; } private List matchConsumerContracts(Tenant tenant, EndpointGroup consumer) { List matches = new ArrayList<>(); if (consumer.getConsumerNamedSelector() != null) { for (ConsumerNamedSelector cns : consumer.getConsumerNamedSelector()) { if (cns.getContract() == null) continue; for (ContractId contractId : cns.getContract()) { Contract contract = TenantUtils.findContract(tenant, contractId); if (contract == null) continue; matches.add(new ConsumerContractMatch(tenant, contract, tenant, consumer, cns)); } } } if (consumer.getConsumerTargetSelector() != null) { for (ConsumerTargetSelector cts : consumer.getConsumerTargetSelector()) { if (tenant.getContract() == null) continue; for (Contract contract : tenant.getContract()) { if (contract.getTarget() == null) continue; for (Target t : contract.getTarget()) { boolean match = true; if (cts.getQualityMatcher() != null) { for (QualityMatcher m : cts.getQualityMatcher()) { if (!MatcherUtils.applyQualityMatcher(m, t)) { match = false; break; } } } if (match) { matches.add(new ConsumerContractMatch(tenant, contract, tenant, consumer, cts)); } } } } } // TODO match selectors also against contract references // for (ConsumerTargetSelector cts : consumer.getConsumerTargetSelector()) { // if (tenant.getContractRef() == null) continue; // for (ContractRef c : tenant.getContractRef()) { // // } // } return matches; } private void amendContractMatches(List matches, List cMatches, Tenant tenant, EndpointGroup provider, ProviderSelectionRelator relator) { if (cMatches == null) return; for (ConsumerContractMatch cMatch : cMatches) { matches.add(new ContractMatch(cMatch, tenant, provider, relator)); } } private List matchProviderContracts(Tenant tenant, EndpointGroup provider, Table> consumerMatches) { List matches = new ArrayList<>(); if (provider.getProviderNamedSelector() != null) { for (ProviderNamedSelector pns : provider.getProviderNamedSelector()) { if (pns.getContract() == null) continue; for (ContractId contractId : pns.getContract()) { Contract c = TenantUtils.findContract(tenant, contractId); if (c == null) continue; List cMatches = consumerMatches.get(tenant.getId(), c.getId()); amendContractMatches(matches, cMatches, tenant, provider, pns); } } } if (provider.getProviderTargetSelector() != null) { for (ProviderTargetSelector pts : provider.getProviderTargetSelector()) { if (tenant.getContract() == null) continue; for (Contract c : tenant.getContract()) { if (c.getTarget() == null) continue; for (Target t : c.getTarget()) { boolean match = true; if (pts.getQualityMatcher() != null) { for (QualityMatcher m : pts.getQualityMatcher()) { if (!MatcherUtils.applyQualityMatcher(m, t)) { match = false; break; } } } if (match) { List cMatches = consumerMatches.get(tenant.getId(), c.getId()); amendContractMatches(matches, cMatches, tenant, provider, pts); } } } } } return matches; } protected static class TenantContext { ListenerRegistration registration; AtomicReference tenant = new AtomicReference<>(); public TenantContext(ListenerRegistration registration) { super(); this.registration = registration; } } /** * Represents a selected contract made by endpoint groups matching it * using selection relators. This is the result of the contract selection * phase. * @author readams * */ @Immutable protected static class ContractMatch extends ConsumerContractMatch { /** * The tenant ID of the provider endpoint group */ final Tenant providerTenant; /** * The provider endpoint group */ final EndpointGroup provider; /** * The provider selection relator that was used to match the contract */ final ProviderSelectionRelator providerRelator; public ContractMatch(ConsumerContractMatch consumerMatch, Tenant providerTenant, EndpointGroup provider, ProviderSelectionRelator providerRelator) { super(consumerMatch.contractTenant, consumerMatch.contract, consumerMatch.consumerTenant, consumerMatch.consumer, consumerMatch.consumerRelator); this.providerTenant = providerTenant; this.provider = provider; this.providerRelator = providerRelator; } } @Immutable private static class ConsumerContractMatch { /** * The tenant of the matching contract */ final Tenant contractTenant; /** * The matching contract */ final Contract contract; /** * The tenant for the endpoint group */ final Tenant consumerTenant; /** * The consumer endpoint group */ final EndpointGroup consumer; /** * The consumer selection relator that was used to match the contract */ final ConsumerSelectionRelator consumerRelator; public ConsumerContractMatch(Tenant contractTenant, Contract contract, Tenant consumerTenant, EndpointGroup consumer, ConsumerSelectionRelator consumerRelator) { super(); this.contractTenant = contractTenant; this.contract = contract; this.consumerTenant = consumerTenant; this.consumer = consumer; this.consumerRelator = consumerRelator; } } @Immutable private class PolicyChangeListener implements DataChangeListener { final TenantId tenantId; public PolicyChangeListener(TenantId tenantId) { super(); this.tenantId = tenantId; } @Override public void onDataChanged(AsyncDataChangeEvent, DataObject> arg0) { updateTenant(tenantId); } } }