* Tweaks to the policy model 28/7528/4
authorRob Adams <readams@readams.net>
Thu, 29 May 2014 23:44:51 +0000 (16:44 -0700)
committerRob Adams <readams@readams.net>
Fri, 13 Jun 2014 21:45:56 +0000 (14:45 -0700)
* InheritanceUtils implements inheritance of tenants to generate a normalized
  representation
* Beginnings of renderer common.  Implement resolution of policies into a
  policy cache that can be queried against and we can add subscriptions.

Change-Id: Ia74f4adc4a4dddd43c7ffff3189cacb3fa69bb51
Signed-off-by: Rob Adams <readams@readams.net>
18 files changed:
.gitignore
groupbasedpolicy/pom.xml
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/endpoint/EndpointRegistry.java
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyListener.java [new file with mode: 0644]
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyResolverService.java [new file with mode: 0644]
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyScope.java [new file with mode: 0644]
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/InheritanceUtils.java [new file with mode: 0644]
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/MatcherUtils.java [new file with mode: 0644]
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyCache.java [new file with mode: 0644]
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyResolutionException.java [new file with mode: 0644]
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyResolver.java [new file with mode: 0644]
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyScopeImpl.java [new file with mode: 0644]
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/TenantUtils.java [new file with mode: 0644]
groupbasedpolicy/src/main/yang/common.yang
groupbasedpolicy/src/main/yang/policy.yang
groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/resolver/internal/InheritanceUtilsTest.java [new file with mode: 0644]
groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/resolver/internal/MatcherUtilsTest.java [new file with mode: 0644]
groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyResolverTest.java [new file with mode: 0644]

index 474ad8fd54b8e73da51f483fd11c64be4fa836b9..e7be9b11a47d3395a6b73f6fd65f938ece268263 100644 (file)
@@ -10,6 +10,7 @@ products
 repository
 workspace
 *~
+#*
 target
 .classpath
 .project
index a57c8410fbc2714ad62c6d2fd981b42dbaf27030..ccad8b4a7952c4dc0b0549fed6f2d0091b5a39a3 100644 (file)
 
   <dependencies>
     <dependency>
-      <groupId>com.google.collections</groupId>
-      <artifactId>google-collections</artifactId>
-      <version>1.0</version>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
+  <!-- Project reporting -->
+  <reporting>
+    <plugins>
+      <!-- Code coverage analysis -->
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>cobertura-maven-plugin</artifactId>
+        <version>2.6</version>
+      </plugin>
+    </plugins>
+  </reporting>
+
+  <!-- project build -->
   <build>
     <plugins>
       <plugin>
           <instructions>
             <Import-Package>*</Import-Package>
             <Export-Package>org.opendaylight.controller.config.yang.config.endpoint_provider</Export-Package>
-            <!-- <Bundle-Activator>
-              org.opendaylight.groupbasedpolicy.plugin.Activator
-            </Bundle-Activator> -->
           </instructions>
           <manifestLocation>${project.basedir}/META-INF</manifestLocation>
         </configuration>
       </plugin>
+      <!-- Code coverage analysis -->
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>cobertura-maven-plugin</artifactId>
+        <version>2.6</version>
+       <configuration>
+          <instrumentation>
+           <excludes>
+             <exclude>org/opendaylight/yang/**/*.class</exclude>
+             <exclude>org/opendaylight/controller/config/yang/**/*.class</exclude>
+           </excludes>
+         </instrumentation>
+       </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <redirectTestOutputToFile>true</redirectTestOutputToFile>
+         <!-- needed for cobertura -->
+          <argLine>-XX:-UseSplitVerifier</argLine>
+        </configuration>
+      </plugin>
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>build-helper-maven-plugin</artifactId>
index 92a80e70164f46bccf0148c2697775434ce98c5b..818c6c3be526651f92e6ebcaee79dc693354480a 100644 (file)
@@ -1,3 +1,11 @@
+/*
+ * 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.endpoint;
 
 import java.util.ArrayList;
@@ -97,7 +105,11 @@ public class EndpointRegistry implements AutoCloseable, EndpointService {
     @Override
     public Future<RpcResult<Void>>
         registerEndpoint(RegisterEndpointInput input) {
-        Endpoint ep = new EndpointBuilder(input).build();
+        long timestamp = System.currentTimeMillis();
+        Endpoint ep = new EndpointBuilder(input)
+            .setTimestamp(timestamp)
+            .build();
+    
         EndpointKey key = 
                 new EndpointKey(ep.getL2Namespace(), ep.getMacAddress());
         InstanceIdentifier<Endpoint> iid = 
@@ -118,6 +130,7 @@ public class EndpointRegistry implements AutoCloseable, EndpointService {
                 EndpointL3 ep3 = new EndpointL3Builder(input)
                     .setIpAddress(key3.getIpAddress())
                     .setL3Namespace(l3addr.getL3Namespace())
+                    .setTimestamp(timestamp)
                     .build();
                 InstanceIdentifier<EndpointL3> iid_l3 = 
                         InstanceIdentifier.builder(Endpoints.class)
diff --git a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyListener.java b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyListener.java
new file mode 100644 (file)
index 0000000..da26322
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+/**
+ * An interface for an object that can listen for events related to group-based
+ * policy resolution.
+ * @author readams
+ */
+public interface PolicyListener {
+
+    
+}
diff --git a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyResolverService.java b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyResolverService.java
new file mode 100644 (file)
index 0000000..6d30f96
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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.Collection;
+import java.util.List;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
+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.tenants.tenant.contract.subject.Rule;
+
+/**
+ * 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.
+ * 
+ * <p>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.
+ * 
+ * In a more formal sense: Let there be endpoint groups G_n, and for each G_n a 
+ * set of conditions C_n that can apply to endpoints in G_n.  Further, let S be 
+ * the set of lists of rules defined in the policy.  Our policy can be 
+ * represented as a function F: (G_n, 2^C_n, G_m, 2^C_m) -> S, where 2^C_n 
+ * represents the power set of C_n. In other words, we want to map all the 
+ * possible tuples of pairs of endpoints along with their active conditions 
+ * onto the right list of rules to apply.
+ * 
+ * <p>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.
+ * 
+ * <p>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 interface PolicyResolverService {
+
+    /**
+     * Get the policy that currently applies to a endpoints. 
+     * with the specified groups and conditions.  The rules are normalized 
+     * such that rules with a direction of "out" apply to traffic from the 
+     * first endpoint to the second endpoint.
+     * 
+     * @param ep1Group the endpoint group for the first endpoint 
+     * @param ep1Conds The conditions that apply to the first endpoint
+     * @param ep2Group the endpoint group for the second endpoint 
+     * @param ep2Conds The conditions that apply to the second endpoint.
+     * @return a list of {@link Rule} that apply to the endpoints.
+     */
+    public List<Rule> getPolicy(EndpointGroupId ep1Group, 
+                                Collection<Condition> ep1Conds,
+                                EndpointGroupId ep2Group, 
+                                Collection<Condition> ep2Conds);
+
+    /**
+     * Register a listener to receive update events.
+     * @param listener the {@link PolicyListener} object to receive the update
+     * events
+     */
+    public PolicyScope registerListener(PolicyListener listener);
+
+    /**
+     * Remove the listener registered for the given {@link PolicyScopeImpl}.
+     * @param scope the scope to remove
+     * @see PolicyResolver#registerListener(PolicyListener)
+     */
+    public void removeListener(PolicyScope scope);
+
+}
\ No newline at end of file
diff --git a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyScope.java b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyScope.java
new file mode 100644 (file)
index 0000000..8313b7b
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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 org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
+
+/**
+ * The policy scope object represents a scope for policy-related information.
+ * A renderer that addresses a particular scope can express this as a 
+ * {@link PolicyScopeImpl} with an associates {@link PolicyListener} that can
+ * receive relevant updates.
+ * @see PolicyResolver 
+ * @author readams
+ */
+public interface PolicyScope {
+
+    /**
+     * Add the endpoint group from the given tenant to the scope of updates
+     * @param tenant the tenant for the endpoint group
+     * @param endpointGroup the endpoint group to add
+     */
+    public void addToScope(TenantId tenant,
+                           EndpointGroupId endpointGroup);
+
+    /**
+     * Add all endpoint groups in the given tenant to the scope of updates
+     * @param tenant the tenant to add.
+     */
+    public void addToScope(TenantId tenant);
+
+}
\ No newline at end of file
diff --git a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/InheritanceUtils.java b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/InheritanceUtils.java
new file mode 100644 (file)
index 0000000..25ba19c
--- /dev/null
@@ -0,0 +1,831 @@
+/*
+ * 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.internal;
+
+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 org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.groupbasedpolicy.resolver.internal.MatcherUtils.GetLabelName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.CapabilityMatcherName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.CapabilityName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClauseName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionMatcherName;
+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.EndpointGroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.LabelName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.QualityMatcherName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.QualityName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RequirementMatcherName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RequirementName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SelectorName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubjectName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TargetName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Label;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.capabilities.Capability;
+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.condition.matchers.ConditionMatcherBuilder;
+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.has.qualities.Quality;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.requirements.Requirement;
+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.target.selector.QualityMatcherBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.target.selector.quality.matcher.MatcherQuality;
+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.TenantBuilder;
+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.ContractBuilder;
+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.EndpointGroupBuilder;
+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.ClauseBuilder;
+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.SubjectBuilder;
+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.TargetBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.ConsumerMatchers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.ConsumerMatchersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.ProviderMatchers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.ProviderMatchersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.RequirementMatcher;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.RequirementMatcherBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.requirement.matcher.MatcherRequirement;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.CapabilityMatcher;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.CapabilityMatcherBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.capability.matcher.MatcherCapability;
+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.ConsumerNamedSelectorBuilder;
+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.ConsumerTargetSelectorBuilder;
+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.ProviderNamedSelectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderTargetSelector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderTargetSelectorBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Utilities useful for resolving the inheritance rules for the various objects
+ * in the system
+ * @author readams
+ *
+ */
+public class InheritanceUtils {
+    /**
+     * Fully resolve the specified {@link Tenant}, returning a tenant with all 
+     * items fully normalized.  This means that no items will have parent/child 
+     * relationships and can be interpreted simply without regard to inheritance
+     * rules 
+     * @param tenantId the {@link TenantId} of the {@link Tenant}
+     * @param transaction a {@link DataModificationTransaction} to use for 
+     * reading the data from the policy store
+     * @return the fully-resolved {@link Tenant}
+     */
+    public static Tenant resolveTenant(TenantId tenantId,
+                                       DataModificationTransaction transaction) {
+        InstanceIdentifier<Tenant> tenantIid = TenantUtils.tenantIid(tenantId);
+        DataObject tObject = transaction.readConfigurationData(tenantIid);
+        if (tObject == null || !(tObject instanceof Tenant))
+            return null;
+        
+        Tenant unresolvedTenant = (Tenant)tObject;
+        HashMap<EndpointGroupId, EndpointGroup> resolvedEgs = new HashMap<>();
+        HashMap<ContractId, Contract> resolvedContracts = new HashMap<>();
+        
+        if (unresolvedTenant.getEndpointGroup() != null) {
+            for (EndpointGroup eg : unresolvedTenant.getEndpointGroup()) {
+                resolveEndpointGroup(unresolvedTenant, eg, resolvedEgs);
+            }
+        }
+        if (unresolvedTenant.getContract() != null) {
+            for (Contract c : unresolvedTenant.getContract()) {
+                resolveContract(unresolvedTenant, c, resolvedContracts);
+            }
+        }
+
+        // XXX TODO - inherit from common tenant
+        
+        return new TenantBuilder()
+            .setId(unresolvedTenant.getId())
+            .setName(unresolvedTenant.getName())
+            .setDescription(unresolvedTenant.getDescription())
+            .setEndpointGroup(ImmutableList.copyOf(resolvedEgs.values()))
+            .setContract(ImmutableList.copyOf(resolvedContracts.values()))
+            .setContractRef(unresolvedTenant.getContractRef())
+            .setSubjectFeatureInstances(unresolvedTenant.getSubjectFeatureInstances())
+            .build();
+    }
+
+    // ****************
+    // Helper functions
+    // ****************
+    
+    private static void resolveEndpointGroup(Tenant unresolvedTenant,
+                                             EndpointGroup unresolvedEg,
+                                             HashMap<EndpointGroupId, 
+                                                     EndpointGroup> resolvedEgs) {
+        // put the unresolved object into the data structure to avoid loops
+        resolvedEgs.put(unresolvedEg.getId(), unresolvedEg);
+        
+        // resolve parent if it hasn't been resolved already
+        EndpointGroup parent = null;
+        if (unresolvedEg.getParent() != null) {
+            if (!resolvedEgs.containsKey(unresolvedEg.getParent())) {
+                parent = TenantUtils.findEndpointGroup(unresolvedTenant, 
+                                                       unresolvedEg.getParent());
+                if (parent != null)
+                    resolveEndpointGroup(unresolvedTenant, parent, resolvedEgs);
+            }
+            parent = resolvedEgs.get(unresolvedEg.getParent());
+        }
+
+        HashMap<SelectorName, ConsumerTargetSelector> resolvedCts = 
+                new HashMap<>();
+        HashMap<SelectorName, ConsumerNamedSelector> resolvedCns = 
+                new HashMap<>();
+        HashMap<SelectorName, ProviderTargetSelector> resolvedPts = 
+                new HashMap<>();
+        HashMap<SelectorName, ProviderNamedSelector> resolvedPns = 
+                new HashMap<>();
+
+        if (unresolvedEg.getConsumerTargetSelector() != null) {
+            for (ConsumerTargetSelector s : unresolvedEg.getConsumerTargetSelector()) {
+                resolveCts(unresolvedTenant, unresolvedEg, s, resolvedCts);
+            }
+        }
+        if (unresolvedEg.getConsumerNamedSelector() != null) {
+            for (ConsumerNamedSelector s : unresolvedEg.getConsumerNamedSelector()) {
+                resolveCns(unresolvedTenant, unresolvedEg, s, resolvedCns);
+            }
+        }
+        if (unresolvedEg.getProviderTargetSelector() != null) {
+            for (ProviderTargetSelector s : unresolvedEg.getProviderTargetSelector()) {
+                resolvePts(unresolvedTenant, unresolvedEg, s, resolvedPts);
+            }
+        }
+        if (unresolvedEg.getProviderNamedSelector() != null) {
+            for (ProviderNamedSelector s : unresolvedEg.getProviderNamedSelector()) {
+                resolvePns(unresolvedTenant, unresolvedEg, s, resolvedPns);
+            }
+        }
+
+        if (parent != null) {
+            if (parent.getConsumerTargetSelector() != null) {
+                for (ConsumerTargetSelector cts : parent.getConsumerTargetSelector()) {
+                    if (!resolvedCts.containsKey(cts.getName()))
+                        resolvedCts.put(cts.getName(), cts);
+                }
+            }
+            if (parent.getConsumerNamedSelector() != null) {
+                for (ConsumerNamedSelector cns : parent.getConsumerNamedSelector()) {
+                    if (!resolvedCns.containsKey(cns.getName()))
+                        resolvedCns.put(cns.getName(), cns);
+                }
+            }
+            if (parent.getProviderTargetSelector() != null) {
+                for (ProviderTargetSelector pts : parent.getProviderTargetSelector()) {
+                    if (!resolvedPts.containsKey(pts.getName()))
+                        resolvedPts.put(pts.getName(), pts);
+                }
+            }
+            if (parent.getProviderNamedSelector() != null) {
+                for (ProviderNamedSelector pns : parent.getProviderNamedSelector()) {
+                    if (!resolvedPns.containsKey(pns.getName()))
+                        resolvedPns.put(pns.getName(), pns);
+                }
+            }
+        }
+
+        // Note: do not set parent, or any of the values that only exist
+        // for inheritance
+        EndpointGroup resolvedEg = new EndpointGroupBuilder()
+            .setId(unresolvedEg.getId())
+            .setDescription(unresolvedEg.getDescription())
+            .setConsumerTargetSelector(ImmutableList.copyOf(resolvedCts.values()))
+            .setConsumerNamedSelector(ImmutableList.copyOf(resolvedCns.values()))
+            .setProviderTargetSelector(ImmutableList.copyOf(resolvedPts.values()))
+            .setProviderNamedSelector(ImmutableList.copyOf(resolvedPns.values()))
+            .build();
+        resolvedEgs.put(resolvedEg.getId(), resolvedEg);
+    }
+    
+    private static void resolveCts(Tenant unresolvedTenant,
+                                   EndpointGroup unresolvedEg,
+                                   ConsumerTargetSelector unresolvedTs,
+                                   HashMap<SelectorName, 
+                                           ConsumerTargetSelector> resolvedCts) {
+        HashMap<QualityMatcherName, QualityMatcher> matchers = new HashMap<>();
+        HashMap<RequirementName, Requirement> requirements = new HashMap<>();
+        HashSet<EndpointGroupId> visited = new HashSet<>();
+
+        resolveCtsAttr(unresolvedTenant, unresolvedEg, unresolvedTs.getName(), 
+                       matchers, requirements, visited);
+        
+        ConsumerTargetSelector resolved = new ConsumerTargetSelectorBuilder()
+            .setName(unresolvedTs.getName())
+            .setQualityMatcher(ImmutableList.copyOf(matchers.values()))
+            .setRequirement(ImmutableList.copyOf(requirements.values()))
+            .build();
+        resolvedCts.put(resolved.getName(), resolved);
+    }
+    
+    private static void resolveCtsAttr(Tenant unresolvedTenant,
+                                       EndpointGroup unresolvedEg,
+                                       SelectorName name,
+                                       HashMap<QualityMatcherName, 
+                                               QualityMatcher> matchers,
+                                       HashMap<RequirementName, 
+                                               Requirement> requirements,
+                                       HashSet<EndpointGroupId> visited) {
+        if (unresolvedEg == null) return;
+        if (visited.contains(unresolvedEg.getId())) return;
+        visited.add(unresolvedEg.getId());
+        if (unresolvedEg.getParent() != null) {
+            resolveCtsAttr(unresolvedTenant, 
+                           TenantUtils.findEndpointGroup(unresolvedTenant, 
+                                                         unresolvedEg.getParent()),
+                           name, 
+                           matchers,
+                           requirements,
+                           visited);
+        }
+        resolveLabels(unresolvedEg.getRequirement(), requirements, 
+                      MatcherUtils.getRequirementName);
+        ConsumerTargetSelector unresolvedSelector = 
+                TenantUtils.findCts(unresolvedEg, name);
+        if (unresolvedSelector == null) return;
+        resolveLabels(unresolvedSelector.getRequirement(), requirements, 
+                      MatcherUtils.getRequirementName);
+        resolveQualityMatcher(unresolvedSelector.getQualityMatcher(), matchers);
+    }
+    
+    private static void resolveCns(Tenant unresolvedTenant,
+                                   EndpointGroup unresolvedEg,
+                                   ConsumerNamedSelector unresolvedTs,
+                                   HashMap<SelectorName, 
+                                           ConsumerNamedSelector> resolvedCns) {
+        HashMap<RequirementName, Requirement> requirements = new HashMap<>();
+        HashSet<ContractId> contracts = new HashSet<>();
+        HashSet<EndpointGroupId> visited = new HashSet<>();
+
+        resolveCnsAttr(unresolvedTenant, unresolvedEg, unresolvedTs.getName(), 
+                       requirements, contracts, visited);
+        
+        ConsumerNamedSelector resolved = new ConsumerNamedSelectorBuilder()
+            .setName(unresolvedTs.getName())
+            .setRequirement(ImmutableList.copyOf(requirements.values()))
+            .setContract(ImmutableList.copyOf(contracts))
+            .build();
+        resolvedCns.put(resolved.getName(), resolved);
+    }
+    
+    private static void resolveCnsAttr(Tenant unresolvedTenant,
+                                       EndpointGroup unresolvedEg,
+                                       SelectorName name,
+                                       HashMap<RequirementName, 
+                                               Requirement> requirements,
+                                       HashSet<ContractId> contracts,
+                                       HashSet<EndpointGroupId> visited) {
+        if (unresolvedEg == null) return;
+        if (visited.contains(unresolvedEg.getId())) return;
+        visited.add(unresolvedEg.getId());
+        if (unresolvedEg.getParent() != null) {
+            resolveCnsAttr(unresolvedTenant, 
+                           TenantUtils.findEndpointGroup(unresolvedTenant, 
+                                                         unresolvedEg.getParent()),
+                           name, requirements, contracts, visited);
+        }
+        resolveLabels(unresolvedEg.getRequirement(), requirements, 
+                      MatcherUtils.getRequirementName);
+        ConsumerNamedSelector unresolvedSelector =
+                TenantUtils.findCns(unresolvedEg, name);
+        if (unresolvedSelector == null) return;
+        resolveLabels(unresolvedSelector.getRequirement(), requirements,
+                      MatcherUtils.getRequirementName);
+        if (unresolvedSelector.getContract() != null) {
+            contracts.addAll(unresolvedSelector.getContract());
+        }
+    }
+
+    private static void resolvePts(Tenant unresolvedTenant,
+                                   EndpointGroup unresolvedEg,
+                                   ProviderTargetSelector unresolvedTs,
+                                   HashMap<SelectorName, 
+                                           ProviderTargetSelector> resolvedCts) {
+        HashMap<QualityMatcherName, QualityMatcher> matchers = new HashMap<>();
+        HashMap<CapabilityName, Capability> capabilities = new HashMap<>();
+        HashSet<EndpointGroupId> visited = new HashSet<>();
+
+        resolvePtsAttr(unresolvedTenant, unresolvedEg, unresolvedTs.getName(), 
+                       matchers, capabilities, visited);
+        
+        ProviderTargetSelector resolved = new ProviderTargetSelectorBuilder()
+            .setName(unresolvedTs.getName())
+            .setQualityMatcher(ImmutableList.copyOf(matchers.values()))
+            .setCapability(ImmutableList.copyOf(capabilities.values()))
+            .build();
+        resolvedCts.put(resolved.getName(), resolved);
+    }
+    
+    private static void resolvePtsAttr(Tenant unresolvedTenant,
+                                       EndpointGroup unresolvedEg,
+                                       SelectorName name,
+                                       HashMap<QualityMatcherName, 
+                                               QualityMatcher> matchers,
+                                       HashMap<CapabilityName, 
+                                               Capability> capabilities,
+                                       HashSet<EndpointGroupId> visited) {
+        if (unresolvedEg == null) return;
+        if (visited.contains(unresolvedEg.getId())) return;
+        visited.add(unresolvedEg.getId());
+        if (unresolvedEg.getParent() != null) {
+           resolvePtsAttr(unresolvedTenant, 
+                           TenantUtils.findEndpointGroup(unresolvedTenant, 
+                                                         unresolvedEg.getParent()),
+                           name, 
+                           matchers,
+                           capabilities, visited);
+        }
+        resolveLabels(unresolvedEg.getCapability(), capabilities, 
+                      MatcherUtils.getCapabilityName);
+        ProviderTargetSelector unresolvedSelector = 
+                TenantUtils.findPts(unresolvedEg, name);
+        if (unresolvedSelector == null) return;
+        resolveLabels(unresolvedSelector.getCapability(), capabilities, 
+                      MatcherUtils.getCapabilityName);
+        resolveQualityMatcher(unresolvedSelector.getQualityMatcher(), matchers);
+    }
+    
+    private static void resolvePns(Tenant unresolvedTenant,
+                                   EndpointGroup unresolvedEg,
+                                   ProviderNamedSelector unresolvedTs,
+                                   HashMap<SelectorName, 
+                                           ProviderNamedSelector> resolvedCns) {
+        HashMap<CapabilityName, Capability> capabilities = new HashMap<>();
+        HashSet<ContractId> contracts = new HashSet<>();
+        HashSet<EndpointGroupId> visited = new HashSet<>();
+        
+        resolvePnsAttr(unresolvedTenant, unresolvedEg, unresolvedTs.getName(), 
+                       capabilities, contracts, visited);
+        
+        ProviderNamedSelector resolved = new ProviderNamedSelectorBuilder()
+            .setName(unresolvedTs.getName())
+            .setCapability(ImmutableList.copyOf(capabilities.values()))
+            .setContract(ImmutableList.copyOf(contracts))
+            .build();
+        resolvedCns.put(resolved.getName(), resolved);
+    }
+    
+    private static void resolvePnsAttr(Tenant unresolvedTenant,
+                                       EndpointGroup unresolvedEg,
+                                       SelectorName name,
+                                       HashMap<CapabilityName, 
+                                               Capability> capabilities,
+                                       HashSet<ContractId> contracts,
+                                       HashSet<EndpointGroupId> visited) {
+        if (unresolvedEg == null) return;
+        if (visited.contains(unresolvedEg.getId())) return;
+        visited.add(unresolvedEg.getId());
+        if (unresolvedEg.getParent() != null) {
+            resolvePnsAttr(unresolvedTenant, 
+                           TenantUtils.findEndpointGroup(unresolvedTenant, 
+                                                         unresolvedEg.getParent()),
+                           name, capabilities, contracts, visited);
+        }
+        resolveLabels(unresolvedEg.getCapability(), capabilities, 
+                      MatcherUtils.getCapabilityName);
+        ProviderNamedSelector unresolvedSelector =
+                TenantUtils.findPns(unresolvedEg, name);
+        if (unresolvedSelector == null) return;
+        resolveLabels(unresolvedSelector.getCapability(), capabilities,
+                      MatcherUtils.getCapabilityName);
+        if (unresolvedSelector.getContract() != null) {
+            contracts.addAll(unresolvedSelector.getContract());
+        }
+    }
+   
+    private static void resolveContract(Tenant unresolvedTenant,
+                                        Contract unresolvedContract,
+                                        HashMap<ContractId, 
+                                                Contract> resolvedContracts) {
+        // put the unresolved object into the data structure to avoid loops
+        resolvedContracts.put(unresolvedContract.getId(), unresolvedContract);
+
+        // resolve parent if it hasn't been resolved already
+        Contract parent = null;
+        if (unresolvedContract.getParent() != null) {
+            if (!resolvedContracts.containsKey(unresolvedContract.getParent())) {
+                parent = TenantUtils.findContract(unresolvedTenant,
+                                                  unresolvedContract.getParent());
+                if (parent != null)
+                    resolveContract(unresolvedTenant, 
+                                    parent, 
+                                    resolvedContracts);
+            }
+            parent = resolvedContracts.get(unresolvedContract.getParent());
+        }
+        
+        HashMap<TargetName, Target> resolvedTargets = new HashMap<>();
+        HashMap<ClauseName, Clause> resolvedClauses = new HashMap<>();
+        HashMap<SubjectName, Subject> resolvedSubjects = new HashMap<>();
+
+        if (unresolvedContract.getTarget() != null) {
+            for (Target t : unresolvedContract.getTarget()) {
+                resolveTarget(unresolvedTenant, unresolvedContract, 
+                              t, resolvedTargets);
+            }
+        }
+        if (unresolvedContract.getClause() != null) {
+            for (Clause c : unresolvedContract.getClause()) {
+                resolveClause(unresolvedTenant, unresolvedContract, 
+                              c, resolvedClauses);
+            }
+        }
+        if (unresolvedContract.getSubject() != null ) {
+            for (Subject s : unresolvedContract.getSubject()) {
+                resolveSubject(unresolvedTenant, unresolvedContract,
+                               s, resolvedSubjects);
+            }
+        }
+
+        if (parent != null) {
+            if (parent.getTarget() != null) {
+                for (Target t : parent.getTarget()) {
+                    if (!resolvedTargets.containsKey(t.getName()))
+                        resolvedTargets.put(t.getName(), t);
+                }
+            }
+            if (parent.getClause() != null) {
+                for (Clause c : parent.getClause()) {
+                    if (!resolvedClauses.containsKey(c.getName()))
+                        resolvedClauses.put(c.getName(), c);
+                }
+            }
+            if (parent.getSubject() != null) {
+                for (Subject s : parent.getSubject()) {
+                    if (!resolvedSubjects.containsKey(s.getName()))
+                        resolvedSubjects.put(s.getName(), s);
+                }
+            }
+        }
+
+        Contract resolvedContract = new ContractBuilder()
+            .setId(unresolvedContract.getId())
+            .setDescription(unresolvedContract.getDescription())
+            .setTarget(ImmutableList.copyOf(resolvedTargets.values()))
+            .setClause(ImmutableList.copyOf(resolvedClauses.values()))
+            .setSubject(ImmutableList.copyOf(resolvedSubjects.values()))
+            .build();
+        resolvedContracts.put(resolvedContract.getId(), resolvedContract);
+    }
+    
+    private static void resolveTarget(Tenant unresolvedTenant,
+                                      Contract unresolvedContract,
+                                      Target unresolvedTarget,
+                                      HashMap<TargetName, Target> resolvedTargets) {
+        HashMap<QualityName, Quality> qualities = new HashMap<>();
+        HashSet<ContractId> visited = new HashSet<>();
+
+        resolveTargetAttrs(unresolvedTenant, 
+                           unresolvedContract, 
+                           unresolvedTarget.getName(), 
+                           qualities, visited);
+
+        Target resolved = new TargetBuilder()
+            .setName(unresolvedTarget.getName())
+            .setQuality(ImmutableList.copyOf(qualities.values()))
+            .build();
+        resolvedTargets.put(resolved.getName(), resolved);
+    }
+    
+    private static void resolveTargetAttrs(Tenant unresolvedTenant,
+                                           Contract unresolvedContract,
+                                           TargetName targetName,
+                                           HashMap<QualityName, Quality> qualities,
+                                           HashSet<ContractId> visited) {
+        if (unresolvedContract == null) return;
+        if (visited.contains(unresolvedContract.getId())) return;
+        visited.add(unresolvedContract.getId());
+        if (unresolvedContract.getParent() != null) {
+            resolveTargetAttrs(unresolvedTenant, 
+                               TenantUtils.findContract(unresolvedTenant, 
+                                                        unresolvedContract.getParent()),
+                               targetName, 
+                               qualities, visited);
+        }
+        resolveLabels(unresolvedContract.getQuality(), qualities, 
+                      MatcherUtils.getQualityName);
+        Target unresolvedTarget = 
+                TenantUtils.findTarget(unresolvedContract, targetName);
+        resolveLabels(unresolvedTarget.getQuality(), qualities, 
+                      MatcherUtils.getQualityName);        
+    }
+
+    private static void 
+        resolveQualityMatcher(Collection<QualityMatcher> toResolve,
+                              HashMap<QualityMatcherName,
+                                      QualityMatcher> matchers) {
+        for (QualityMatcher qm : toResolve) {
+            if (matchers.containsKey(qm.getName())) {
+                QualityMatcher oqm = matchers.get(qm.getName());
+                QualityMatcherBuilder qmb = new QualityMatcherBuilder();
+                qmb.setName(qm.getName());
+                qmb.setMatchType(oqm.getMatchType());
+                if (qm.getMatchType() != null)
+                    qmb.setMatchType(qm.getMatchType());
+
+                HashMap<QualityName, MatcherQuality> qualities = 
+                        new HashMap<>();
+                resolveLabels(oqm.getMatcherQuality(), qualities, 
+                              MatcherUtils.getMatcherQualityName);
+                resolveLabels(qm.getMatcherQuality(), qualities, 
+                              MatcherUtils.getMatcherQualityName);
+                
+                qmb.setMatcherQuality(ImmutableList.copyOf(qualities.values()));
+                matchers.put(qm.getName(), qmb.build());
+            } else {
+                matchers.put(qm.getName(), qm);
+            }
+        }
+    }
+
+    private static void 
+        resolveCapabilityMatcher(Collection<CapabilityMatcher> toResolve,
+                                 HashMap<CapabilityMatcherName,
+                                         CapabilityMatcher> matchers) {
+        for (CapabilityMatcher m : toResolve) {
+            if (matchers.containsKey(m.getName())) {
+                CapabilityMatcher om = matchers.get(m.getName());
+                CapabilityMatcherBuilder mb = new CapabilityMatcherBuilder();
+                mb.setName(m.getName());
+                mb.setMatchType(om.getMatchType());
+                if (m.getMatchType() != null)
+                    mb.setMatchType(m.getMatchType());
+
+                HashMap<CapabilityName, MatcherCapability> labels = 
+                        new HashMap<>();
+                resolveLabels(om.getMatcherCapability(), labels, 
+                              MatcherUtils.getMatcherCapabilityName);
+                resolveLabels(m.getMatcherCapability(), labels, 
+                              MatcherUtils.getMatcherCapabilityName);
+                
+                mb.setMatcherCapability(ImmutableList.copyOf(labels.values()));
+                matchers.put(m.getName(), mb.build());
+            } else {
+                matchers.put(m.getName(), m);
+            }
+        }
+    }
+
+    private static void 
+        resolveRequirementMatcher(Collection<RequirementMatcher> toResolve,
+                                  HashMap<RequirementMatcherName,
+                                          RequirementMatcher> matchers) {
+        for (RequirementMatcher m : toResolve) {
+            if (matchers.containsKey(m.getName())) {
+                RequirementMatcher om = matchers.get(m.getName());
+                RequirementMatcherBuilder mb = new RequirementMatcherBuilder();
+                mb.setName(m.getName());
+                mb.setMatchType(om.getMatchType());
+                if (m.getMatchType() != null)
+                    mb.setMatchType(m.getMatchType());
+
+                HashMap<RequirementName, MatcherRequirement> labels = 
+                        new HashMap<>();
+                resolveLabels(om.getMatcherRequirement(), labels, 
+                              MatcherUtils.getMatcherRequirementName);
+                resolveLabels(m.getMatcherRequirement(), labels, 
+                              MatcherUtils.getMatcherRequirementName);
+                
+                mb.setMatcherRequirement(ImmutableList.copyOf(labels.values()));
+                matchers.put(m.getName(), mb.build());
+            } else {
+                matchers.put(m.getName(), m);
+            }
+        }
+    }
+
+    private static void 
+        resolveConditionMatcher(Collection<ConditionMatcher> toResolve,
+                                 HashMap<ConditionMatcherName,
+                                         ConditionMatcher> matchers) {
+        for (ConditionMatcher m : toResolve) {
+            if (matchers.containsKey(m.getName())) {
+                ConditionMatcher om = matchers.get(m.getName());
+                ConditionMatcherBuilder mb = new ConditionMatcherBuilder();
+                mb.setName(m.getName());
+                mb.setMatchType(om.getMatchType());
+                if (m.getMatchType() != null)
+                    mb.setMatchType(m.getMatchType());
+
+                HashMap<ConditionName, Condition> labels = 
+                        new HashMap<>();
+                resolveLabels(om.getCondition(), labels, 
+                              MatcherUtils.getConditionName);
+                resolveLabels(m.getCondition(), labels, 
+                              MatcherUtils.getConditionName);
+                
+                mb.setCondition(ImmutableList.copyOf(labels.values()));
+                matchers.put(m.getName(), mb.build());
+            } else {
+                matchers.put(m.getName(), m);
+            }
+        }
+    }
+
+    private static void resolveClause(Tenant unresolvedTenant,
+                                      Contract unresolvedContract,
+                                      Clause unresolvedClause,
+                                      HashMap<ClauseName, Clause> resolvedClauses) {
+        HashMap<CapabilityMatcherName, CapabilityMatcher> capMatchers = new HashMap<>();
+        HashMap<ConditionMatcherName, ConditionMatcher> provCondMatchers = new HashMap<>();
+        HashMap<RequirementMatcherName, RequirementMatcher> reqMatchers = new HashMap<>();
+        HashMap<ConditionMatcherName, ConditionMatcher> consCondMatchers = new HashMap<>();
+        HashSet<SubjectName> subjectRefs = new HashSet<>();
+        HashSet<ContractId> visited = new HashSet<>();
+
+        resolveClauseAttr(unresolvedTenant, unresolvedContract, 
+                          unresolvedClause.getName(), subjectRefs, 
+                          capMatchers, provCondMatchers,
+                          reqMatchers, consCondMatchers, visited);
+        
+        Clause resolved = new ClauseBuilder()
+            .setName(unresolvedClause.getName())
+            .setSubjectRefs(ImmutableList.copyOf(subjectRefs))
+            .setProviderMatchers(new ProviderMatchersBuilder()
+                .setCapabilityMatcher(ImmutableList.copyOf(capMatchers.values()))
+                .setConditionMatcher(ImmutableList.copyOf(provCondMatchers.values()))
+                .build())
+            .setConsumerMatchers(new ConsumerMatchersBuilder()
+                .setRequirementMatcher(ImmutableList.copyOf(reqMatchers.values()))
+                .setConditionMatcher(ImmutableList.copyOf(consCondMatchers.values()))
+                .build())
+            .build();
+        resolvedClauses.put(resolved.getName(), resolved);
+    }
+    
+    private static void resolveClauseAttr(Tenant unresolvedTenant,
+                                          Contract unresolvedContract,
+                                          ClauseName clauseName,
+                                          HashSet<SubjectName> subjectRefs,
+                                          HashMap<CapabilityMatcherName, 
+                                                  CapabilityMatcher> capMatchers,
+                                          HashMap<ConditionMatcherName, 
+                                                  ConditionMatcher> provCondMatchers,
+                                          HashMap<RequirementMatcherName, 
+                                                  RequirementMatcher> reqMatchers,
+                                          HashMap<ConditionMatcherName, 
+                                                  ConditionMatcher> consCondMatchers,
+                                          HashSet<ContractId> visited) {
+        if (unresolvedContract == null) return;
+        if (visited.contains(unresolvedContract.getId())) return;
+        visited.add(unresolvedContract.getId());
+        if (unresolvedContract.getParent() != null) {
+            resolveClauseAttr(unresolvedTenant, 
+                              TenantUtils.findContract(unresolvedTenant, 
+                                                       unresolvedContract.getParent()), 
+                              clauseName, 
+                              subjectRefs, 
+                              capMatchers, 
+                              provCondMatchers,
+                              reqMatchers, 
+                              consCondMatchers, visited);
+        }
+        
+        Clause unresolvedClause =
+                TenantUtils.findClause(unresolvedContract, clauseName);
+        if (unresolvedClause == null) return;
+        
+        if (unresolvedClause.getProviderMatchers() != null) {
+            ProviderMatchers pms = unresolvedClause.getProviderMatchers();
+            resolveCapabilityMatcher(pms.getCapabilityMatcher(), capMatchers);
+            resolveConditionMatcher(pms.getConditionMatcher(), provCondMatchers);
+        }
+        if (unresolvedClause.getConsumerMatchers() != null) {
+            ConsumerMatchers cms = unresolvedClause.getConsumerMatchers();
+            resolveRequirementMatcher(cms.getRequirementMatcher(), reqMatchers);
+            resolveConditionMatcher(cms.getConditionMatcher(), consCondMatchers);
+        }
+        if (unresolvedClause.getSubjectRefs() != null)
+            subjectRefs.addAll(unresolvedClause.getSubjectRefs());
+    }
+
+    private static class Mutable<O> {
+        O value;
+    }
+    
+    private static void resolveSubject(Tenant unresolvedTenant,
+                                       Contract unresolvedContract,
+                                       Subject unresolvedSubject,
+                                       HashMap<SubjectName, Subject> resolvedSubjects) {
+        Mutable<Integer> order = new Mutable<>();
+        Mutable<List<Rule>> rules = new Mutable<>();
+        rules.value = Collections.emptyList();
+        HashSet<ContractId> visited = new HashSet<>();
+        
+        resolveSubjectAttr(unresolvedTenant, unresolvedContract, 
+                           unresolvedSubject.getName(), order, rules, visited);
+        
+        Subject resolved = new SubjectBuilder()
+            .setName(unresolvedSubject.getName())
+            .setOrder(order.value)
+            .setRule(rules.value)
+            .build();
+        resolvedSubjects.put(resolved.getName(), resolved);
+    }
+    
+    private static Rule makeRule(Rule r, int order) {
+        return new RuleBuilder()
+            .setName(r.getName())
+            .setActionRef(r.getActionRef())
+            .setClassifierRef(r.getClassifierRef())
+            .setOrder(order)
+            .build();
+    }
+    
+    private static void resolveSubjectAttr(Tenant unresolvedTenant,
+                                           Contract unresolvedContract,
+                                           SubjectName subjectName,
+                                           Mutable<Integer> order,
+                                           Mutable<List<Rule>> rules,
+                                           HashSet<ContractId> visited) {
+        if (unresolvedContract == null) return;
+        if (visited.contains(unresolvedContract.getId())) return;
+        visited.add(unresolvedContract.getId());
+        if (unresolvedContract.getParent() != null) {
+            resolveSubjectAttr(unresolvedTenant, 
+                              TenantUtils.findContract(unresolvedTenant, 
+                                                       unresolvedContract.getParent()), 
+                              subjectName,
+                              order,
+                              rules, visited);
+        }
+        
+        Subject unresolvedSubject = 
+                TenantUtils.findSubject(unresolvedContract, subjectName);
+        if (unresolvedSubject == null) return;
+        if (unresolvedSubject.getOrder() != null)
+            order.value = unresolvedSubject.getOrder();
+        if (unresolvedSubject.getRule() != null) {
+            ImmutableList.Builder<Rule> rbuilder = 
+                    new ImmutableList.Builder<Rule>();
+            ArrayList<Rule> nrules = 
+                    new ArrayList<>(unresolvedSubject.getRule());
+            Collections.sort(nrules, TenantUtils.RULE_COMPARATOR);
+            int index = 0;
+            for (Rule r : nrules) {
+                rbuilder.add(makeRule(r, index++));
+            }
+            for (Rule r : rules.value) {
+                rbuilder.add(makeRule(r, index++));
+            }
+            rules.value = rbuilder.build();
+        }
+    }
+    
+    /**
+     * Given a partially-resolved set of labels, add the next item in the
+     * inheritance ordering to the set of resolved labels.
+     * @param toResolve the new set to add
+     * @param labels the partially-resolved set
+     * @param getName a function object to get the appropriate typed label name
+     */
+    private static <L extends Label, LN extends LabelName> void 
+        resolveLabels(Collection<L> toResolve, HashMap<LN, L> labels, 
+                      GetLabelName<L, LN> getName) {
+        if (toResolve == null) return;
+        for (L l : toResolve) {
+            if (l.getInclusionRule() != null) {
+                switch (l.getInclusionRule()) {
+                case Include:
+                    // override
+                    labels.put(getName.getName(l), l);
+                    break;
+                case Exclude:
+                    // remove
+                    labels.remove(getName.getName(l));
+                    break;
+                }
+            } else {
+                // default to Include
+                labels.put(getName.getName(l), l);
+            }
+        }
+    }
+}
diff --git a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/MatcherUtils.java b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/MatcherUtils.java
new file mode 100644 (file)
index 0000000..8237715
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * 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.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.concurrent.Immutable;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.CapabilityName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.LabelName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.QualityName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RelatorName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RequirementName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SelectorName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TargetName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.ConsumerSelectionRelator;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Label;
+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.Relator;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.capabilities.Capability;
+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.has.qualities.Quality;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.requirements.Requirement;
+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.target.selector.quality.matcher.MatcherQuality;
+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.RequirementMatcher;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.requirement.matcher.MatcherRequirement;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.CapabilityMatcher;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.capability.matcher.MatcherCapability;
+
+/**
+ * Utilities related to matchers and labels
+ * @author readams
+ */
+public class MatcherUtils {
+    /**
+     * Apply a quality matcher to a normalized target
+     * @param matcher the {@link QualityMatcher} to apply
+     * @param target the {@link Target} to match against
+     * @return <code>true</code> if the matcher matches the target
+     */
+    public static boolean applyQualityMatcher(QualityMatcher matcher,
+                                              Target target) {
+        List<MatcherLabel<QualityName,TargetName>> mls = new ArrayList<>();
+        if (matcher.getMatcherQuality() != null) {
+            for (MatcherQuality ml : matcher.getMatcherQuality()) {
+                mls.add(new MatcherLabel<>(ml.getName(), 
+                        ml.getTargetNamespace()));
+            }
+        }
+        Set<QualityName> toMatch = new HashSet<>();
+        for (Quality q : target.getQuality()) {
+            toMatch.add(q.getName());
+        }
+        return applyLabelMatcher(mls, matcher.getMatchType(), 
+                                 toMatch, target.getName());
+    }
+
+    /**
+     * Apply a requirement matcher to a normalized consumer selection relator
+     * @param matcher the {@link RequirementMatcher} to apply
+     * @param target the {@link ConsumerSelectionRelator} to match against
+     * @return <code>true</code> if the matcher matches the target
+     */
+    public static boolean applyReqMatcher(RequirementMatcher matcher,
+                                          ConsumerSelectionRelator relator) {
+        List<MatcherLabel<RequirementName,SelectorName>> mls = new ArrayList<>();
+        if (matcher.getMatcherRequirement() != null) {
+            for (MatcherRequirement ml : matcher.getMatcherRequirement()) {
+                mls.add(new MatcherLabel<>(ml.getName(), 
+                                           ml.getSelectorNamespace()));
+            }
+        }
+        Set<RequirementName> toMatch = new HashSet<>();
+        for (Requirement q : relator.getRequirement()) {
+            toMatch.add(q.getName());
+        }
+        return applyLabelMatcher(mls, matcher.getMatchType(), 
+                                 toMatch, relator.getName());
+    }
+
+    /**
+     * Apply a capability matcher to a normalized provider selection relator
+     * @param matcher the {@link RequirementMatcher} to apply
+     * @param target the {@link ProviderSelectionRelator} to match against
+     * @return <code>true</code> if the matcher matches the target
+     */
+    public static boolean applyCapMatcher(CapabilityMatcher matcher,
+                                          ProviderSelectionRelator relator) {
+        List<MatcherLabel<CapabilityName,SelectorName>> mls = new ArrayList<>();
+        if (matcher.getMatcherCapability() != null) { 
+            for (MatcherCapability ml : matcher.getMatcherCapability()) {
+                mls.add(new MatcherLabel<>(ml.getName(), 
+                                           ml.getSelectorNamespace()));
+            }
+        }
+        Set<CapabilityName> toMatch = new HashSet<>();
+        for (Capability q : relator.getCapability()) {
+            toMatch.add(q.getName());
+        }
+        return applyLabelMatcher(mls, matcher.getMatchType(), 
+                                 toMatch, relator.getName());
+    }
+   
+    /**
+     * Functional interface used for generic label methods
+     * @author readams
+     *
+     * @param <L> The specific label type
+     * @param <LN> the related label name type
+     */
+    public interface GetLabelName<L extends Label, LN extends LabelName> {
+        /**
+         * Get the appropriate typed name for the given label
+         * @param label the label
+         * @return the name
+         */
+        public LN getName(L label);
+    }
+    
+    /**
+     * A {@link GetLabelName} for qualities
+     */
+    public static final GetLabelName<Quality, QualityName> getQualityName = 
+        new GetLabelName<Quality, QualityName>() {
+        @Override
+        public QualityName getName(Quality label) {
+            return label.getName();
+        }
+    };
+    
+    /**
+     * A {@link GetLabelName} for matcher qualities
+     */
+    public static final GetLabelName<MatcherQuality, QualityName> getMatcherQualityName = 
+            new GetLabelName<MatcherQuality, QualityName>() {
+        @Override
+        public QualityName getName(MatcherQuality label) {
+            return label.getName();
+        }
+    };
+    
+    /**
+     * A {@link GetLabelName} for requirements
+     */
+    public static final GetLabelName<Requirement, RequirementName> getRequirementName = 
+            new GetLabelName<Requirement, RequirementName>() {
+        @Override
+        public RequirementName getName(Requirement label) {
+            return label.getName();
+        }
+    };
+    
+    /**
+     * A {@link GetLabelName} for matcher requirements
+     */
+    public static final GetLabelName<MatcherRequirement, RequirementName> getMatcherRequirementName = 
+            new GetLabelName<MatcherRequirement, RequirementName>() {
+        @Override
+        public RequirementName getName(MatcherRequirement label) {
+            return label.getName();
+        }
+    };
+
+    /**
+     * A {@link GetLabelName} for capabilities
+     */
+    public static final GetLabelName<Capability, CapabilityName> getCapabilityName = 
+            new GetLabelName<Capability, CapabilityName>() {
+        @Override
+        public CapabilityName getName(Capability label) {
+            return label.getName();
+        }
+    };
+
+    /**
+     * A {@link GetLabelName} for matcher capabilities
+     */
+    public static final GetLabelName<MatcherCapability, CapabilityName> getMatcherCapabilityName = 
+            new GetLabelName<MatcherCapability, CapabilityName>() {
+        @Override
+        public CapabilityName getName(MatcherCapability label) {
+            return label.getName();
+        }
+    };
+
+    /**
+     * A {@link GetLabelName} for capabilities
+     */
+    public static final GetLabelName<Condition, ConditionName> getConditionName = 
+            new GetLabelName<Condition, ConditionName>() {
+        @Override
+        public ConditionName getName(Condition label) {
+            return label.getName();
+        }
+    };
+    
+    @Immutable
+    private static class MatcherLabel<LN extends LabelName, 
+                                      NS extends RelatorName> {
+        final LN name;
+        final NS namespace;
+
+        public MatcherLabel(LN name, NS namespace) {
+            super();
+            this.name = name;
+            this.namespace = namespace;
+        }
+    }
+    private static <LN extends LabelName, L extends Label, 
+             NS extends RelatorName, R extends Relator>         
+        boolean applyLabelMatcher(Collection<MatcherLabel<LN, NS>> matcherLabels,
+                                  MatchType matchType,
+                                  Set<LN> toMatch,
+                                  NS matchNamespace) {
+        int matches = 0;
+        int matchersize = 0;
+        if (matcherLabels != null) {
+            matchersize = matcherLabels.size();
+            for (MatcherLabel<LN, NS> matcherLabel : matcherLabels) {
+                if (matcherLabel.namespace != null && 
+                    !matcherLabel.namespace.equals(matchNamespace))
+                    continue;
+                if (!toMatch.contains(matcherLabel.name))
+                    continue;
+                matches += 1;
+            }
+        }
+        if (matchType == null) matchType = MatchType.All;
+        switch (matchType) {
+        case Any:
+            return matches > 0;
+        case None:
+            return matches == 0;
+        case All:
+        default:
+            return matches == matchersize;
+        }
+    }
+}
diff --git a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyCache.java b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyCache.java
new file mode 100644 (file)
index 0000000..7e90136
--- /dev/null
@@ -0,0 +1,260 @@
+package org.opendaylight.groupbasedpolicy.resolver.internal;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.annotation.concurrent.Immutable;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
+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.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.contract.subject.Rule;
+
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Ordering;
+import com.google.common.collect.Table;
+
+/**
+ * Represent the policy relationships between endpoint groups 
+ * @author readams
+ *
+ */
+class PolicyCache {
+    
+    /**
+     * 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<Table<EgKey, EgKey, Policy>> policy = 
+            new AtomicReference<>();    
+
+            
+    protected void updatePolicy(Table<EgKey, EgKey, Policy> newPolicy) {
+        Table<EgKey, EgKey, Policy> oldPolicy = policy.getAndSet(newPolicy);
+        
+        // TODO compute delta between old and new policy and notify listeners
+    }
+            
+    // **************
+    // Helper classes
+    // **************
+
+    @Immutable
+    protected static class Policy {
+        final Table<ConditionSet, ConditionSet, List<RuleGroup>> ruleMap;
+        public Policy(Table<ConditionSet, ConditionSet, List<RuleGroup>> ruleMap) {
+            super();
+            this.ruleMap = ruleMap;
+        }
+        @Override
+        public String toString() {
+            return "Policy [ruleMap=" + ruleMap + "]";
+        }
+    }
+
+    @Immutable
+    protected static class RuleGroup implements Comparable<RuleGroup> {
+        final List<Rule> rules;
+        final Integer order;
+        final Tenant contractTenant;
+        final Contract relatedContract;
+        final SubjectName relatedSubject; 
+
+        public RuleGroup(List<Rule> rules, Integer order,
+                         Tenant contractTenant, Contract contract,
+                         SubjectName subject) {
+            super();
+            this.rules = rules;
+            this.order = order;
+            this.contractTenant = contractTenant;
+            this.relatedContract = contract;
+            this.relatedSubject = subject;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((order == null) ? 0 : order.hashCode());
+            result = prime * result + ((rules == null) ? 0 : rules.hashCode());
+            result = prime * result +
+                     ((relatedSubject == null) ? 0 : relatedSubject.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;
+            RuleGroup other = (RuleGroup) obj;
+            if (order == null) {
+                if (other.order != null)
+                    return false;
+            } else if (!order.equals(other.order))
+                return false;
+            if (rules == null) {
+                if (other.rules != null)
+                    return false;
+            } else if (!rules.equals(other.rules))
+                return false;
+            if (relatedSubject == null) {
+                if (other.relatedSubject != null)
+                    return false;
+            } else if (!relatedSubject.equals(other.relatedSubject))
+                return false;
+            return true;
+        }
+
+        @Override
+        public int compareTo(RuleGroup o) {
+            return ComparisonChain.start()
+                .compare(order, o.order, 
+                         Ordering.natural().nullsLast())
+                .result();
+        }
+
+        @Override
+        public String toString() {
+            return "RuleGroup [rules=" + rules + ", order=" + order +
+                   ", contractTenant=" + contractTenant.getId() + 
+                   ", relatedContract=" + relatedContract.getId() + 
+                   ", relatedSubject=" + relatedSubject + "]";
+        }
+
+    }
+    
+    @Immutable
+    protected static class EgKey {
+        private final TenantId tenantId;
+        private final EndpointGroupId egId;
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((egId == null) ? 0 : egId.hashCode());
+            result = prime * result +
+                     ((tenantId == null) ? 0 : tenantId.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;
+            EgKey other = (EgKey) obj;
+            if (egId == null) {
+                if (other.egId != null)
+                    return false;
+            } else if (!egId.equals(other.egId))
+                return false;
+            if (tenantId == null) {
+                if (other.tenantId != null)
+                    return false;
+            } else if (!tenantId.equals(other.tenantId))
+                return false;
+            return true;
+        }
+        public EgKey(TenantId tenantId, EndpointGroupId egId) {
+            super();
+            this.tenantId = tenantId;
+            this.egId = egId;
+        }
+        @Override
+        public String toString() {
+            return "EgKey [tenantId=" + tenantId + ", egId=" + egId + "]";
+        }
+    }
+
+    /**
+     * Represents a set of conditions for endpoint groups.  For an endpoint
+     * to match the condition set, all the conditions in "all" must match,
+     * and none of the conditions in "none" can match.  Additionally, in
+     * each set of "any" conditions, at least one condition must match.
+     * Note that if all sets are empty, then the condition set matches 
+     * automatically
+     * @author readams
+     */
+    @Immutable
+    public static class ConditionSet {
+        private final Set<ConditionName> all;
+        private final Set<ConditionName> none;
+        private final Set<? extends Set<ConditionName>> any;
+        private final int hashCode;
+        
+        public static final ConditionSet EMPTY = 
+                new ConditionSet(Collections.<ConditionName>emptySet(),
+                                 Collections.<ConditionName>emptySet(),
+                                 Collections.<Set<ConditionName>>emptySet());
+        
+        public ConditionSet(Set<ConditionName> all,
+                            Set<ConditionName> none,
+                            Set<? extends Set<ConditionName>> any) {
+            super();
+            this.all = all;
+            this.none = none;
+            this.any = any;
+            this.hashCode = computeHashCode();
+        }
+        
+        private int computeHashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((all == null) ? 0 : all.hashCode());
+            result = prime * result + ((any == null) ? 0 : any.hashCode());
+            result = prime * result + ((none == null) ? 0 : none.hashCode());
+            return result;
+        }
+        
+        @Override
+        public int hashCode() {
+            return hashCode;
+        }
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            ConditionSet other = (ConditionSet) obj;
+            if (all == null) {
+                if (other.all != null)
+                    return false;
+            } else if (!all.equals(other.all))
+                return false;
+            if (any == null) {
+                if (other.any != null)
+                    return false;
+            } else if (!any.equals(other.any))
+                return false;
+            if (none == null) {
+                if (other.none != null)
+                    return false;
+            } else if (!none.equals(other.none))
+                return false;
+            return true;
+        }
+        @Override
+        public String toString() {
+            return "ConditionSet [all=" + all + ", none=" + none + ", any=" +
+                   any + "]";
+        }
+    }
+}
\ No newline at end of file
diff --git a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyResolutionException.java b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyResolutionException.java
new file mode 100644 (file)
index 0000000..2a44200
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.internal;
+
+/**
+ * An error in resolving policy
+ * @author readams
+ */
+public class PolicyResolutionException extends Exception {
+
+    private static final long serialVersionUID = -5737401204099404140L;
+
+    public PolicyResolutionException() {
+        super();
+    }
+
+    public PolicyResolutionException(String message, Throwable cause,
+                                     boolean enableSuppression,
+                                     boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+
+    public PolicyResolutionException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public PolicyResolutionException(String message) {
+        super(message);
+    }
+
+    public PolicyResolutionException(Throwable cause) {
+        super(cause);
+    }
+
+}
diff --git a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyResolver.java b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyResolver.java
new file mode 100644 (file)
index 0000000..c6e636d
--- /dev/null
@@ -0,0 +1,641 @@
+/*
+ * 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.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.annotation.concurrent.Immutable;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.groupbasedpolicy.resolver.PolicyListener;
+import org.opendaylight.groupbasedpolicy.resolver.PolicyResolverService;
+import org.opendaylight.groupbasedpolicy.resolver.PolicyScope;
+import org.opendaylight.groupbasedpolicy.resolver.internal.PolicyCache.ConditionSet;
+import org.opendaylight.groupbasedpolicy.resolver.internal.PolicyCache.EgKey;
+import org.opendaylight.groupbasedpolicy.resolver.internal.PolicyCache.Policy;
+import org.opendaylight.groupbasedpolicy.resolver.internal.PolicyCache.RuleGroup;
+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.EndpointGroupId;
+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.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.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.RequirementMatcher;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.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.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 com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Ordering;
+import com.google.common.collect.Table;
+import com.google.common.collect.Table.Cell;
+
+/**
+ * Implementation for {@link PolicyResolverService}
+ * @author readams
+ */
+public class PolicyResolver implements PolicyResolverService {
+    private final DataBrokerService dataProvider;
+
+    /**
+     *  Keep track of the current relevant policy scopes.
+     */
+    private CopyOnWriteArrayList<PolicyScopeImpl> scopes;
+    
+    private ConcurrentMap<TenantId, TenantContext> resolvedTenants;
+    
+    private PolicyCache policyCache = new PolicyCache();
+    
+    public PolicyResolver(DataBrokerService dataProvider) {
+        super();
+        this.dataProvider = dataProvider;
+        scopes = new CopyOnWriteArrayList<>();
+        resolvedTenants = new ConcurrentHashMap<>();
+    }
+
+    // *********************
+    // PolicyResolverService
+    // *********************
+
+    @Override
+    public List<Rule> getPolicy(EndpointGroupId ep1Group,
+                                Collection<Condition> ep1Conds,
+                                EndpointGroupId ep2Group,
+                                Collection<Condition> ep2Conds) {
+        return null;
+    }
+
+    @Override
+    public PolicyScope registerListener(PolicyListener listener) {
+        PolicyScopeImpl ps = new PolicyScopeImpl(this, listener);
+        scopes.add(ps);
+        
+        return ps;
+    }
+    
+    @Override
+    public void removeListener(PolicyScope scope) {
+        scopes.remove(scope);        
+    }
+
+    // *****************
+    // Protected methods
+    // *****************
+    
+    private void updateTenant(TenantId tenantId) {
+        TenantContext context = resolvedTenants.get(tenantId);
+        if (context == null) {
+            ListenerRegistration<DataChangeListener> registration = 
+                    dataProvider.registerDataChangeListener(TenantUtils.tenantIid(tenantId), 
+                                                            new PolicyChangeListener(tenantId));
+
+            context = new TenantContext(tenantId, 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;
+            }
+        }
+        // Resolve the new tenant and update atomically
+        boolean cont = true;
+        Tenant t = null;
+        while (cont) {
+            Tenant ot = context.tenant.get();
+            DataModificationTransaction transaction = 
+                    dataProvider.beginTransaction();
+            t = InheritanceUtils.resolveTenant(tenantId, transaction);
+            cont = !context.tenant.compareAndSet(ot, t);
+        }
+        
+        // Update the policy cache and notify listeners
+        Table<EgKey, EgKey, Policy> policy = resolvePolicy(t);        
+        policyCache.updatePolicy(policy);
+    }
+
+    /**
+     * 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<EgKey, EgKey, Policy> resolvePolicy(Tenant t) {
+        // select contracts that apply for the given tenant
+        Table<EgKey, EgKey, List<ContractMatch>> contractMatches =
+                selectContracts(t);
+        
+        // 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);
+    }
+    
+    /**
+     * Choose the contracts that are in scope for each pair of endpoint
+     * groups, then perform subject selection for the pair
+     */
+    protected Table<EgKey, EgKey, List<ContractMatch>> 
+        selectContracts(Tenant tenant) {
+        // For each endpoint group, match consumer selectors 
+        // against contracts to get a set of matching consumer selectors
+        Table<TenantId, ContractId, List<ConsumerContractMatch>> consumerMatches = 
+                HashBasedTable.create();
+        if (tenant.getEndpointGroup() == null) return HashBasedTable.create();
+        for (EndpointGroup group : tenant.getEndpointGroup()) {
+            List<ConsumerContractMatch> r = 
+                    matchConsumerContracts(tenant, group);
+            for (ConsumerContractMatch ccm : r) {
+                List<ConsumerContractMatch> 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.
+        Table<EgKey, EgKey, List<ContractMatch>> contractMatches = 
+                HashBasedTable.create();
+        for (EndpointGroup group : tenant.getEndpointGroup()) {
+            List<ContractMatch> 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<ContractMatch> egPairMatches =
+                        contractMatches.get(consumerKey, providerKey);
+                if (egPairMatches == null) {
+                    egPairMatches = new ArrayList<>();
+                    contractMatches.put(consumerKey, providerKey,
+                                        egPairMatches);
+                }
+
+                egPairMatches.add(cm);
+            }
+        }
+        return contractMatches;
+    }
+    
+    private boolean clauseMatches(Clause clause, ContractMatch match) {
+        if (clause.getConsumerMatchers() != null) {
+            List<RequirementMatcher> reqMatchers = 
+                    clause.getConsumerMatchers().getRequirementMatcher();
+            if (reqMatchers != null) {
+                for (RequirementMatcher reqMatcher : reqMatchers) {
+                    if (!MatcherUtils.applyReqMatcher(reqMatcher, 
+                                                      match.consumerRelator)) {
+                        return false;
+                    }
+                }
+            }
+        }
+        if (clause.getProviderMatchers() != null) {
+            List<CapabilityMatcher> capMatchers = 
+                    clause.getProviderMatchers().getCapabilityMatcher();
+            if (capMatchers != null) {
+                for (CapabilityMatcher capMatcher : capMatchers) {
+                    if (!MatcherUtils.applyCapMatcher(capMatcher, 
+                                                      match.providerRelator)) {
+                        return false;
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+    private ConditionSet buildConditionSet(List<ConditionMatcher> condMatchers) {
+        if (condMatchers == null) return ConditionSet.EMPTY;
+
+        ImmutableSet.Builder<ConditionName> allb = ImmutableSet.builder();
+        ImmutableSet.Builder<ConditionName> noneb = ImmutableSet.builder();
+        ImmutableSet.Builder<Set<ConditionName>> 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<ConditionName> 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<ConditionMatcher> condMatchers =
+                    clause.getConsumerMatchers().getConditionMatcher();
+            return buildConditionSet(condMatchers);
+        }
+        return ConditionSet.EMPTY;
+    }
+
+    private ConditionSet buildProvConditionSet(Clause clause) {
+        if (clause.getProviderMatchers() != null) {
+            List<ConditionMatcher> condMatchers =
+                    clause.getProviderMatchers().getConditionMatcher();
+            return buildConditionSet(condMatchers);
+        }
+        return ConditionSet.EMPTY;
+    }
+    
+    private Policy resolvePolicy(Tenant contractTenant,
+                                 Contract contract,
+                                 Policy merge,
+                                 Table<ConditionSet, ConditionSet, List<Subject>> subjectMap) {
+        Table<ConditionSet, ConditionSet, List<RuleGroup>> ruleMap = 
+                HashBasedTable.create();
+        if (merge != null) {
+            ruleMap.putAll(merge.ruleMap);
+        }
+        for (Cell<ConditionSet, ConditionSet, List<Subject>> entry : 
+                subjectMap.cellSet()) {
+            List<RuleGroup> rules = new ArrayList<>();
+            List<RuleGroup> oldrules = 
+                    ruleMap.get(entry.getRowKey(), entry.getColumnKey());
+            if (oldrules != null) {
+                rules.addAll(oldrules);
+            }
+            for (Subject s : entry.getValue()) {
+                if (s.getRule() == null) continue;
+                List<Rule> 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(entry.getRowKey(), entry.getColumnKey(), 
+                        Collections.unmodifiableList(rules));
+        }
+        return new Policy(ruleMap);
+    }
+    
+    /**
+     * Choose the set of subjects that in scope for each possible set of 
+     * endpoint conditions
+     */
+    protected Table<EgKey, EgKey, Policy> 
+            selectSubjects(Table<EgKey, EgKey, 
+                                 List<ContractMatch>> contractMatches) {
+        // 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<EgKey, EgKey, Policy> policy = HashBasedTable.create();
+
+        for (List<ContractMatch> matches : contractMatches.values()) {
+            for (ContractMatch match : matches) {
+                List<Clause> clauses = match.contract.getClause();
+                if (clauses == null) continue;
+
+                List<Subject> 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());
+                Policy existing = policy.get(ckey, pkey);
+                boolean alreadyMatched = false;
+                if (existing != null) {
+                    for (List<RuleGroup> rgl : existing.ruleMap.values()) {
+                        for (RuleGroup rg : rgl) {
+                            if (rg.relatedContract == match.contract) {
+                                alreadyMatched = true;
+                                break;
+                            }
+                        }
+                        if (alreadyMatched) break;
+                    }
+                    if (alreadyMatched) continue;
+                }
+                
+                HashMap<SubjectName, Subject> subjects = new HashMap<>();
+                for (Subject s : subjectList) {
+                    subjects.put(s.getName(), s);
+                }
+                
+                Table<ConditionSet, ConditionSet, List<Subject>> subjectMap = 
+                        HashBasedTable.create();
+                
+                for (Clause clause : clauses) {
+                    if (clause.getSubjectRefs() != null &&
+                        clauseMatches(clause, match)) {
+                        ConditionSet consCSet = buildConsConditionSet(clause);
+                        ConditionSet provCSet = buildProvConditionSet(clause);
+                        List<Subject> 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(ckey, pkey, 
+                           resolvePolicy(match.contractTenant, 
+                                         match.contract,
+                                         existing, 
+                                         subjectMap));
+            }
+        }
+        
+        return policy;
+    }
+    
+    private List<ConsumerContractMatch> matchConsumerContracts(Tenant tenant,
+                                                               EndpointGroup consumer) {
+        List<ConsumerContractMatch> 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<ContractMatch> matches,
+                                      List<ConsumerContractMatch> 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<ContractMatch> 
+        matchProviderContracts(Tenant tenant, EndpointGroup provider,
+                               Table<TenantId, 
+                                     ContractId, 
+                                     List<ConsumerContractMatch>> consumerMatches) {
+        List<ContractMatch> 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<ConsumerContractMatch> 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<ConsumerContractMatch> cMatches = 
+                                    consumerMatches.get(tenant.getId(), 
+                                                        c.getId());
+                            amendContractMatches(matches, cMatches, tenant, 
+                                                 provider, pts);
+
+                        }
+                    }
+                }
+            }
+        }
+        return matches;
+    }
+
+    private static class TenantContext {
+        TenantId tenantId;
+        ListenerRegistration<DataChangeListener> registration;
+
+        AtomicReference<Tenant> tenant = new AtomicReference<Tenant>();
+        
+        public TenantContext(TenantId tenantId,
+                             ListenerRegistration<DataChangeListener> registration) {
+            super();
+            this.tenantId = tenantId;
+            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(DataChangeEvent<InstanceIdentifier<?>, 
+                                                  DataObject> change) {
+            updateTenant(tenantId);
+        }
+        
+    }
+}
diff --git a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyScopeImpl.java b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyScopeImpl.java
new file mode 100644 (file)
index 0000000..de93901
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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.internal;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.opendaylight.groupbasedpolicy.resolver.PolicyListener;
+import org.opendaylight.groupbasedpolicy.resolver.PolicyResolverService;
+import org.opendaylight.groupbasedpolicy.resolver.PolicyScope;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
+
+/**
+ * Implementation for {@link PolicyScope}
+ * @author readams
+ */
+public class PolicyScopeImpl implements PolicyScope {
+    /**
+     * The parent resolver for this scope.
+     */
+    private final PolicyResolverService resolver;
+    
+    /**
+     * The listener for this policy scope
+     */
+    private final PolicyListener listener;
+
+    /**
+     * The set of policy scope elements that we want to listen to.
+     */
+    private Set<PolicyScopeElement> scopeElements;
+    
+    public PolicyScopeImpl(PolicyResolverService resolver, 
+                           PolicyListener listener) {
+        super();
+        this.resolver = resolver;
+        this.listener = listener;
+        Map<PolicyScopeElement,Boolean> smap = new ConcurrentHashMap<>();
+        scopeElements = Collections.newSetFromMap(smap);
+    }
+
+    // ***********
+    // PolicyScope
+    // ***********
+
+    @Override
+    public void addToScope(TenantId tenant, EndpointGroupId endpointGroup) {
+        scopeElements.add(new PolicyScopeElement(tenant, endpointGroup));
+    }
+
+    @Override
+    public void addToScope(TenantId tenant) {
+        scopeElements.add(new PolicyScopeElement(tenant, null));        
+    }
+    
+    // ***************
+    // PolicyScopeImpl
+    // ***************
+
+    protected Set<PolicyScopeElement> getScopeElements() {
+        return scopeElements;
+    }
+    
+    protected static class PolicyScopeElement {
+        private final TenantId tenant;
+        private final EndpointGroupId endpointGroup;
+        public PolicyScopeElement(TenantId tenant, 
+                                  EndpointGroupId endpointGroup) {
+            super();
+            this.tenant = tenant;
+            this.endpointGroup = endpointGroup;
+        }
+        public TenantId getTenant() {
+            return tenant;
+        }
+        public EndpointGroupId getEndpointGroup() {
+            return endpointGroup;
+        }
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result +
+                     ((endpointGroup == null) ? 0 : endpointGroup.hashCode());
+            result = prime * result + ((tenant == null) ? 0 : tenant.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;
+            PolicyScopeElement other = (PolicyScopeElement) obj;
+            if (endpointGroup == null) {
+                if (other.endpointGroup != null)
+                    return false;
+            } else if (!endpointGroup.equals(other.endpointGroup))
+                return false;
+            if (tenant == null) {
+                if (other.tenant != null)
+                    return false;
+            } else if (!tenant.equals(other.tenant))
+                return false;
+            return true;
+        }
+    }
+
+}
diff --git a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/TenantUtils.java b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/internal/TenantUtils.java
new file mode 100644 (file)
index 0000000..5416ba0
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * 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.internal;
+
+import java.util.Comparator;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClauseName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContractId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SelectorName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubjectName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TargetName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Tenants;
+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.TenantKey;
+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.subject.Rule;
+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.yang.binding.InstanceIdentifier;
+
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.Ordering;
+
+/**
+ * Static methods for manipulating group-based policy tenants
+ * @author readams
+ */
+public class TenantUtils {
+    /**
+     * A comparator that assigns the natural ordering for rules, null-aware
+     * @author readams
+     */
+    public static class RuleComparator implements Comparator<Rule> {
+    
+        @Override
+        public int compare(Rule o1, Rule o2) {
+            return ComparisonChain.start()
+                .compare(o1.getOrder(), o2.getOrder(), 
+                         Ordering.natural().nullsLast())
+                .result();
+        }
+        
+    }
+
+    /**
+     * An instance of RuleComparator
+     */
+    public static final RuleComparator RULE_COMPARATOR = new RuleComparator();
+
+    /**
+     * Generate an {@link InstanceIdentifier} for an {@link Tenant}
+     * @param tenantKey a tenant key
+     * @return the {@link InstanceIdentifier}
+     */
+    public static InstanceIdentifier<Tenant> tenantIid(TenantKey tenantKey) {
+        return InstanceIdentifier.builder(Tenants.class)
+                .child(Tenant.class, tenantKey)
+                .build();
+    }
+
+    /**
+     * Generate an {@link InstanceIdentifier} for an {@link Tenant}
+     * @param tenantId a tenant id
+     * @return the {@link InstanceIdentifier}
+     */
+    public static InstanceIdentifier<Tenant> tenantIid(TenantId tenantId) {
+        return tenantIid(new TenantKey(tenantId));
+    }
+    
+    /**
+     * Find a contract with a specified ID within a tenant
+     * @param tenant the {@link Tenant} to search
+     * @param contractId the {@link ContractId} to search for
+     * @return the {@link Contract} if it exists, null otherwise
+     */
+    public static Contract findContract(Tenant tenant, 
+                                        ContractId contractId) {
+        if (tenant.getContract() != null) {
+            for (Contract c : tenant.getContract()) {
+                if (contractId.equals(c.getId())) {
+                    return c;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Find a clause with a specified name within a contract
+     * @param tenant the {@link Contract} to search
+     * @param contractId the {@link ClauseName} to search for
+     * @return the {@link Clause} if it exists, null otherwise
+     */
+    public static Clause findClause(Contract contract, 
+                                    ClauseName clauseName) {
+        if (contract.getClause() != null) {
+            for (Clause c : contract.getClause()) {
+                if (clauseName.equals(c.getName())) {
+                    return c;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Find a subject with a specified name within a contract
+     * @param tenant the {@link Contract} to search
+     * @param subjectName the {@link SubjectName} to search for
+     * @return the {@link Subject} if it exists, null otherwise
+     */
+    public static Subject findSubject(Contract contract, 
+                                      SubjectName subjectName) {
+        if (contract.getSubject() != null) {
+            for (Subject c : contract.getSubject()) {
+                if (subjectName.equals(c.getName())) {
+                    return c;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Find a target with a specified name within a contract
+     * @param contract the {@link Contract} to search
+     * @param targetName the {@link TargetName} to search for
+     * @return the {@link Target} if it exists, null otherwise
+     */
+    public static Target findTarget(Contract contract, 
+                                    TargetName targetName) {
+        if (contract.getTarget() != null) {
+            for (Target t : contract.getTarget()) {
+                if (targetName.equals(t.getName())) {
+                    return t;
+                }
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Find an endpoint group with a specified ID within a tenant
+     * @param tenant the {@link Tenant} to search
+     * @param egId the {@link EndpointGroupId} to search for
+     * @return the {@link EndpointGroup} if it exists, null otherwise
+     */
+    public static EndpointGroup findEndpointGroup(Tenant tenant, 
+                                                  EndpointGroupId egId) {
+        if (tenant.getEndpointGroup() != null) {
+            for (EndpointGroup eg : tenant.getEndpointGroup()) {
+                if (egId.equals(eg.getId())) {
+                    return eg;
+                }
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Find a consumer named selector in an endpoint group
+     * @param eg the {@link EndpointGroup} to search
+     * @param name the {@link NamedSelectorName} to search for
+     * @return the {@link ConsumerNamedSelector} if it exists, null otherwise
+     */
+    public static ConsumerNamedSelector findCns(EndpointGroup eg,
+                                                SelectorName name) {
+        if (eg.getConsumerNamedSelector() != null) {
+            for (ConsumerNamedSelector s : eg.getConsumerNamedSelector()) {
+                if (name.equals(s.getName()))
+                    return s;
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Find a consumer target selector in an endpoint group
+     * @param eg the {@link EndpointGroup} to search
+     * @param name the {@link SelectorName} to search for
+     * @return the {@link ConsumerNamedSelector} if it exists, null otherwise
+     */
+    public static ConsumerTargetSelector findCts(EndpointGroup eg,
+                                                 SelectorName name) {
+        if (eg.getConsumerTargetSelector() != null) {
+            for (ConsumerTargetSelector s : eg.getConsumerTargetSelector()) {
+                if (name.equals(s.getName()))
+                    return s;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Find a provider named selector in an endpoint group
+     * @param eg the {@link EndpointGroup} to search
+     * @param name the {@link NamedSelectorName} to search for
+     * @return the {@link ProviderNamedSelector} if it exists, null otherwise
+     */
+    public static ProviderNamedSelector findPns(EndpointGroup eg,
+                                                SelectorName name) {
+        if (eg.getProviderNamedSelector() != null) {
+            for (ProviderNamedSelector s : eg.getProviderNamedSelector()) {
+                if (name.equals(s.getName()))
+                    return s;
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Find a provider target selector in an endpoint group
+     * @param eg the {@link EndpointGroup} to search
+     * @param name the {@link TargetSelectorName} to search for
+     * @return the {@link ProviderNamedSelector} if it exists, null otherwise
+     */
+    public static ProviderTargetSelector findPts(EndpointGroup eg,
+                                                 SelectorName name) {
+        if (eg.getProviderTargetSelector() != null) {
+            for (ProviderTargetSelector s : eg.getProviderTargetSelector()) {
+                if (name.equals(s.getName()))
+                    return s;
+            }
+        }
+        return null;
+    }    
+}
index 2a182bb5d777f491efe2bf0f39ebb0bdcee4cd0a..d5c2e231e841a46ae1119c856a0fa2d27aaaa5c1 100644 (file)
@@ -74,16 +74,6 @@ module gbp-common {
         description "A name for a selector.";
     }
 
-    typedef named-selector-name {
-        type selector-name;
-        description "A name for a named selector.";
-    }
-
-    typedef target-selector-name {
-        type selector-name;
-        description "A name for a target selector.";
-    }
-
     // *****************
     // Unique object IDs
     // *****************
@@ -113,29 +103,33 @@ module gbp-common {
         description "A unique ID for a contract";
     }
 
-    typedef matcher-id {
-        type unique-id;
-        description "A unique ID for a matcher";
+    // *************
+    // Matcher names
+    // *************
+
+    typedef matcher-name {
+        type name;
+        description "A name for a matcher";
     }
 
-    typedef quality-matcher-id {
-        type matcher-id;
-        description "A unique ID for a quality matcher";
+    typedef quality-matcher-name {
+        type matcher-name;
+        description "A name for a quality matcher";
     }
 
-    typedef requirement-matcher-id {
-        type matcher-id;
-        description "A unique ID for a  matcher";
+    typedef requirement-matcher-name {
+        type matcher-name;
+        description "A name for a requirement matcher";
     }
 
-    typedef condition-matcher-id {
-        type matcher-id;
-        description "A unique ID for a condition matcher";
+    typedef condition-matcher-name {
+        type matcher-name;
+        description "A name for a condition matcher";
     }
 
-    typedef capability-matcher-id {
-        type matcher-id;
-        description "A unique ID for a capability matcher";
+    typedef capability-matcher-name {
+        type matcher-name;
+        description "A name for a capability matcher";
     }
 
     // *************
index e391f05f3311915a8d552ed8afbb1396c8c7f6e3..d7b91fe97de0e86589ca3c9528b36b18b33fc27d 100644 (file)
@@ -52,27 +52,6 @@ module policy {
         }
     }
 
-    typedef name-inheritance {
-        description 
-            "Determines how names are inherited by matchers.  Names are 
-             used by matchers to narrow the scope of labels against
-             which they match to only particular targets or selectors.";
-
-        type enumeration {
-            enum inherit {
-                description 
-                    "Inherit the name parameter from the parent
-                     matcher.  If there is no parent matcher, then
-                     acts as if 'specified' were selected.";
-            }
-            enum specified {
-                description 
-                    "Match against only the specied target or selector
-                     name, as appropriate for the context.";
-            }
-        }
-    }
-
     grouping quality-base {
         description "Base type for qualities and matcher qualities.";
         leaf name {
@@ -231,6 +210,24 @@ module policy {
         }
     }
 
+    grouping has-condition-matchers {
+        description "Objects with condition matchers";
+        list condition-matcher {
+            description "Match against conditions on endpoints.";
+                
+            key "name";
+            leaf name {
+                description 
+                    "A name for the condition matcher";
+                type gbp-common:condition-matcher-name;
+                mandatory true;
+            }
+                
+            uses matcher;
+            uses has-conditions;
+        }
+    }
+
     // ************************
     // Endpoint group groupings
     // ************************
@@ -248,6 +245,12 @@ module policy {
             "Selection relators allow endpoint groups to select 
              contracts based on their name or on their qualities.";
         uses relator;
+
+        leaf name {
+            description "A name for the selector";
+            type gbp-common:selector-name;
+            mandatory true;
+        }
     }
 
     grouping consumer-selection-relator {
@@ -255,7 +258,7 @@ module policy {
             "A selection relator for matching contracts to meet
              endpoint group requirements.  See consumer-named-selector
              and consumer-target-selector for more details.";
-
+        uses selection-relator;
         uses has-requirements;
     }
 
@@ -264,7 +267,7 @@ module policy {
             "A selection relator for matching contracts to meet
              endpoint group requirements.  See provider-named-selector
              and provider-named-selector for more details.";
-
+        uses selection-relator;
         uses has-capabilities;
     }
 
@@ -283,11 +286,9 @@ module policy {
              endpoint group, and provider named selectors select
              contracts to provide capabilities for the endpoint group.";
 
-        uses selection-relator;
-
         leaf name {
             description "A name for the named selector.";
-            type gbp-common:named-selector-name;
+            type gbp-common:selector-name;
             mandatory true;
         }
 
@@ -315,14 +316,6 @@ module policy {
              contracts to provide capabilities to other endpoint
              groups.";
 
-        uses selection-relator;
-
-        leaf name {
-            description "A name for the target selector";
-            type gbp-common:target-selector-name;
-            mandatory true;
-        }
-
         list quality-matcher {
             description 
                 "A quality matcher is used in a target selector to
@@ -332,10 +325,10 @@ module policy {
                  match or if a match can be made with any of the
                  qualities.";
 
-            key "id";
-            leaf id {
-                description "A unique ID for the quality matcher";
-                type gbp-common:quality-matcher-id;
+            key "name";
+            leaf name {
+                description "A unique name for the quality matcher";
+                type gbp-common:quality-matcher-name;
                 mandatory true;
             }
 
@@ -351,23 +344,15 @@ module policy {
                 key name;
                 uses quality-base;
 
-                leaf name-inheritance {
-                    description
-                        "Determine how the target name should be inherited
-                         from any parent matchers.";
-
-                    type name-inheritance;
-                    default inherit;
-                }
-
-                leaf target-name {
+                leaf target-namespace {
                     description
                         "The name of the target to match.  This allows
                          us to specify that we want to match only
                          qualities scoped to a particular target name.
 
                          This parameter is optional; if not specified,
-                         match against any selector name.";
+                         use an inherited target name if it exists, or
+                         match against any target name.";
 
                     type gbp-common:target-name;
                 }
@@ -944,6 +929,7 @@ module policy {
                          An active subject can then apply its rules to
                          the traffic between the endpoints.";
 
+                    key "name";
                     leaf name {
                         description "A name for the subject";
                         type gbp-common:subject-name;
@@ -1016,11 +1002,11 @@ module policy {
                                 "Match against requirements in the consumer 
                                  endpoint group.";
                 
-                            key "id";
-                            leaf id {
+                            key "name";
+                            leaf name {
                                 description 
-                                    "A unique ID for the requirement matcher";
-                                type gbp-common:requirement-matcher-id;
+                                    "A name for the requirement matcher";
+                                type gbp-common:requirement-matcher-name;
                                 mandatory true;
                             }
                 
@@ -1038,54 +1024,25 @@ module policy {
                                 key name;
                                 uses requirement-base;
 
-                                leaf name-inheritance {
-                                    description 
-                                        "Determine how the selector 
-                                         name should be inherited from
-                                         any parent matchers.";
-                                    type name-inheritance;
-                                    default inherit;
-                                }
-                                leaf selector-name {
+                                leaf selector-namespace {
                                     description 
                                         "The name of the selector to  
                                          match.  This allows us to
                                          specify that we want to match
                                          only requirements scoped to a
                                          particular selector name.
+
                                          This parameter is optional;
-                                         if not specified, match
+                                         if not specified, use an
+                                         inherited selector namespace
+                                         if it exists, or match
                                          against any selector name.";
                 
                                     type gbp-common:selector-name;
                                 }
                             }
                         }
-                
-                        list consumer-condition-matcher {
-                            description 
-                                "Match against conditions on endpoints in the 
-                                 consumer endpoint group.";
-                
-                            key "id";
-                            leaf id {
-                                description 
-                                    "A unique ID for the condition matcher";
-                                type gbp-common:condition-matcher-id;
-                                mandatory true;
-                            }
-                
-                            uses matcher;
-                            uses has-conditions;
-                            leaf parent {
-                                description 
-                                    "Parent condition matcher from which
-                                     we inherit.";
-                                type leafref {
-                                    path "/tenants/tenant/contract/clause/consumer-matchers/consumer-condition-matcher/id";
-                                }
-                            }
-                        }
+                        uses has-condition-matchers;
                     }
                 
                     container provider-matchers {
@@ -1098,11 +1055,11 @@ module policy {
                                 "Match against capabilities in the provider  
                                  endpoint group.";
                 
-                            key "id";
-                            leaf id {
+                            key "name";
+                            leaf name {
                                 description 
-                                    "A unique ID for the capability matcher";
-                                type gbp-common:capability-matcher-id;
+                                    "A name for the capability matcher";
+                                type gbp-common:capability-matcher-name;
                                 mandatory true;
                             }
                 
@@ -1120,55 +1077,26 @@ module policy {
 
                                 key name;
                                 uses capability-base;
-                                leaf name-inheritance {
-                                    description 
-                                        "Determine how the selector 
-                                         name should be inherited from
-                                         any parent matchers.";
-                                    type name-inheritance;
-                                    default inherit;
-                                }
-                                leaf selector-name {
+
+                                leaf selector-namespace {
                                     description 
                                         "The name of the selector to
                                          match.  This allows us to
                                          specify that we want to match
                                          only capabilities scoped to a
                                          particular selector name.
+
                                          This parameter is optional;
-                                         if not specified, match
+                                         if not specified, use an
+                                         inherited selector namespace
+                                         if it exists, or match
                                          against any selector name.";
                 
                                     type gbp-common:selector-name;
                                 }
                             }
                         }
-                
-                        list provider-condition-matcher {
-                            description 
-                                "Match against conditions on endpoints in the 
-                                 provider endpoint group.";
-                
-                            key "id";
-                            leaf id {
-                                description 
-                                    "A unique ID for the condition matcher";
-                                type gbp-common:condition-matcher-id;
-                                mandatory true;
-                            }
-                
-                            uses matcher;
-                            uses has-conditions;
-
-                            leaf parent {
-                                description 
-                                    "Parent condition matcher from which
-                                     we inherit.";
-                                type leafref {
-                                    path "/tenants/tenant/contract/clause/provider-matchers/provider-condition-matcher/id";
-                                }
-                            }
-                        }
+                        uses has-condition-matchers;
                     }
                 
                     // ******************
@@ -1199,7 +1127,7 @@ module policy {
                 } 
             }
                 
-            list contract-refs {
+            list contract-ref {
                 description 
                     "Contract references allow forming contracts
                      between endpoint groups in different tenants.
diff --git a/groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/resolver/internal/InheritanceUtilsTest.java b/groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/resolver/internal/InheritanceUtilsTest.java
new file mode 100644 (file)
index 0000000..90f3eb6
--- /dev/null
@@ -0,0 +1,1126 @@
+/*
+ * 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.internal;
+
+import java.util.HashSet;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.CapabilityMatcherName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.CapabilityName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClauseName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionMatcherName;
+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.EndpointGroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.QualityMatcherName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.QualityName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RequirementMatcherName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RequirementName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RuleName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SelectorName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubjectName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TargetName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.CapabilityBase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Label.InclusionRule;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Matcher.MatchType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.QualityBase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.RequirementBase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.action.refs.ActionRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.action.refs.ActionRefBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.capabilities.Capability;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.capabilities.CapabilityBuilder;
+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.condition.matchers.ConditionMatcherBuilder;
+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.has.conditions.ConditionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.qualities.Quality;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.qualities.QualityBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.requirements.Requirement;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.requirements.RequirementBuilder;
+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.target.selector.QualityMatcherBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.target.selector.quality.matcher.MatcherQuality;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.target.selector.quality.matcher.MatcherQualityBuilder;
+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.TenantBuilder;
+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.ContractBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.ContractKey;
+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.EndpointGroupBuilder;
+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.ClauseBuilder;
+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.SubjectBuilder;
+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.TargetBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.ConsumerMatchersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.ProviderMatchersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.RequirementMatcher;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.RequirementMatcherBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.requirement.matcher.MatcherRequirementBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.CapabilityMatcher;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.CapabilityMatcherBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.capability.matcher.MatcherCapabilityBuilder;
+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.ConsumerNamedSelectorBuilder;
+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.ConsumerTargetSelectorBuilder;
+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.ProviderNamedSelectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderTargetSelector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderTargetSelectorBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+import static org.junit.Assert.*;
+
+import static org.mockito.Mockito.*;
+
+public class InheritanceUtilsTest {
+    // ******
+    // Labels
+    // ******
+    
+    Quality q1 = new QualityBuilder()
+        .setName(new QualityName("q1"))
+        .build();
+    Quality q1Include = new QualityBuilder(q1)
+        .setInclusionRule(InclusionRule.Include)
+        .build();
+    Quality q1Exclude = new QualityBuilder(q1)
+        .setInclusionRule(InclusionRule.Exclude)
+        .build();
+    Quality q2 = new QualityBuilder()
+        .setName(new QualityName("q2"))
+        .build();
+    Quality q2Exclude = new QualityBuilder()
+        .setName(new QualityName("q2"))
+        .setInclusionRule(InclusionRule.Exclude)
+        .build();
+    Quality q3 = new QualityBuilder()
+        .setName(new QualityName("q3"))
+        .build();
+
+    Requirement r1 = new RequirementBuilder()
+        .setName(new RequirementName("r1"))
+        .build();
+    Requirement r2 = new RequirementBuilder()
+        .setName(new RequirementName("r2"))
+        .build();
+    Requirement r1exclude = new RequirementBuilder()
+        .setName(new RequirementName("r1"))
+        .setInclusionRule(InclusionRule.Exclude)
+        .build();
+    Requirement r3 = new RequirementBuilder()
+        .setName(new RequirementName("r3"))
+        .build();
+
+    Capability c1 = new CapabilityBuilder()
+        .setName(new CapabilityName("c1"))
+        .build();
+    Capability c2 = new CapabilityBuilder()
+        .setName(new CapabilityName("c2"))
+        .build();
+    Capability c1exclude = new CapabilityBuilder()
+        .setName(new CapabilityName("c1"))
+        .setInclusionRule(InclusionRule.Exclude)
+        .build();
+    Capability c3 = new CapabilityBuilder()
+        .setName(new CapabilityName("c3"))
+        .build();
+    
+    Condition cond1 = new ConditionBuilder()
+        .setName(new ConditionName("cond1"))
+        .build();
+    Condition cond2 = new ConditionBuilder()
+        .setName(new ConditionName("cond2"))
+        .build();
+    Condition cond2exlude = new ConditionBuilder()
+        .setName(new ConditionName("cond2"))
+        .setInclusionRule(InclusionRule.Exclude)
+        .build();
+
+    // *********
+    // Contracts
+    // *********
+
+    TargetName q2TargetName = new TargetName("q2");
+    Target q2Target = new TargetBuilder()
+        .setName(q2TargetName)
+        .setQuality(ImmutableList.of(q2))
+        .build();
+    
+    TargetName q1ExcludeTargetName = new TargetName("q1_exclude");
+    Target q1ExcludeTarget = new TargetBuilder()
+        .setName(q1ExcludeTargetName)
+        .setQuality(ImmutableList.of(q1Exclude, q2))
+        .build();
+    
+    TargetName q1IncludeTargetName = new TargetName("q1_include");
+    Target q1IncludeTarget = new TargetBuilder()
+        .setName(q1IncludeTargetName)
+        .setQuality(ImmutableList.of(q1Include))
+        .build();
+
+    Target q2PlusTarget = new TargetBuilder()
+        .setName(q2TargetName)
+        .setQuality(ImmutableList.of(q3))
+        .build();
+
+    SubjectName subject1 = new SubjectName("subject1");
+    SubjectName subject2 = new SubjectName("subject2");
+    
+    RequirementMatcher rm_r1 = new RequirementMatcherBuilder()
+        .setName(new RequirementMatcherName("rm_r1"))
+        .setMatcherRequirement(ImmutableList.of(new MatcherRequirementBuilder(r1)
+                                                    .build()))
+        .build();
+    RequirementMatcher rm_r1_plus = new RequirementMatcherBuilder()
+        .setName(new RequirementMatcherName("rm_r1"))
+        .setMatchType(MatchType.All)
+        .setMatcherRequirement(ImmutableList.of(new MatcherRequirementBuilder(r2)
+                                                    .build()))
+        .build();
+
+    CapabilityMatcher capm_c1 = new CapabilityMatcherBuilder()
+        .setName(new CapabilityMatcherName("capm_c1"))
+        .setMatcherCapability(ImmutableList.of(new MatcherCapabilityBuilder(c1)
+                                                .build()))
+        .build();
+
+    ConditionMatcher cm_c1 = new ConditionMatcherBuilder()
+        .setName(new ConditionMatcherName("cm_c1"))
+        .setCondition(ImmutableList.of(cond1))
+        .build();
+    ConditionMatcher cm_c2 = new ConditionMatcherBuilder()
+        .setName(new ConditionMatcherName("cm_c2"))
+        .setMatchType(MatchType.All)
+        .setCondition(ImmutableList.of(cond2))
+        .build();
+    ConditionMatcher cm_c2_plus = new ConditionMatcherBuilder()
+        .setName(new ConditionMatcherName("cm_c2"))
+        .setCondition(ImmutableList.of(cond2exlude))
+        .build();
+    
+    ClauseName clauseName1 = new ClauseName("clauseName1");
+    Clause clause1 = new ClauseBuilder()
+        .setName(clauseName1)
+        .setSubjectRefs(ImmutableList.of(subject1))
+        .setProviderMatchers(new ProviderMatchersBuilder()
+            .setCapabilityMatcher(ImmutableList.of(capm_c1))
+            .setConditionMatcher(ImmutableList.of(cm_c1))
+            .build())
+        .setConsumerMatchers(new ConsumerMatchersBuilder()
+            .setRequirementMatcher(ImmutableList.of(rm_r1))
+            .setConditionMatcher(ImmutableList.of(cm_c2))
+            .build())
+        .build();
+
+    Clause clause1plus = new ClauseBuilder()
+        .setName(clauseName1)
+        .setSubjectRefs(ImmutableList.of(subject2))
+        .setConsumerMatchers(new ConsumerMatchersBuilder()
+            .setRequirementMatcher(ImmutableList.of(rm_r1_plus))
+            .setConditionMatcher(ImmutableList.of(cm_c2_plus))
+            .build())
+        .build();
+
+    ActionRef a1 = new ActionRefBuilder()
+        .setName(new ActionName("a1"))
+        .build();
+    ClassifierRef cr1 = new ClassifierRefBuilder()
+        .setName(new ClassifierName("cr1"))
+        .build();
+    Rule rule1 = new RuleBuilder()
+        .setName(new RuleName("r1"))
+        .setActionRef(ImmutableList.of(a1))
+        .setClassifierRef(ImmutableList.of(cr1))
+        .build();
+    Rule rule2 = new RuleBuilder()
+        .setName(new RuleName("r2"))
+        .setOrder(5)
+        .build();
+    Rule rule3 = new RuleBuilder()
+        .setName(new RuleName("r3"))
+        .setOrder(7)
+        .build();
+    Rule rule4 = new RuleBuilder()
+        .setName(new RuleName("r4"))
+        .setOrder(1)
+        .build();
+
+    Subject s1 = new SubjectBuilder()
+        .setName(new SubjectName("s1"))
+        .setRule(ImmutableList.of(rule1, rule2))
+        .build();
+    Subject s1_plus = new SubjectBuilder()
+        .setName(s1.getName())
+        .setRule(ImmutableList.of(rule3, rule4))
+        .setOrder(4)
+        .build();
+    Subject s2 = new SubjectBuilder()
+        .setName(new SubjectName("s2"))
+        .setOrder(5)
+        .build();
+
+    ContractId contractId1 = 
+            new ContractId("e7e6804f-7fcb-46cf-9bc6-abfec0896d95");
+    Contract contract1 = new ContractBuilder()
+        .setId(contractId1)
+        .setQuality(ImmutableList.of(q1))
+        .setTarget(ImmutableList.of(q2Target, 
+                                    q1IncludeTarget, 
+                                    q1ExcludeTarget))
+        .setClause(ImmutableList.of(clause1))
+        .setSubject(ImmutableList.of(s1))
+        .build();
+
+    ContractId contractId2 = 
+            new ContractId("3f56ae44-d1e4-4617-95af-c809dfc50149");
+    ContractKey contractKey2 = new ContractKey(contractId2);
+    Contract contract2 = new ContractBuilder()
+        .setId(contractId2)
+        .setParent(contractId1)
+        .setTarget(ImmutableList.of(q2PlusTarget, q1IncludeTarget))
+        .setClause(ImmutableList.of(clause1plus))
+        .setSubject(ImmutableList.of(s1_plus, s2))
+        .build();
+
+    ContractId cloop2Id = new ContractId("89700928-7316-4216-a853-a7ea3934b8f4");
+    Contract cloop1 = new ContractBuilder()
+        .setId(new ContractId("56bbce36-e60b-473d-92de-bb63b5a6dbb5"))
+        .setParent(cloop2Id)
+        .setClause(ImmutableList.of(clause1))
+        .setSubject(ImmutableList.of(s1, s2))
+        .build();
+    Contract cloop2 = new ContractBuilder()
+        .setId(cloop2Id)
+        .setParent(cloop1.getId())
+        .build();
+    ContractId cselfloopid = 
+            new ContractId("63edead2-d6f1-4acf-9f78-831595d194ee");
+    Contract cselfloop = new ContractBuilder()
+        .setId(cselfloopid)
+        .setParent(cselfloopid)
+        .build();
+    Contract corphan = new ContractBuilder()
+        .setId(new ContractId("f72c15f3-76ab-4c7e-a817-eb5f6efcb654"))
+        .setParent(new ContractId("eca4d0d5-8c62-4f46-ad42-71c1f4d3da12"))
+        .build();
+    
+    // ***************
+    // Endpoint Groups
+    // ***************
+    
+    SelectorName cnsName1 = new SelectorName("cns1");
+    ConsumerNamedSelector cns1 = new ConsumerNamedSelectorBuilder()
+        .setName(cnsName1)
+        .setContract(ImmutableList.of(contractId1))
+        .setRequirement(ImmutableList.of(r2))
+        .build();
+
+    ConsumerNamedSelector cns1_plus = new ConsumerNamedSelectorBuilder()
+        .setName(cnsName1)
+        .setContract(ImmutableList.of(contractId2))
+        .setRequirement(ImmutableList.of(r3))
+        .build();
+    
+    SelectorName pnsName1 = new SelectorName("cns1");
+    ProviderNamedSelector pns1 = new ProviderNamedSelectorBuilder()
+        .setName(cnsName1)
+        .setContract(ImmutableList.of(contractId1))
+        .setCapability(ImmutableList.of(c2))
+        .build();
+
+    ProviderNamedSelector pns1_plus = new ProviderNamedSelectorBuilder()
+        .setName(cnsName1)
+        .setContract(ImmutableList.of(contractId2))
+        .setCapability(ImmutableList.of(c3))
+        .build();
+    
+    QualityMatcher qm_q1_all = new QualityMatcherBuilder()
+        .setName(new QualityMatcherName("qm_q1_all"))
+        .setMatcherQuality(ImmutableList.of(new MatcherQualityBuilder(q1)
+                                                .build()))
+        .setMatchType(MatchType.All)
+        .build();
+    QualityMatcher qm_q1_any = new QualityMatcherBuilder()
+        .setName(new QualityMatcherName("qm_q1_any"))
+        .setMatcherQuality(ImmutableList.of(new MatcherQualityBuilder(q1)
+                                            .build()))
+        .setMatchType(MatchType.Any)
+        .build();
+    QualityMatcher qm_q2q3_any = new QualityMatcherBuilder()
+        .setName(new QualityMatcherName("qm_q2q3_any"))
+        .setMatcherQuality(ImmutableList.of(new MatcherQualityBuilder(q2)
+                                                .build(),
+                                              new MatcherQualityBuilder(q3)
+                                                .build()))
+        .setMatchType(MatchType.Any)
+        .build();
+
+    QualityMatcher qm_q2tq2 = new QualityMatcherBuilder()
+        .setName(new QualityMatcherName("qm_q2tq2"))
+        .setMatcherQuality(ImmutableList.of(new MatcherQualityBuilder(q2)
+                                                .setTargetNamespace(q2TargetName)
+                                                .build()))
+        .setMatchType(MatchType.Any)
+        .build();
+    QualityMatcher qm_q2q3_plus = new QualityMatcherBuilder()
+        .setName(new QualityMatcherName("qm_q2q3_any"))
+        .setMatcherQuality(ImmutableList.of(new MatcherQualityBuilder(q3)
+                                                .setTargetNamespace(q2TargetName)
+                                                .build(),
+                                              new MatcherQualityBuilder(q2Exclude)
+                                                .build()))
+        .setMatchType(MatchType.All)
+        .build();
+    QualityMatcher qm_q1_plus = new QualityMatcherBuilder()
+        .setName(new QualityMatcherName("qm_q1_any"))
+        .build();
+    
+    SelectorName ctsName1 = new SelectorName("cts1");
+    ConsumerTargetSelector cts1 = new ConsumerTargetSelectorBuilder()
+        .setName(ctsName1)
+        .setQualityMatcher(ImmutableList.of(qm_q1_all, qm_q1_any))
+        .setRequirement(ImmutableList.of(r2))
+        .build();
+    SelectorName ctsName2 = new SelectorName("cts2");
+    ConsumerTargetSelector cts2 = new ConsumerTargetSelectorBuilder()
+        .setName(ctsName2)
+        .setQualityMatcher(ImmutableList.of(qm_q2q3_any))
+        .setRequirement(ImmutableList.of(r1exclude, r3))
+        .build();
+    ConsumerTargetSelector cts1_plus = new ConsumerTargetSelectorBuilder()
+        .setName(ctsName1)
+        .setQualityMatcher(ImmutableList.of(qm_q1_plus,
+                                              qm_q2q3_any, 
+                                              qm_q1_plus))
+        .setRequirement(ImmutableList.of(r3))
+        .build();
+    ConsumerTargetSelector cts2_plus = new ConsumerTargetSelectorBuilder()
+        .setName(ctsName2)
+        .setQualityMatcher(ImmutableList.of(qm_q2tq2, 
+                                              qm_q2q3_plus))
+        .setRequirement(ImmutableList.of(r3))
+        .build();
+    
+    SelectorName ptsName1 = new SelectorName("pts1");
+    ProviderTargetSelector pts1 = new ProviderTargetSelectorBuilder()
+        .setName(ptsName1)
+        .setQualityMatcher(ImmutableList.of(qm_q1_all, qm_q1_any))
+        .setCapability(ImmutableList.of(c2))
+        .build();
+    SelectorName ptsName2 = new SelectorName("pts2");
+    ProviderTargetSelector pts2 = new ProviderTargetSelectorBuilder()
+        .setName(ptsName2)
+        .setQualityMatcher(ImmutableList.of(qm_q2q3_any))
+        .setCapability(ImmutableList.of(c1exclude, c3))
+        .build();
+    ProviderTargetSelector pts1_plus = new ProviderTargetSelectorBuilder()
+        .setName(ptsName1)
+        .setQualityMatcher(ImmutableList.of(qm_q1_plus,
+                                              qm_q2q3_any, 
+                                              qm_q1_plus))
+        .setCapability(ImmutableList.of(c3))
+        .build();
+    ProviderTargetSelector pts2_plus = new ProviderTargetSelectorBuilder()
+        .setName(ptsName2)
+        .setQualityMatcher(ImmutableList.of(qm_q2tq2, 
+                                              qm_q2q3_plus))
+        .setCapability(ImmutableList.of(c3))
+        .build();
+    
+    EndpointGroupId egId1 = 
+            new EndpointGroupId("c0e5edfb-02d2-412b-8757-a77b3daeb5d4");
+    EndpointGroup eg1 = new EndpointGroupBuilder()
+        .setId(egId1)
+        .setRequirement(ImmutableList.of(r1))
+        .setCapability(ImmutableList.of(c1))
+        .setConsumerTargetSelector(ImmutableList.of(cts1, cts2))
+        .setConsumerNamedSelector(ImmutableList.of(cns1))
+        .setProviderTargetSelector(ImmutableList.of(pts1, pts2))
+        .setProviderNamedSelector(ImmutableList.of(pns1))
+        .build();
+    EndpointGroupId egId2 = 
+            new EndpointGroupId("60483327-ad76-43dd-b3bf-54ffb73ef4b8"); 
+    EndpointGroup eg2 = new EndpointGroupBuilder()
+        .setId(egId2)
+        .setParent(egId1)
+        .setConsumerTargetSelector(ImmutableList.of(cts1_plus, cts2_plus))
+        .setConsumerNamedSelector(ImmutableList.of(cns1_plus))
+        .setProviderTargetSelector(ImmutableList.of(pts1_plus, pts2_plus))
+        .setProviderNamedSelector(ImmutableList.of(pns1_plus))
+        .build();
+
+    EndpointGroupId egloop2Id = 
+            new EndpointGroupId("cb5be574-9836-4053-8ec4-4b4a43331d65");
+    EndpointGroup egloop1 = new EndpointGroupBuilder()
+        .setId(new EndpointGroupId("a33fdd4d-f58b-4741-a69f-08aecab9af2e"))
+        .setParent(egloop2Id)
+        .setConsumerNamedSelector(ImmutableList.of(cns1))
+        .setProviderNamedSelector(ImmutableList.of(pns1))
+        .setConsumerTargetSelector(ImmutableList.of(cts1))
+        .setProviderTargetSelector(ImmutableList.of(pts1))
+        .build();
+    EndpointGroup egloop2 = new EndpointGroupBuilder()
+        .setId(egloop2Id)
+        .setParent(egloop1.getId())
+        .build();
+    EndpointGroupId egselfloopid = 
+            new EndpointGroupId("996ad104-f852-4d77-96cf-cddde5cebb84");
+    EndpointGroup egselfloop = new EndpointGroupBuilder()
+        .setId(egselfloopid)
+        .setParent(egselfloopid)
+        .build();
+    EndpointGroup egorphan = new EndpointGroupBuilder()
+        .setId(new EndpointGroupId("feafeac9-ce1a-4b19-8455-8fcc9a4ff013"))
+        .setParent(new EndpointGroupId("aa9dfcf1-610c-42f9-8c3a-f67b43196821"))
+        .build();
+    
+    // *******
+    // Tenants
+    // *******
+    
+    TenantId tenantId1 = new TenantId("0ac5d219-979c-4cca-8f90-83b69bc414ad");
+    Tenant tenant1 = new TenantBuilder()
+        .setId(tenantId1)
+        .setEndpointGroup(ImmutableList.of(eg1, eg2))
+        .setContract(ImmutableList.of(contract1, contract2))
+        .build();
+
+    Tenant malformed = new TenantBuilder()
+        .setId(new TenantId("b26e6b18-8e74-4062-a7d2-e8437132030d"))
+        .setContract(ImmutableList.of(cloop1, cloop2, cselfloop, corphan))
+        .setEndpointGroup(ImmutableList.of(egloop1, egloop2, egselfloop, egorphan))
+        .build();
+    
+    InstanceIdentifier<Tenant> tenantiid1 = TenantUtils.tenantIid(tenantId1);
+    InstanceIdentifier<Tenant> tenantiid2 = 
+            TenantUtils.tenantIid(malformed.getId());
+
+    // ****************
+    // Other test state
+    // ****************
+
+    DataModificationTransaction transaction;
+    
+    @Before
+    public void setup() throws Exception {
+        transaction = mock(DataModificationTransaction.class);
+        when(transaction.readConfigurationData(tenantiid1))
+            .thenReturn(tenant1);
+        when(transaction.readConfigurationData(tenantiid2))
+            .thenReturn(malformed);
+    }
+
+    public boolean containsQuality(List<? extends QualityBase> qualities, 
+                                   QualityBase quality) {
+        for (QualityBase q : qualities) {
+            if (q.getName().equals(quality.getName()))
+                return true;
+        }
+        return false;
+    }
+
+    @Test
+    public void testTargetSimple() throws Exception {
+        Tenant tenant = InheritanceUtils.resolveTenant(tenantId1, transaction);
+        Contract c1 = TenantUtils.findContract(tenant, contractId1);
+
+        // target with a quality directly in the target and one in 
+        // the containing contract
+        Target result = TenantUtils.findTarget(c1, q2TargetName);
+        assertEquals(q2TargetName, result.getName());
+        List<Quality> qualities = result.getQuality();
+        assertTrue(q1.getName() + " found in q2target", 
+                   containsQuality(qualities, q1));
+        assertTrue(q2.getName() + " found in q2target", 
+                   containsQuality(qualities, q2));
+
+        // target with a quality directly in the target with explicit "include"
+        result = TenantUtils.findTarget(c1, q1IncludeTargetName);
+        qualities = result.getQuality();
+        assertTrue(q1.getName() + " found in q1IncludeTargetName", 
+                   containsQuality(qualities, q1));
+        
+        // target with a quality from the containing contract but overridden
+        // in the target
+        result = TenantUtils.findTarget(c1, q1ExcludeTargetName);
+        qualities = result.getQuality();
+        assertFalse(q1.getName() + " found in q1ExcludeTargetName", 
+                    containsQuality(qualities, q1));
+        assertTrue(q2.getName() + " found in q1ExcludeTargetName", 
+                   containsQuality(qualities, q2));
+    }
+
+    @Test
+    public void testTargetInheritance() throws Exception {
+        Tenant tenant = InheritanceUtils.resolveTenant(tenantId1, transaction);
+        Contract c2 = TenantUtils.findContract(tenant, contractId2);
+
+        // hits the q2PlusTarget which should include everything in q2Target
+        // plus q3
+        Target result = TenantUtils.findTarget(c2, q2TargetName);
+        List<Quality> qualities = result.getQuality();
+        assertTrue(q1.getName() + " found in q2target", 
+                   containsQuality(qualities, q1));
+        assertTrue(q2.getName() + " found in q2target", 
+                   containsQuality(qualities, q2));
+        assertTrue(q3.getName() + " found in q2target", 
+                   containsQuality(qualities, q3));
+
+        // Simple case of inheriting the behavior from the base but not messing
+        // it up
+        result = TenantUtils.findTarget(c2, q1IncludeTargetName);
+        qualities = result.getQuality();
+        assertTrue(q1.getName() + " found in q1IncludeTargetName", 
+                   containsQuality(qualities, q1));
+        assertFalse(q2.getName() + " found in q1IncludeTargetName", 
+                   containsQuality(qualities, q2));
+        assertFalse(q3.getName() + " found in q1IncludeTargetName", 
+                    containsQuality(qualities, q3));
+
+        // Inherit a target from the base that isn't found in the child at all
+        result = TenantUtils.findTarget(c2, q1ExcludeTargetName);
+        qualities = result.getQuality();
+        assertFalse(q1.getName() + " found in q1ExcludeTargetName", 
+                    containsQuality(qualities, q1));
+        assertTrue(q2.getName() + " found in q1ExcludeTargetName", 
+                   containsQuality(qualities, q2));
+        assertFalse(q3.getName() + " found in q1ExcludeTargetName", 
+                    containsQuality(qualities, q3));
+    }
+    
+    private boolean containsRequirement(List<? extends RequirementBase> requirements, 
+                                       RequirementBase requirement) {
+        for (RequirementBase r : requirements) {
+            if (r.getName().equals(requirement.getName()))
+                return true;
+        }
+        return false;
+    }
+    
+    private boolean containsCapability(List<? extends CapabilityBase> capabilities, 
+                                      CapabilityBase capability) {
+        for (CapabilityBase r : capabilities) {
+            if (r.getName().equals(capability.getName()))
+                return true;
+        }
+        return false;
+    }
+
+    private boolean containsCondition(List<? extends Condition> conditions, 
+                                      Condition condition) {
+        for (Condition r : conditions) {
+            if (r.getName().equals(condition.getName()))
+                return true;
+        }
+        return false;
+    }
+    
+    @Test
+    public void testConsumerTargetSelectorSimple() throws Exception {
+        Tenant tenant = InheritanceUtils.resolveTenant(tenantId1, transaction);
+        EndpointGroup egResult1 = TenantUtils.findEndpointGroup(tenant, egId1);
+        
+        // should get r1 from eg1 and r2 from target selector
+        ConsumerTargetSelector result =
+                TenantUtils.findCts(egResult1, ctsName1);
+        assertEquals(ctsName1, result.getName());
+        List<Requirement> requirements = result.getRequirement();
+        assertTrue(r1.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r1));
+        assertTrue(r2.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r2));
+        
+        List<QualityMatcher> matchers = result.getQualityMatcher();
+        assertEquals(2, matchers.size());
+        for (QualityMatcher m : matchers) {
+            if (m.getName().equals(new QualityMatcherName("qm_q1_all"))) {
+                assertTrue(containsQuality(m.getMatcherQuality(), q1));
+                assertEquals(MatchType.All, m.getMatchType());                
+            } else {
+                assertTrue(containsQuality(m.getMatcherQuality(), q1));
+                assertEquals(MatchType.Any, m.getMatchType());
+            }
+        }
+
+        // should get r1 from eg1 but excluded in target selector
+        // r3 comes from target selector
+        result = TenantUtils.findCts(egResult1, ctsName2);
+        assertEquals(ctsName2, result.getName());
+        requirements = result.getRequirement();
+        assertFalse(r1.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r1));
+        assertFalse(r2.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r2));
+        assertTrue(r3.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r3));
+        
+        matchers = result.getQualityMatcher();
+        assertEquals(1, matchers.size());
+        assertTrue(containsQuality(matchers.get(0).getMatcherQuality(), q2));
+        assertTrue(containsQuality(matchers.get(0).getMatcherQuality(), q3));
+        assertEquals(MatchType.Any, matchers.get(0).getMatchType());
+    }
+
+    @Test
+    public void testConsumerTargetSelectorInheritance() throws Exception {
+        Tenant tenant = InheritanceUtils.resolveTenant(tenantId1, transaction);
+        EndpointGroup egResult2 = TenantUtils.findEndpointGroup(tenant, egId2);
+
+        ConsumerTargetSelector result = 
+                TenantUtils.findCts(egResult2, ctsName1);
+
+        List<Requirement> requirements = result.getRequirement();
+        assertTrue(r1.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r1));
+        assertTrue(r3.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r3));
+        
+        // should have three matchers, 
+        // (1) qm_q1_all inherited from endpoint group 1
+        // (2) qm_q1_any inherited from endpoint group 1, but overridden in 
+        //     endpoint group 2 with no new semantics
+        // (3) qm_q2q3_any defined in endpoint group 2
+        List<QualityMatcher> matchers = result.getQualityMatcher();
+        assertEquals(3, matchers.size());
+        for (QualityMatcher m : matchers) {
+            if (m.getName().equals(new QualityMatcherName("qm_q1_all"))) {
+                assertEquals(1, m.getMatcherQuality().size());
+                assertTrue(containsQuality(m.getMatcherQuality(), q1));
+                assertEquals(MatchType.All, m.getMatchType());
+            } else if (m.getName().equals(new QualityMatcherName("qm_q1_any"))) {
+                assertEquals(1, m.getMatcherQuality().size());
+                assertTrue(containsQuality(m.getMatcherQuality(), q1));
+                assertEquals(MatchType.Any, m.getMatchType());
+            } else {
+                assertTrue(containsQuality(m.getMatcherQuality(), q2));
+                assertTrue(containsQuality(m.getMatcherQuality(), q3));
+                assertEquals(MatchType.Any, m.getMatchType());
+            }
+        }
+        
+        result = TenantUtils.findCts(egResult2, ctsName2);
+        assertEquals(ctsName2, result.getName());
+        requirements = result.getRequirement();
+        
+        // should get r1 from eg1 but excluded in target selector
+        // r3 comes from target selector
+        assertFalse(r1.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r1));
+        assertFalse(r2.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r2));
+        assertTrue(r3.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r3));
+        
+        // Should get 2 matchers: 
+        // (1) qm_q2q2_any inherited from eg1, except that q2 is excluded 
+        //     by qm_q2q3_plus and q3 has a target namespace added 
+        // (2) qm_q2tq2_any newly-defined with a target namespace
+        matchers = result.getQualityMatcher();
+        assertEquals(2, matchers.size());
+        for (QualityMatcher m : matchers) {
+            if (m.getName().equals(new QualityMatcherName("qm_q2q3_any"))) {
+                assertFalse(containsQuality(m.getMatcherQuality(), q1));
+                assertFalse(containsQuality(m.getMatcherQuality(), q2));
+                assertTrue(containsQuality(m.getMatcherQuality(), q3));
+                for (MatcherQuality mq : m.getMatcherQuality()) {
+                    if (mq.getName().equals(q3.getName())) {
+                        assertEquals(q2TargetName, mq.getTargetNamespace());
+                    } else {
+                        assertNull(mq.getTargetNamespace());
+                    }
+                }
+                assertEquals(MatchType.All, m.getMatchType());
+            } else {
+                assertTrue(containsQuality(m.getMatcherQuality(), q2));
+                assertEquals(MatchType.Any, m.getMatchType());
+                assertEquals(1, m.getMatcherQuality().size());
+                assertEquals(q2TargetName,
+                             m.getMatcherQuality().get(0).getTargetNamespace());
+            }
+        }
+    }
+    
+    @Test
+    public void testConsumerNamedSelectorSimple() throws Exception {
+        Tenant tenant = InheritanceUtils.resolveTenant(tenantId1, transaction);
+        EndpointGroup egResult1 = TenantUtils.findEndpointGroup(tenant, egId1);
+        
+        // should get r1 from eg1 and r2 from selector
+        ConsumerNamedSelector result =
+                TenantUtils.findCns(egResult1, cnsName1);
+        assertEquals(cnsName1, result.getName());
+        List<Requirement> requirements = result.getRequirement();
+        assertEquals(2, requirements.size());
+        assertTrue(r1.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r1));
+        assertTrue(r2.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r2));
+        
+        assertEquals(1, result.getContract().size());
+        HashSet<ContractId> cids = new HashSet<>();
+        cids.addAll(result.getContract());
+        assertEquals(ImmutableSet.of(contractId1), cids);
+    }
+
+    @Test
+    public void testConsumerNamedSelectorInheritance() throws Exception {
+        Tenant tenant = InheritanceUtils.resolveTenant(tenantId1, transaction);
+        EndpointGroup egResult2 = TenantUtils.findEndpointGroup(tenant, egId2);
+
+        // should get r1 from eg1 and r2 from eg1 selector, 
+        // and r3 from eg2 selector
+        ConsumerNamedSelector result =
+                TenantUtils.findCns(egResult2, cnsName1);
+        assertEquals(cnsName1, result.getName());
+        List<Requirement> requirements = result.getRequirement();
+        assertEquals(3, requirements.size());
+        assertTrue(r1.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r1));
+        assertTrue(r2.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r2));
+        assertTrue(r3.getName() + " found in " + requirements,
+                   containsRequirement(requirements, r3));
+
+        assertEquals(2, result.getContract().size());
+        HashSet<ContractId> cids = new HashSet<>();
+        cids.addAll(result.getContract());
+        assertEquals(ImmutableSet.of(contractId1, contractId2), cids);
+    }
+
+    @Test
+    public void testProviderTargetSelectorSimple() throws Exception {
+        Tenant tenant = InheritanceUtils.resolveTenant(tenantId1, transaction);
+        EndpointGroup egResult1 = TenantUtils.findEndpointGroup(tenant, egId1);
+        
+        // should get c1 from eg1 and c2 from target selector
+        ProviderTargetSelector result =
+                TenantUtils.findPts(egResult1, ptsName1);
+        assertEquals(ptsName1, result.getName());
+        List<Capability> capabilities = result.getCapability();
+        assertTrue(c1.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c1));
+        assertTrue(c2.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c2));
+        
+        List<QualityMatcher> matchers = result.getQualityMatcher();
+        assertEquals(2, matchers.size());
+        for (QualityMatcher m : matchers) {
+            if (m.getName().equals(new QualityMatcherName("qm_q1_all"))) {
+                assertTrue(containsQuality(m.getMatcherQuality(), q1));
+                assertEquals(MatchType.All, m.getMatchType());                
+            } else {
+                assertTrue(containsQuality(m.getMatcherQuality(), q1));
+                assertEquals(MatchType.Any, m.getMatchType());
+            }
+        }
+
+        // should get c1 from eg1 but excluded in target selector
+        // c3 comes from target selector
+        result = TenantUtils.findPts(egResult1, ptsName2);
+        assertEquals(ptsName2, result.getName());
+        capabilities = result.getCapability();
+        assertFalse(c1.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c1));
+        assertFalse(c2.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c2));
+        assertTrue(c3.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c3));
+        
+        matchers = result.getQualityMatcher();
+        assertEquals(1, matchers.size());
+        assertTrue(containsQuality(matchers.get(0).getMatcherQuality(), q2));
+        assertTrue(containsQuality(matchers.get(0).getMatcherQuality(), q3));
+        assertEquals(MatchType.Any, matchers.get(0).getMatchType());
+    }
+
+    @Test
+    public void testProviderTargetSelectorInheritance() throws Exception {
+        Tenant tenant = InheritanceUtils.resolveTenant(tenantId1, transaction);
+        EndpointGroup egResult2 = TenantUtils.findEndpointGroup(tenant, egId2);
+
+        ProviderTargetSelector result = 
+                TenantUtils.findPts(egResult2, ptsName1);
+
+        List<Capability> capabilities = result.getCapability();
+        assertTrue(c1.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c1));
+        assertTrue(c3.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c3));
+        
+        // should have three matchers, 
+        // (1) qm_q1_all inherited from endpoint group 1
+        // (2) qm_q1_any inherited from endpoint group 1, but overridden in 
+        //     endpoint group 2 with no new semantics
+        // (3) qm_q2q3_any defined in endpoint group 2
+        List<QualityMatcher> matchers = result.getQualityMatcher();
+        assertEquals(3, matchers.size());
+        for (QualityMatcher m : matchers) {
+            if (m.getName().equals(new QualityMatcherName("qm_q1_all"))) {
+                assertEquals(1, m.getMatcherQuality().size());
+                assertTrue(containsQuality(m.getMatcherQuality(), q1));
+                assertEquals(MatchType.All, m.getMatchType());
+            } else if (m.getName().equals(new QualityMatcherName("qm_q1_any"))) {
+                assertEquals(1, m.getMatcherQuality().size());
+                assertTrue(containsQuality(m.getMatcherQuality(), q1));
+                assertEquals(MatchType.Any, m.getMatchType());
+            } else {
+                assertTrue(containsQuality(m.getMatcherQuality(), q2));
+                assertTrue(containsQuality(m.getMatcherQuality(), q3));
+                assertEquals(MatchType.Any, m.getMatchType());
+            }
+        }
+        
+        result = TenantUtils.findPts(egResult2, ptsName2);
+        assertEquals(ptsName2, result.getName());
+        capabilities = result.getCapability();
+        
+        // should get c1 from eg1 but excluded in target selector
+        // c3 comes from target selector
+        assertFalse(c1.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c1));
+        assertFalse(c2.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c2));
+        assertTrue(c3.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c3));
+        
+        // Should get 2 matchers: 
+        // (1) qm_q2q2_any inherited from eg1, except that q2 is excluded 
+        //     by qm_q2q3_plus and q3 has a target namespace added 
+        // (2) qm_q2tq2_any newly-defined with a target namespace
+        matchers = result.getQualityMatcher();
+        assertEquals(2, matchers.size());
+        for (QualityMatcher m : matchers) {
+            if (m.getName().equals(new QualityMatcherName("qm_q2q3_any"))) {
+                assertFalse(containsQuality(m.getMatcherQuality(), q1));
+                assertFalse(containsQuality(m.getMatcherQuality(), q2));
+                assertTrue(containsQuality(m.getMatcherQuality(), q3));
+                for (MatcherQuality mq : m.getMatcherQuality()) {
+                    if (mq.getName().equals(q3.getName())) {
+                        assertEquals(q2TargetName, mq.getTargetNamespace());
+                    } else {
+                        assertNull(mq.getTargetNamespace());
+                    }
+                }
+                assertEquals(MatchType.All, m.getMatchType());
+            } else {
+                assertTrue(containsQuality(m.getMatcherQuality(), q2));
+                assertEquals(MatchType.Any, m.getMatchType());
+                assertEquals(1, m.getMatcherQuality().size());
+                assertEquals(q2TargetName,
+                             m.getMatcherQuality().get(0).getTargetNamespace());
+            }
+        }
+    }
+
+    @Test
+    public void testProviderNamedSelectorSimple() throws Exception {
+        Tenant tenant = InheritanceUtils.resolveTenant(tenantId1, transaction);
+        EndpointGroup egResult1 = TenantUtils.findEndpointGroup(tenant, egId1);
+        
+        // should get c1 from eg1 and c2 from selector
+        ProviderNamedSelector result =
+                TenantUtils.findPns(egResult1, cnsName1);
+        assertEquals(cnsName1, result.getName());
+        List<Capability> capabilities = result.getCapability();
+        assertEquals(2, capabilities.size());
+        assertTrue(c1.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c1));
+        assertTrue(c2.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c2));
+        
+        assertEquals(1, result.getContract().size());
+        HashSet<ContractId> cids = new HashSet<>();
+        cids.addAll(result.getContract());
+        assertEquals(ImmutableSet.of(contractId1), cids);
+    }
+
+    @Test
+    public void testProviderNamedSelectorInheritance() throws Exception {
+        Tenant tenant = InheritanceUtils.resolveTenant(tenantId1, transaction);
+        EndpointGroup egResult2 = TenantUtils.findEndpointGroup(tenant, egId2);
+
+        // should get c1 from eg1 and c2 from eg1 selector, 
+        // and c3 from eg2 selector
+        ProviderNamedSelector result =
+                TenantUtils.findPns(egResult2, cnsName1);
+        assertEquals(cnsName1, result.getName());
+        List<Capability> capabilities = result.getCapability();
+        assertEquals(3, capabilities.size());
+        assertTrue(c1.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c1));
+        assertTrue(c2.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c2));
+        assertTrue(c3.getName() + " found in " + capabilities,
+                   containsCapability(capabilities, c3));
+
+        assertEquals(2, result.getContract().size());
+        HashSet<ContractId> cids = new HashSet<>();
+        cids.addAll(result.getContract());
+        assertEquals(ImmutableSet.of(contractId1, contractId2), cids);
+    }
+
+    @Test
+    public void testClauseSimple() throws Exception {
+        Tenant tenant = InheritanceUtils.resolveTenant(tenantId1, transaction);
+        Contract cresult1 = TenantUtils.findContract(tenant, contractId1);
+        
+        Clause result = TenantUtils.findClause(cresult1, clauseName1);
+        assertEquals(clauseName1, result.getName());
+        
+        // subject refs: subject1 from clause1
+        assertEquals(1, result.getSubjectRefs().size());
+        assertEquals(ImmutableSet.of(subject1), 
+                     ImmutableSet.copyOf(result.getSubjectRefs()));
+
+        assertNotNull(result.getProviderMatchers());
+        List<ConditionMatcher> cm = 
+                result.getProviderMatchers().getConditionMatcher();
+        assertEquals(1, cm.size());
+        assertEquals(1, cm.get(0).getCondition().size());
+        assertTrue(containsCondition(cm.get(0).getCondition(), cond1));
+
+        List<CapabilityMatcher> capm = 
+                result.getProviderMatchers().getCapabilityMatcher();
+        assertEquals(1, capm.size());
+        assertEquals(1, capm.get(0).getMatcherCapability().size());
+        assertTrue(containsCapability(capm.get(0).getMatcherCapability(), c1));
+        
+        assertNotNull(result.getConsumerMatchers());
+        cm = result.getConsumerMatchers().getConditionMatcher();
+        assertEquals(1, cm.size());
+        assertEquals(1, cm.get(0).getCondition().size());
+        assertTrue(containsCondition(cm.get(0).getCondition(), cond2));
+
+        List<RequirementMatcher> pm = 
+                result.getConsumerMatchers().getRequirementMatcher();
+        assertEquals(1, pm.size());
+        assertEquals(1, pm.get(0).getMatcherRequirement().size());
+        assertTrue(containsRequirement(pm.get(0).getMatcherRequirement(), r1));
+        
+    }
+
+    @Test
+    public void testClauseInheritance() throws Exception {
+        Tenant tenant = InheritanceUtils.resolveTenant(tenantId1, transaction);
+        Contract cresult2 = TenantUtils.findContract(tenant, contractId2);
+        
+        Clause result = TenantUtils.findClause(cresult2, clauseName1);
+        assertEquals(clauseName1, result.getName());
+        // subject refs: subject1 from clause1, subject2 from clause2
+        assertEquals(2, result.getSubjectRefs().size());
+        assertEquals(ImmutableSet.of(subject1, subject2), 
+                     ImmutableSet.copyOf(result.getSubjectRefs()));
+
+        assertNotNull(result.getProviderMatchers());
+        List<ConditionMatcher> cm = 
+                result.getProviderMatchers().getConditionMatcher();
+        assertEquals(1, cm.size());
+        assertEquals(1, cm.get(0).getCondition().size());
+        assertTrue(containsCondition(cm.get(0).getCondition(), cond1));
+        
+        List<CapabilityMatcher> capm = 
+                result.getProviderMatchers().getCapabilityMatcher();
+        assertEquals(1, capm.size());
+        assertEquals(1, capm.get(0).getMatcherCapability().size());
+        assertTrue(containsCapability(capm.get(0).getMatcherCapability(), c1));
+        
+        assertNotNull(result.getConsumerMatchers());
+        cm = result.getConsumerMatchers().getConditionMatcher();
+        assertEquals(1, cm.size());
+        assertEquals(cm_c2.getName(), cm.get(0).getName());
+        assertEquals(MatchType.All, cm.get(0).getMatchType());
+        assertEquals(0, cm.get(0).getCondition().size());
+
+        List<RequirementMatcher> pm = 
+                result.getConsumerMatchers().getRequirementMatcher();
+        assertEquals(1, pm.size());
+        assertEquals(2, pm.get(0).getMatcherRequirement().size());
+        assertTrue(containsRequirement(pm.get(0).getMatcherRequirement(), r1));
+        assertTrue(containsRequirement(pm.get(0).getMatcherRequirement(), r2));
+    }
+
+    @Test
+    public void testSubjectSimple() throws Exception {
+        Tenant tenant = InheritanceUtils.resolveTenant(tenantId1, transaction);
+
+        Contract result = TenantUtils.findContract(tenant, contractId1);
+        List<Subject> subjects = result.getSubject();
+        assertEquals(1, subjects.size());
+        
+        assertEquals(s1.getName(), subjects.get(0).getName());
+        List<Rule> rules = subjects.get(0).getRule();
+        assertEquals(2, rules.size());
+        assertEquals(rule2.getName(), rules.get(0).getName());
+        assertEquals(rule1.getName(), rules.get(1).getName());
+    }
+
+    @Test
+    public void testSubjectInheritance() throws Exception {
+        Tenant tenant = InheritanceUtils.resolveTenant(tenantId1, transaction);
+
+        Contract result = TenantUtils.findContract(tenant, contractId2);
+        List<Subject> subjects = result.getSubject();
+        assertEquals(2, subjects.size());
+        for (Subject s: subjects) {
+            if (s1.getName().equals(s.getName())) {
+                assertEquals(Integer.valueOf(4), s.getOrder());
+                List<Rule> rules = s.getRule();
+                assertEquals(4, rules.size());
+                assertEquals(rule4.getName(), rules.get(0).getName());
+                assertEquals(rule3.getName(), rules.get(1).getName());
+                assertEquals(rule2.getName(), rules.get(2).getName());
+                assertEquals(rule1.getName(), rules.get(3).getName());
+            } else if (s2.getName().equals(s.getName())) {
+                assertEquals(0, s.getRule().size());
+                assertEquals(Integer.valueOf(5), s.getOrder());
+            } else {
+                fail("extra subject?");
+            }
+        }
+    }
+    
+    @Test
+    public void testMalformedPolicy() throws Exception {
+        Tenant tenant = 
+                InheritanceUtils.resolveTenant(malformed.getId(), transaction);
+        Contract c = TenantUtils.findContract(tenant, cloop2Id);
+        assertEquals(1, c.getClause().size());
+        Clause clause = c.getClause().get(0);
+        assertEquals(1, clause.getConsumerMatchers().getConditionMatcher().size());
+        assertEquals(1, clause.getConsumerMatchers().getRequirementMatcher().size());
+        assertEquals(1, clause.getProviderMatchers().getConditionMatcher().size());
+        assertEquals(1, clause.getProviderMatchers().getCapabilityMatcher().size());
+        assertEquals(2, c.getSubject().size());
+        
+        EndpointGroup eg = TenantUtils.findEndpointGroup(tenant, egloop2Id);
+        assertEquals(1, eg.getConsumerNamedSelector().size());
+        assertEquals(1, eg.getConsumerTargetSelector().size());
+        assertEquals(1, eg.getProviderNamedSelector().size());
+        assertEquals(1, eg.getProviderTargetSelector().size());
+        
+    }
+}
diff --git a/groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/resolver/internal/MatcherUtilsTest.java b/groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/resolver/internal/MatcherUtilsTest.java
new file mode 100644 (file)
index 0000000..0d8d62c
--- /dev/null
@@ -0,0 +1,266 @@
+package org.opendaylight.groupbasedpolicy.resolver.internal;
+
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.CapabilityName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.QualityName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RequirementName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SelectorName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TargetName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Matcher.MatchType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.capabilities.Capability;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.capabilities.CapabilityBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.qualities.Quality;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.qualities.QualityBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.requirements.Requirement;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.requirements.RequirementBuilder;
+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.target.selector.QualityMatcherBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.target.selector.quality.matcher.MatcherQuality;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.target.selector.quality.matcher.MatcherQualityBuilder;
+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.TargetBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.RequirementMatcher;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.RequirementMatcherBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.requirement.matcher.MatcherRequirement;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.requirement.matcher.MatcherRequirementBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.CapabilityMatcher;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.CapabilityMatcherBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.capability.matcher.MatcherCapability;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.capability.matcher.MatcherCapabilityBuilder;
+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.ConsumerNamedSelectorBuilder;
+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.ConsumerTargetSelectorBuilder;
+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.ProviderNamedSelectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderTargetSelector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderTargetSelectorBuilder;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.junit.Assert.*;
+
+public class MatcherUtilsTest {
+    
+    @Test
+    public void testApplyQualityMatcher() throws Exception {
+        Quality q1 = new QualityBuilder().setName(new QualityName("q1")).build();
+        Quality q2 = new QualityBuilder().setName(new QualityName("q2")).build();
+        Quality q3 = new QualityBuilder().setName(new QualityName("q3")).build();
+        Quality q4 = new QualityBuilder().setName(new QualityName("q4")).build();
+        
+        Target t1 = new TargetBuilder()
+            .setName(new TargetName("t1"))
+            .setQuality(ImmutableList.of(q1, q2))
+            .build();
+        Target t2 = new TargetBuilder()
+            .setName(new TargetName("t2"))
+            .setQuality(ImmutableList.of(q3, q4))
+            .build();
+
+        MatcherQuality mq1 = new MatcherQualityBuilder(q1).build();
+        MatcherQuality mq2 = new MatcherQualityBuilder(q2).build();
+        MatcherQuality mq1_ns1 = new MatcherQualityBuilder(q1)
+            .setTargetNamespace(t1.getName())
+            .build();
+        MatcherQuality mq1_ns2 = new MatcherQualityBuilder(q1)
+            .setTargetNamespace(t2.getName())
+            .build();
+        MatcherQuality mq3 = new MatcherQualityBuilder(q3).build();
+        
+        QualityMatcher qm = new QualityMatcherBuilder()
+            .setMatchType(MatchType.All)
+            .setMatcherQuality(ImmutableList.of(mq1, mq1_ns1))
+            .build();
+
+        assertTrue(MatcherUtils.applyQualityMatcher(qm, t1));
+        assertFalse(MatcherUtils.applyQualityMatcher(qm, t2));
+        
+        qm = new QualityMatcherBuilder()
+            .setMatcherQuality(ImmutableList.of(mq1_ns2))
+            .build();
+
+        assertFalse(MatcherUtils.applyQualityMatcher(qm, t1));
+        assertFalse(MatcherUtils.applyQualityMatcher(qm, t2));
+
+        qm = new QualityMatcherBuilder()
+            .setMatchType(MatchType.Any)
+            .setMatcherQuality(ImmutableList.of(mq1, mq3))
+            .build();
+
+        assertTrue(MatcherUtils.applyQualityMatcher(qm, t1));
+        assertTrue(MatcherUtils.applyQualityMatcher(qm, t2));
+
+        qm = new QualityMatcherBuilder()
+            .setMatchType(MatchType.Any)
+            .setMatcherQuality(ImmutableList.of(mq1, mq2))
+            .build();
+
+        assertTrue(MatcherUtils.applyQualityMatcher(qm, t1));
+        assertFalse(MatcherUtils.applyQualityMatcher(qm, t2));
+
+        qm = new QualityMatcherBuilder()
+            .setMatchType(MatchType.None)
+            .setMatcherQuality(ImmutableList.of(mq3, mq1_ns2))
+            .build();
+
+        assertTrue(MatcherUtils.applyQualityMatcher(qm, t1));
+        assertFalse(MatcherUtils.applyQualityMatcher(qm, t2));
+        
+        qm = new QualityMatcherBuilder().build();
+        assertTrue(MatcherUtils.applyQualityMatcher(qm, t1));
+
+        qm = new QualityMatcherBuilder()
+            .setMatchType(MatchType.Any)
+            .build();
+        assertFalse(MatcherUtils.applyQualityMatcher(qm, t1));
+    }
+    
+    @Test
+    public void testApplyCapMatcher() throws Exception {
+        Capability q1 = new CapabilityBuilder().setName(new CapabilityName("q1")).build();
+        Capability q2 = new CapabilityBuilder().setName(new CapabilityName("q2")).build();
+        Capability q3 = new CapabilityBuilder().setName(new CapabilityName("q3")).build();
+        Capability q4 = new CapabilityBuilder().setName(new CapabilityName("q4")).build();
+        
+        ProviderTargetSelector t1 = new ProviderTargetSelectorBuilder()
+            .setName(new SelectorName("t1"))
+            .setCapability(ImmutableList.of(q1, q2))
+            .build();
+        ProviderNamedSelector t2 = new ProviderNamedSelectorBuilder()
+            .setName(new SelectorName("t2"))
+            .setCapability(ImmutableList.of(q3, q4))
+            .build();
+
+        MatcherCapability mq1 = new MatcherCapabilityBuilder(q1).build();
+        MatcherCapability mq2 = new MatcherCapabilityBuilder(q2).build();
+        MatcherCapability mq1_ns1 = new MatcherCapabilityBuilder(q1)
+            .setSelectorNamespace(t1.getName())
+            .build();
+        MatcherCapability mq1_ns2 = new MatcherCapabilityBuilder(q1)
+            .setSelectorNamespace(t2.getName())
+            .build();
+        MatcherCapability mq3 = new MatcherCapabilityBuilder(q3).build();
+        
+        CapabilityMatcher qm = new CapabilityMatcherBuilder()
+            .setMatchType(MatchType.All)
+            .setMatcherCapability(ImmutableList.of(mq1, mq1_ns1))
+            .build();
+
+        assertTrue(MatcherUtils.applyCapMatcher(qm, t1));
+        assertFalse(MatcherUtils.applyCapMatcher(qm, t2));
+        
+        qm = new CapabilityMatcherBuilder()
+            .setMatcherCapability(ImmutableList.of(mq1_ns2))
+            .build();
+
+        assertFalse(MatcherUtils.applyCapMatcher(qm, t1));
+        assertFalse(MatcherUtils.applyCapMatcher(qm, t2));
+
+        qm = new CapabilityMatcherBuilder()
+            .setMatchType(MatchType.Any)
+            .setMatcherCapability(ImmutableList.of(mq1, mq3))
+            .build();
+
+        assertTrue(MatcherUtils.applyCapMatcher(qm, t1));
+        assertTrue(MatcherUtils.applyCapMatcher(qm, t2));
+
+        qm = new CapabilityMatcherBuilder()
+            .setMatchType(MatchType.Any)
+            .setMatcherCapability(ImmutableList.of(mq1, mq2))
+            .build();
+
+        assertTrue(MatcherUtils.applyCapMatcher(qm, t1));
+        assertFalse(MatcherUtils.applyCapMatcher(qm, t2));
+
+        qm = new CapabilityMatcherBuilder()
+            .setMatchType(MatchType.None)
+            .setMatcherCapability(ImmutableList.of(mq3, mq1_ns2))
+            .build();
+
+        assertTrue(MatcherUtils.applyCapMatcher(qm, t1));
+        assertFalse(MatcherUtils.applyCapMatcher(qm, t2));
+        
+        qm = new CapabilityMatcherBuilder().build();
+        assertTrue(MatcherUtils.applyCapMatcher(qm, t1));
+
+        qm = new CapabilityMatcherBuilder()
+            .setMatchType(MatchType.Any)
+            .build();
+        assertFalse(MatcherUtils.applyCapMatcher(qm, t1));
+    }
+    
+    @Test
+    public void testApplyReqMatcher() throws Exception {
+        Requirement q1 = new RequirementBuilder().setName(new RequirementName("q1")).build();
+        Requirement q2 = new RequirementBuilder().setName(new RequirementName("q2")).build();
+        Requirement q3 = new RequirementBuilder().setName(new RequirementName("q3")).build();
+        Requirement q4 = new RequirementBuilder().setName(new RequirementName("q4")).build();
+        
+        ConsumerNamedSelector t1 = new ConsumerNamedSelectorBuilder()
+            .setName(new SelectorName("t1"))
+            .setRequirement(ImmutableList.of(q1, q2))
+            .build();
+        ConsumerTargetSelector t2 = new ConsumerTargetSelectorBuilder()
+            .setName(new SelectorName("t2"))
+            .setRequirement(ImmutableList.of(q3, q4))
+            .build();
+
+        MatcherRequirement mq1 = new MatcherRequirementBuilder(q1).build();
+        MatcherRequirement mq2 = new MatcherRequirementBuilder(q2).build();
+        MatcherRequirement mq1_ns1 = new MatcherRequirementBuilder(q1)
+            .setSelectorNamespace(t1.getName())
+            .build();
+        MatcherRequirement mq1_ns2 = new MatcherRequirementBuilder(q1)
+            .setSelectorNamespace(t2.getName())
+            .build();
+        MatcherRequirement mq3 = new MatcherRequirementBuilder(q3).build();
+        
+        RequirementMatcher qm = new RequirementMatcherBuilder()
+            .setMatchType(MatchType.All)
+            .setMatcherRequirement(ImmutableList.of(mq1, mq1_ns1))
+            .build();
+
+        assertTrue(MatcherUtils.applyReqMatcher(qm, t1));
+        assertFalse(MatcherUtils.applyReqMatcher(qm, t2));
+        
+        qm = new RequirementMatcherBuilder()
+            .setMatcherRequirement(ImmutableList.of(mq1_ns2))
+            .build();
+
+        assertFalse(MatcherUtils.applyReqMatcher(qm, t1));
+        assertFalse(MatcherUtils.applyReqMatcher(qm, t2));
+
+        qm = new RequirementMatcherBuilder()
+            .setMatchType(MatchType.Any)
+            .setMatcherRequirement(ImmutableList.of(mq1, mq3))
+            .build();
+
+        assertTrue(MatcherUtils.applyReqMatcher(qm, t1));
+        assertTrue(MatcherUtils.applyReqMatcher(qm, t2));
+
+        qm = new RequirementMatcherBuilder()
+            .setMatchType(MatchType.Any)
+            .setMatcherRequirement(ImmutableList.of(mq1, mq2))
+            .build();
+
+        assertTrue(MatcherUtils.applyReqMatcher(qm, t1));
+        assertFalse(MatcherUtils.applyReqMatcher(qm, t2));
+
+        qm = new RequirementMatcherBuilder()
+            .setMatchType(MatchType.None)
+            .setMatcherRequirement(ImmutableList.of(mq3, mq1_ns2))
+            .build();
+
+        assertTrue(MatcherUtils.applyReqMatcher(qm, t1));
+        assertFalse(MatcherUtils.applyReqMatcher(qm, t2));
+
+        qm = new RequirementMatcherBuilder().build();
+        assertTrue(MatcherUtils.applyReqMatcher(qm, t1));
+
+        qm = new RequirementMatcherBuilder()
+            .setMatchType(MatchType.Any)
+            .build();
+        assertFalse(MatcherUtils.applyReqMatcher(qm, t1));
+    }
+}
diff --git a/groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyResolverTest.java b/groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/resolver/internal/PolicyResolverTest.java
new file mode 100644 (file)
index 0000000..ef795f2
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ * 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.internal;
+
+import java.util.HashSet;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.groupbasedpolicy.resolver.internal.PolicyResolver;
+import org.opendaylight.groupbasedpolicy.resolver.internal.PolicyCache.ConditionSet;
+import org.opendaylight.groupbasedpolicy.resolver.internal.PolicyCache.EgKey;
+import org.opendaylight.groupbasedpolicy.resolver.internal.PolicyCache.Policy;
+import org.opendaylight.groupbasedpolicy.resolver.internal.PolicyCache.RuleGroup;
+import org.opendaylight.groupbasedpolicy.resolver.internal.PolicyResolver.ContractMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.CapabilityMatcherName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.CapabilityName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClauseName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionMatcherName;
+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.EndpointGroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.QualityMatcherName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.QualityName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RequirementMatcherName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RequirementName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RuleName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SelectorName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubjectName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TargetName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Matcher.MatchType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.capabilities.Capability;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.capabilities.CapabilityBuilder;
+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.condition.matchers.ConditionMatcherBuilder;
+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.has.conditions.ConditionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.qualities.Quality;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.qualities.QualityBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.requirements.Requirement;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.requirements.RequirementBuilder;
+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.target.selector.QualityMatcherBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.target.selector.quality.matcher.MatcherQualityBuilder;
+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.TenantBuilder;
+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.ContractBuilder;
+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.EndpointGroupBuilder;
+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.ClauseBuilder;
+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.SubjectBuilder;
+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.TargetBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.ConsumerMatchersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.ProviderMatchersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.RequirementMatcher;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.RequirementMatcherBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.requirement.matcher.MatcherRequirementBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.CapabilityMatcher;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.CapabilityMatcherBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.capability.matcher.MatcherCapabilityBuilder;
+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.ConsumerNamedSelectorBuilder;
+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.ConsumerTargetSelectorBuilder;
+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.ProviderNamedSelectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderTargetSelector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderTargetSelectorBuilder;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Table;
+
+import static org.junit.Assert.*;
+
+public class PolicyResolverTest {
+    Quality q1 = new QualityBuilder()
+        .setName(new QualityName("q1"))
+        .build();
+    Quality q2 = new QualityBuilder()
+        .setName(new QualityName("q2"))
+        .build();
+    Quality q3 = new QualityBuilder()
+        .setName(new QualityName("q3"))
+        .build();
+
+    Requirement r1 = new RequirementBuilder()
+        .setName(new RequirementName("r1"))
+        .build();
+    Requirement r2 = new RequirementBuilder()
+        .setName(new RequirementName("r2"))
+        .build();
+    Requirement r3 = new RequirementBuilder()
+        .setName(new RequirementName("r3"))
+        .build();
+
+    Capability cap1 = new CapabilityBuilder()
+        .setName(new CapabilityName("cap1"))
+        .build();
+    Capability cap2 = new CapabilityBuilder()
+        .setName(new CapabilityName("cap2"))
+        .build();
+    Capability cap3 = new CapabilityBuilder()
+        .setName(new CapabilityName("cap3"))
+        .build();
+
+    Condition cond1 = new ConditionBuilder()
+        .setName(new ConditionName("cond1"))
+        .build();
+    Condition cond2 = new ConditionBuilder()
+        .setName(new ConditionName("cond2"))
+        .build();
+    Condition cond3 = new ConditionBuilder()
+        .setName(new ConditionName("cond3"))
+        .build();
+
+    Target t1 = new TargetBuilder()
+        .setName(new TargetName("t1"))
+        .setQuality(ImmutableList.of(q1,q2))
+        .build();
+    Target t2 = new TargetBuilder()
+        .setName(new TargetName("t1"))
+        .setQuality(ImmutableList.of(q3))
+        .build();
+    Target t0 = new TargetBuilder()
+        .setName(new TargetName("t1"))
+        .build();
+    
+    Rule rule1 = new RuleBuilder()
+        .setName(new RuleName("r1"))
+        .setOrder(Integer.valueOf(5))
+        .build();
+    Rule rule2 = new RuleBuilder()
+        .setName(new RuleName("r2"))
+        .build();
+    Rule rule3 = new RuleBuilder()
+        .setName(new RuleName("r3"))
+        .build();
+    Subject s1 = new SubjectBuilder()
+        .setName(new SubjectName("s1"))
+        .setRule(ImmutableList.of(rule1))
+        .build();
+    Subject s2 = new SubjectBuilder()
+        .setName(new SubjectName("s1"))
+        .setRule(ImmutableList.of(rule2))
+        .setOrder(Integer.valueOf(3))
+        .build();
+    Subject s3 = new SubjectBuilder()
+        .setName(new SubjectName("3"))
+        .setRule(ImmutableList.of(rule3))
+        .setOrder(Integer.valueOf(3))
+        .build();
+
+    RequirementMatcher rm1 = new RequirementMatcherBuilder()
+        .setName(new RequirementMatcherName("rm1"))
+        .setMatcherRequirement(ImmutableList.of(new MatcherRequirementBuilder(r1).build()))
+        .build();
+    RequirementMatcher rm3 = new RequirementMatcherBuilder()
+        .setName(new RequirementMatcherName("rm1"))
+        .setMatcherRequirement(ImmutableList.of(new MatcherRequirementBuilder(r3).build()))
+        .build();
+    CapabilityMatcher capm1 = new CapabilityMatcherBuilder()
+        .setName(new CapabilityMatcherName("cap1"))
+        .setMatcherCapability(ImmutableList.of(new MatcherCapabilityBuilder(cap1).build()))
+        .build();
+    CapabilityMatcher capm3 = new CapabilityMatcherBuilder()
+        .setName(new CapabilityMatcherName("cap3"))
+        .setMatcherCapability(ImmutableList.of(new MatcherCapabilityBuilder(cap3).build()))
+        .build();
+    ConditionMatcher condm1 = new ConditionMatcherBuilder()
+        .setName(new ConditionMatcherName("condm1"))
+        .setCondition(ImmutableList.of(cond1))
+        .setMatchType(MatchType.All)
+        .build();
+    ConditionMatcher condm2 = new ConditionMatcherBuilder()
+        .setName(new ConditionMatcherName("condm2"))
+        .setCondition(ImmutableList.of(cond1, cond2))
+        .setMatchType(MatchType.Any)
+        .build();
+    ConditionMatcher condm3 = new ConditionMatcherBuilder()
+        .setName(new ConditionMatcherName("condm3"))
+        .setCondition(ImmutableList.of(cond3))
+        .setMatchType(MatchType.None)
+        .build();
+
+    Clause clause1 = new ClauseBuilder()
+        .setName(new ClauseName("clause1"))
+        .setConsumerMatchers(new ConsumerMatchersBuilder()
+            .setRequirementMatcher(ImmutableList.of(rm1))
+            .setConditionMatcher(ImmutableList.of(condm1, condm2, condm3))
+            .build())
+        .setProviderMatchers(new ProviderMatchersBuilder()
+            .setCapabilityMatcher(ImmutableList.of(capm1))
+            .build())
+        .setSubjectRefs(ImmutableList.of(s1.getName()))
+        .build();
+    Clause clause2 = new ClauseBuilder()
+        .setName(new ClauseName("clause2"))
+        .setConsumerMatchers(new ConsumerMatchersBuilder()
+            .setRequirementMatcher(ImmutableList.of(rm3))
+            .build())
+        .setProviderMatchers(new ProviderMatchersBuilder()
+            .setCapabilityMatcher(ImmutableList.of(capm3))
+            .build())
+        .setSubjectRefs(ImmutableList.of(s2.getName()))
+        .build();
+    Clause clause3 = new ClauseBuilder()
+        .setName(new ClauseName("clause3"))
+        .setSubjectRefs(ImmutableList.of(s3.getName(), s2.getName()))
+        .build();
+    Clause clause0 = new ClauseBuilder()
+        .setName(new ClauseName("clause0"))
+        .build();
+    Clause clause00 = new ClauseBuilder()
+        .setName(new ClauseName("clause00"))
+        .setConsumerMatchers(new ConsumerMatchersBuilder().build())
+        .setProviderMatchers(new ProviderMatchersBuilder().build())
+        .build();
+    
+    Contract contract1 = new ContractBuilder()
+        .setId(new ContractId("c9eea992-ba51-4e11-b797-986853832ad9"))
+        .setTarget(ImmutableList.of(t1))
+        .setClause(ImmutableList.of(clause1, clause0, clause00))
+        .setSubject(ImmutableList.of(s1))
+        .build();
+    Contract contract2 = new ContractBuilder()
+        .setId(new ContractId("3a3b67ff-1795-4dc0-a7b2-2c3453872e4e"))
+        .setTarget(ImmutableList.of(t1, t2))
+        .setClause(ImmutableList.of(clause3))
+        .setSubject(ImmutableList.of(s2))
+        .build();
+    Contract contract0 = new ContractBuilder()
+        .setId(new ContractId("ce467a3c-2c7b-4e9e-a575-7da1fbdf1833"))
+        .build();
+    Contract contract00 = new ContractBuilder()
+        .setId(new ContractId("79de88e8-b37f-4764-a1a3-7f3b37b15433"))
+        .setTarget(ImmutableList.of(t0))
+        .build();
+    
+    ConsumerNamedSelector cns1 = new ConsumerNamedSelectorBuilder()
+        .setName(new SelectorName("cns1"))
+        .setContract(ImmutableList.of(contract1.getId()))
+        .setRequirement(ImmutableList.of(r1, r3))
+        .build();
+    ConsumerNamedSelector cns2 = new ConsumerNamedSelectorBuilder()
+        .setName(new SelectorName("cns2"))
+        .setContract(ImmutableList.of(contract2.getId()))
+        .setRequirement(ImmutableList.of(r1, r3))
+        .build();
+    ProviderNamedSelector pns1 = new ProviderNamedSelectorBuilder()
+        .setName(new SelectorName("pns1"))
+        .setContract(ImmutableList.of(contract1.getId(), contract2.getId()))
+        .setCapability(ImmutableList.of(cap1, cap3))
+        .build();
+    
+    QualityMatcher qm1 = new QualityMatcherBuilder()
+        .setName(new QualityMatcherName("qm1"))
+        .setMatcherQuality(ImmutableList.of(new MatcherQualityBuilder(q1).build()))
+        .build();
+    QualityMatcher qm3 = new QualityMatcherBuilder()
+         .setName(new QualityMatcherName("qm3"))
+         .setMatcherQuality(ImmutableList.of(new MatcherQualityBuilder(q3).build()))
+         .build();
+    ConsumerTargetSelector cts1 = new ConsumerTargetSelectorBuilder()
+        .setName(new SelectorName("cts1"))
+        .setQualityMatcher(ImmutableList.of(qm1))
+        .build();
+    ProviderTargetSelector pts1 = new ProviderTargetSelectorBuilder()
+        .setName(new SelectorName("pts1"))
+        .setQualityMatcher(ImmutableList.of(qm3))
+        .build();
+    
+    EndpointGroup eg1 = new EndpointGroupBuilder()
+        .setId(new EndpointGroupId("12802e21-8602-40ec-91d3-a75a296881ab"))
+        .setConsumerNamedSelector(ImmutableList.of(cns1))
+        .build();
+    EndpointGroup eg2 = new EndpointGroupBuilder()
+        .setId(new EndpointGroupId("66bb92ff-6e4c-41f1-8c7d-baa322016ab5"))
+        .setProviderNamedSelector(ImmutableList.of(pns1))
+        .build();
+    EndpointGroup eg3 = new EndpointGroupBuilder()
+        .setId(new EndpointGroupId("0ed93cb5-28ee-46bd-a5a1-41d6aa88dae5"))
+        .setConsumerNamedSelector(ImmutableList.of(cns1, cns2))
+        .build();
+    EndpointGroup eg4 = new EndpointGroupBuilder()
+        .setId(new EndpointGroupId("51eaf011-94a9-4cb1-b12d-149b77c5c016"))
+        .setConsumerTargetSelector(ImmutableList.of(cts1))
+        .build();
+    EndpointGroup eg5 = new EndpointGroupBuilder()
+        .setId(new EndpointGroupId("92344738-ba37-4d69-b9e5-904eebdad585"))
+        .setProviderTargetSelector(ImmutableList.of(pts1))
+        .build();
+    EndpointGroup eg0 = new EndpointGroupBuilder()
+        .setId(new EndpointGroupId("64e03313-d6d8-43cb-ae4d-5a9b0a410c91"))
+        .build();
+
+    Tenant tenant1 = new TenantBuilder()
+        .setId(new TenantId("144b9aec-ef06-44f1-a50c-2fe5be456feb"))
+        .setContract(ImmutableList.of(contract1, contract2))
+        .setEndpointGroup(ImmutableList.of(eg1, eg2))
+        .build();
+    Tenant tenant2 = new TenantBuilder()
+        .setId(new TenantId("138a2bc3-d3cb-4588-ad7a-63c9f19ce3e5"))
+        .setContract(ImmutableList.of(contract1, contract2))
+        .setEndpointGroup(ImmutableList.of(eg1, eg2, eg3))
+        .build();
+    Tenant tenant3 = new TenantBuilder()
+        .setId(new TenantId("d1feede4-c31f-4232-ace2-93fcd065af1d"))
+        .setContract(ImmutableList.of(contract1, contract2))
+        .setEndpointGroup(ImmutableList.of(eg4, eg5))
+        .build();
+    Tenant tenant0 = new TenantBuilder().build();
+    Tenant tenant00 = new TenantBuilder()
+        .setContract(ImmutableList.of(contract0, contract00))
+        .setEndpointGroup(ImmutableList.of(eg0))
+        .build();
+
+    PolicyResolver resolver;
+    
+    @Before
+    public void setup() throws Exception {
+        resolver = new PolicyResolver(null);
+    }
+    
+    public void verifyMatches(List<ContractId> contrids,
+                              List<TenantId> contrtids,
+                              List<ContractMatch> matches) {
+        HashSet<ContractMatchKey> v = new HashSet<>();
+        for (int i = 0; i < contrids.size(); i++) {
+            v.add(new ContractMatchKey(contrtids.get(i), contrids.get(i)));
+        }
+        assertEquals(contrids.size(), matches.size());
+        for (ContractMatch m : matches) {
+            ContractMatchKey k = 
+                    new ContractMatchKey(m.contractTenant.getId(), 
+                                         m.contract.getId());
+            assertTrue(v.contains(k));
+        }
+    }
+    
+    @Test
+    public void testContractSelection() throws Exception {
+        // named selectors
+        Table<EgKey, EgKey, List<ContractMatch>> contractMatches =
+                resolver.selectContracts(tenant1);
+        assertEquals(1, contractMatches.size());
+        List<ContractMatch> matches = 
+                contractMatches.get(new EgKey(tenant1.getId(), eg1.getId()),
+                                    new EgKey(tenant1.getId(), eg2.getId()));
+        verifyMatches(ImmutableList.of(contract1.getId()),
+                      ImmutableList.of(tenant1.getId()),
+                      matches);
+
+        contractMatches = resolver.selectContracts(tenant2);
+        assertEquals(2, contractMatches.size());
+        matches = contractMatches.get(new EgKey(tenant2.getId(), eg1.getId()),
+                                      new EgKey(tenant2.getId(), eg2.getId()));
+        verifyMatches(ImmutableList.of(contract1.getId()),
+                      ImmutableList.of(tenant2.getId()),
+                      matches);
+        
+        matches = contractMatches.get(new EgKey(tenant2.getId(), eg3.getId()),
+                                      new EgKey(tenant2.getId(), eg2.getId()));
+        verifyMatches(ImmutableList.of(contract2.getId(), contract1.getId()),
+                      ImmutableList.of(tenant2.getId(), tenant2.getId()),
+                      matches);
+        
+        // target selectors
+        contractMatches = resolver.selectContracts(tenant3);
+        assertEquals(1, contractMatches.size());
+        matches = contractMatches.get(new EgKey(tenant3.getId(), eg4.getId()),
+                                      new EgKey(tenant3.getId(), eg5.getId()));
+        verifyMatches(ImmutableList.of(contract2.getId()),
+                      ImmutableList.of(tenant3.getId()),
+                      matches);
+        
+        // empty matches
+        contractMatches = resolver.selectContracts(tenant0);
+        assertEquals(0, contractMatches.size());
+
+        contractMatches = resolver.selectContracts(tenant00);
+        assertEquals(0, contractMatches.size());
+    }
+
+    @Test
+    public void testSubjectSelection() throws Exception {
+        ConditionSet cs = 
+                new ConditionSet(ImmutableSet.of(cond1.getName()), 
+                                 ImmutableSet.of(cond3.getName()),
+                                 ImmutableSet.of(ImmutableSet.of(cond1.getName(), 
+                                                                 cond2.getName())));
+
+        Table<EgKey, EgKey, List<ContractMatch>> contractMatches =
+                resolver.selectContracts(tenant1);
+        Table<EgKey, EgKey, Policy> policy = 
+                resolver.selectSubjects(contractMatches);
+        assertEquals(1, policy.size());
+        Policy p = policy.get(new EgKey(tenant1.getId(), eg1.getId()),
+                              new EgKey(tenant1.getId(), eg2.getId()));
+        List<RuleGroup> rules = p.ruleMap.get(cs, ConditionSet.EMPTY);
+        assertNotNull(rules);
+        assertEquals(1, rules.size());
+        RuleGroup rg = rules.get(0);
+        assertEquals(tenant1.getId(), rg.contractTenant.getId());
+        assertEquals(contract1.getId(), rg.relatedContract.getId());
+        assertEquals(s1.getName(), rg.relatedSubject);
+        assertEquals(1, rg.rules.size());
+        assertEquals(rule1.getName(), rg.rules.get(0).getName());
+
+        contractMatches = resolver.selectContracts(tenant2);
+        policy = resolver.selectSubjects(contractMatches);
+
+        assertEquals(2, policy.size());
+        p = policy.get(new EgKey(tenant2.getId(), eg3.getId()),
+                       new EgKey(tenant2.getId(), eg2.getId()));
+        rules = p.ruleMap.get(cs, ConditionSet.EMPTY);
+        assertNotNull(rules);
+        assertEquals(1, rules.size());
+        rg = rules.get(0);
+        assertEquals(tenant2.getId(), rg.contractTenant.getId());
+        assertEquals(contract1.getId(), rg.relatedContract.getId());
+        assertEquals(s1.getName(), rg.relatedSubject);
+        assertEquals(1, rg.rules.size());
+        assertEquals(rule1.getName(), rg.rules.get(0).getName());
+
+        rules = p.ruleMap.get(ConditionSet.EMPTY, ConditionSet.EMPTY);
+        assertNotNull(rules);
+        assertEquals(1, rules.size());
+        rg = rules.get(0);
+        assertEquals(tenant2.getId(), rg.contractTenant.getId());
+        assertEquals(contract2.getId(), rg.relatedContract.getId());
+        assertEquals(s2.getName(), rg.relatedSubject);
+        assertEquals(1, rg.rules.size());
+        assertEquals(rule2.getName(), rg.rules.get(0).getName());
+        
+        p = policy.get(new EgKey(tenant2.getId(), eg1.getId()),
+                       new EgKey(tenant2.getId(), eg2.getId()));
+        rules = p.ruleMap.get(cs, ConditionSet.EMPTY);
+        assertNotNull(rules);
+        assertEquals(1, rules.size());
+        rg = rules.get(0);
+        assertEquals(tenant2.getId(), rg.contractTenant.getId());
+        assertEquals(contract1.getId(), rg.relatedContract.getId());
+        assertEquals(s1.getName(), rg.relatedSubject);
+        assertEquals(1, rg.rules.size());
+        assertEquals(rule1.getName(), rg.rules.get(0).getName());
+
+        contractMatches = resolver.selectContracts(tenant3);
+        policy = resolver.selectSubjects(contractMatches);
+
+        assertEquals(1, policy.size());
+        p = policy.get(new EgKey(tenant3.getId(), eg4.getId()),
+                       new EgKey(tenant3.getId(), eg5.getId()));
+        rules = p.ruleMap.get(ConditionSet.EMPTY, ConditionSet.EMPTY);
+        assertNotNull(rules);
+        assertEquals(1, rules.size());
+        rg = rules.get(0);
+        assertEquals(tenant3.getId(), rg.contractTenant.getId());
+        assertEquals(contract2.getId(), rg.relatedContract.getId());
+        assertEquals(s2.getName(), rg.relatedSubject);
+        assertEquals(1, rg.rules.size());
+        assertEquals(rule2.getName(), rg.rules.get(0).getName());
+    }
+    
+    @Test
+    public void testUpdates() throws Exception {
+        // test subscriptions and update notifications
+    }
+
+    private static class ContractMatchKey {
+        TenantId tenant;
+        ContractId contract;
+        public ContractMatchKey(TenantId tenant, ContractId contract) {
+            super();
+            this.tenant = tenant;
+            this.contract = contract;
+        }
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result +
+                     ((contract == null) ? 0 : contract.hashCode());
+            result = prime * result +
+                     ((tenant == null) ? 0 : tenant.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;
+            ContractMatchKey other = (ContractMatchKey) obj;
+            if (contract == null) {
+                if (other.contract != null)
+                    return false;
+            } else if (!contract.equals(other.contract))
+                return false;
+            if (tenant == null) {
+                if (other.tenant != null)
+                    return false;
+            } else if (!tenant.equals(other.tenant))
+                return false;
+            return true;
+        }
+        @Override
+        public String toString() {
+            return "ContractMatchKey [tenant=" + tenant + ", contract=" +
+                   contract + "]";
+        }
+    }
+
+}