CORD-349 Support VLAN cross-connect traffic

Change related to this topic:
- Support VLAN cross-connect traffic
    Utilize ports subjectClass to achieve. For non-xConnect port, set interface VLAN to -1
- Remove VLAN checking since we have multiple VLANs per port
- Hash the L2 interface group key generation to include VLAN as well
- Update the network-cfg.json sample

Other refactoring changes:
- Read next objective stores from srManager directly
- Use constant for flow priority
- CORD-267 Javadoc fix

Change-Id: I4ca8c2d9c8b3633a4a0101c5070d19343f7e5b90
diff --git a/src/main/java/org/onosproject/segmentrouting/ArpHandler.java b/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
index 6d9171b..5132489 100644
--- a/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
@@ -41,6 +41,10 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+/**
+ * Handler of ARP packets that responses or forwards ARP packets that
+ * are sent to the controller.
+ */
 public class ArpHandler {
 
     private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
diff --git a/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
index 1a37efd..9e24da4 100644
--- a/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -37,6 +37,10 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+/**
+ * Default routing handler that is responsible for route computing and
+ * routing rule population.
+ */
 public class DefaultRoutingHandler {
 
     private static Logger log = LoggerFactory
@@ -512,6 +516,7 @@
      */
     public void populatePortAddressingRules(DeviceId deviceId) {
         rulePopulator.populateRouterMacVlanFilters(deviceId);
+        rulePopulator.populateXConnectVlanFilters(deviceId);
         rulePopulator.populateRouterIpPunts(deviceId);
     }
 
diff --git a/src/main/java/org/onosproject/segmentrouting/DefaultTunnel.java b/src/main/java/org/onosproject/segmentrouting/DefaultTunnel.java
index 7016143..7d757c4 100644
--- a/src/main/java/org/onosproject/segmentrouting/DefaultTunnel.java
+++ b/src/main/java/org/onosproject/segmentrouting/DefaultTunnel.java
@@ -20,7 +20,7 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Tunnel class.
+ * Default Tunnel class.
  */
 public class DefaultTunnel implements Tunnel {
 
diff --git a/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
index d1dc8dd..a8d16e8 100644
--- a/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
@@ -38,6 +38,10 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+/**
+ * Handler of ICMP packets that responses or forwards ICMP packets that
+ * are sent to the controller.
+ */
 public class IcmpHandler {
 
     private static Logger log = LoggerFactory.getLogger(IcmpHandler.class);
diff --git a/src/main/java/org/onosproject/segmentrouting/IpHandler.java b/src/main/java/org/onosproject/segmentrouting/IpHandler.java
index d6a9dcf..4a5a3e3 100644
--- a/src/main/java/org/onosproject/segmentrouting/IpHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/IpHandler.java
@@ -37,6 +37,10 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+/**
+ * Handler of IP packets that forwards IP packets that are sent to the controller,
+ * except the ICMP packets which are processed by @link{IcmpHandler}.
+ */
 public class IpHandler {
 
     private static Logger log = LoggerFactory.getLogger(IpHandler.class);
diff --git a/src/main/java/org/onosproject/segmentrouting/Policy.java b/src/main/java/org/onosproject/segmentrouting/Policy.java
index 2e41795..6133254 100644
--- a/src/main/java/org/onosproject/segmentrouting/Policy.java
+++ b/src/main/java/org/onosproject/segmentrouting/Policy.java
@@ -24,16 +24,24 @@
      * Enums for policy type.
      */
     enum Type {
-        // Tunnel flow policy type
+        /**
+         * Tunnel flow policy type.
+         */
         TUNNEL_FLOW,
 
-        // Load balancing policy type
+        /**
+         * Load balancing policy type.
+         */
         LOADBALANCE,
 
-        // policy to avoid specific routers or links
+        /**
+         * policy to avoid specific routers or links.
+         */
         AVOID,
 
-        // Access Control policy type
+        /**
+         * Access Control policy type.
+         */
         DENY
     }
 
diff --git a/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java b/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java
index 0a4c26d..308d026 100644
--- a/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java
@@ -48,18 +48,43 @@
     private FlowObjectiveService flowObjectiveService;
     private TunnelHandler tunnelHandler;
     private final EventuallyConsistentMap<String, Policy> policyStore;
-
+    /**
+     * Result of policy creation.
+     */
     public enum Result {
+        /**
+         * Success.
+         */
         SUCCESS,
+
+        /**
+         * The same policy exists already.
+         */
         POLICY_EXISTS,
+
+        /**
+         * The policy ID exists already.
+         */
         ID_EXISTS,
+
+        /**
+         * Cannot find associated tunnel.
+         */
         TUNNEL_NOT_FOUND,
+
+        /**
+         * Policy was not found.
+         */
         POLICY_NOT_FOUND,
+
+        /**
+         * Policy type {} is not supported yet.
+         */
         UNSUPPORTED_TYPE
     }
 
     /**
-     * Creates a reference.
+     * Constructs policy handler.
      *
      * @param appId                segment routing application ID
      * @param deviceConfiguration  DeviceConfiguration reference
diff --git a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 03b29a5..6017e8b 100644
--- a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -23,6 +23,7 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
 import org.onosproject.segmentrouting.config.DeviceConfiguration;
 import org.onosproject.segmentrouting.grouphandler.NeighborSet;
@@ -49,11 +50,15 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+/**
+ * Populator of segment routing flow rules.
+ */
 public class RoutingRulePopulator {
     private static final Logger log = LoggerFactory
             .getLogger(RoutingRulePopulator.class);
@@ -63,6 +68,10 @@
     private DeviceConfiguration config;
 
     private static final int HIGHEST_PRIORITY = 0xffff;
+    //
+    private static final int XCONNECT_PRIORITY = 1000;
+    private static final int DEFAULT_PRIORITY = 100;
+    private static final int FLOOD_PRIORITY = 5;
     private static final long OFPP_MAX = 0xffffff00L;
 
 
@@ -120,6 +129,14 @@
         rulePopulationCounter.incrementAndGet();
     }
 
+    /**
+     * Removes IP rules for host when the host is gone.
+     *
+     * @param deviceId device ID of the device that host attaches to
+     * @param hostIp IP address of the host
+     * @param hostMac MAC address of the host
+     * @param outPort port that host attaches to
+     */
     public void revokeIpRuleForHost(DeviceId deviceId, Ip4Address hostIp,
             MacAddress hostMac, PortNumber outPort) {
         log.debug("Revoke IP table entry for host {} at {}:{}",
@@ -175,7 +192,7 @@
                 .withSelector(selector)
                 .nextStep(portNextObjId)
                 .fromApp(srManager.appId).makePermanent()
-                .withPriority(100).withFlag(ForwardingObjective.Flag.SPECIFIC);
+                .withPriority(DEFAULT_PRIORITY).withFlag(ForwardingObjective.Flag.SPECIFIC);
     }
 
     /**
@@ -369,7 +386,7 @@
         for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
             ((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId)
                     .makePermanent()).withSelector(selector)
-                    .withPriority(100))
+                    .withPriority(DEFAULT_PRIORITY))
                     .withFlag(ForwardingObjective.Flag.SPECIFIC);
             srManager.flowObjectiveService.
                 forward(deviceId,
@@ -464,7 +481,8 @@
                 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
                 fob.withKey(Criteria.matchInPort(port.number()))
                 .addCondition(Criteria.matchEthDst(deviceMac))
-                .addCondition(Criteria.matchVlanId(VlanId.NONE));
+                .addCondition(Criteria.matchVlanId(VlanId.NONE))
+                .withPriority(DEFAULT_PRIORITY);
                 // vlan assignment is valid only if this instance is master
                 if (srManager.mastershipService.isLocalMaster(deviceId)) {
                     TrafficTreatment tt = DefaultTrafficTreatment.builder()
@@ -558,7 +576,7 @@
             fob.withFlag(Flag.SPECIFIC)
                     .withSelector(sbuilder.build())
                     .nextStep(nextId)
-                    .withPriority(5)
+                    .withPriority(FLOOD_PRIORITY)
                     .fromApp(srManager.appId)
                     .makePermanent();
 
@@ -572,6 +590,86 @@
         });
     }
 
+    /**
+     * Creates a filtering objective to permit VLAN cross-connect traffic.
+     *
+     * @param deviceId the DPID of the switch
+     */
+    public void populateXConnectVlanFilters(DeviceId deviceId) {
+        Map<VlanId, List<ConnectPoint>> xConnectsForDevice =
+                config.getXConnects();
+        xConnectsForDevice.forEach((vlanId, connectPoints) -> {
+            // Only proceed  the xConnect for given device
+            for (ConnectPoint connectPoint : connectPoints) {
+                if (!connectPoint.deviceId().equals(deviceId)) {
+                    return;
+                }
+            }
+
+            connectPoints.forEach(connectPoint -> {
+                FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
+                fob.withKey(Criteria.matchInPort(connectPoint.port()))
+                        .addCondition(Criteria.matchVlanId(vlanId))
+                        .addCondition(Criteria.matchEthDst(MacAddress.NONE))
+                        .withPriority(XCONNECT_PRIORITY);
+
+                fob.permit().fromApp(srManager.appId);
+                srManager.flowObjectiveService
+                        .filter(deviceId, fob.add(new SRObjectiveContext(deviceId,
+                                SRObjectiveContext.ObjectiveType.FILTER)));
+            });
+        });
+    }
+
+    /**
+     * Populates a forwarding objective that points the VLAN cross-connect
+     * packets to a broadcast group.
+     *
+     * @param deviceId switch ID to set the rules
+     */
+    public void populateXConnectBroadcastRule(DeviceId deviceId) {
+        Map<VlanId, List<ConnectPoint>> xConnects =
+                config.getXConnects();
+        xConnects.forEach((vlanId, connectPoints) -> {
+            // Only proceed  the xConnect for given device
+            for (ConnectPoint connectPoint : connectPoints) {
+                if (!connectPoint.deviceId().equals(deviceId)) {
+                    return;
+                }
+            }
+
+            int nextId = srManager.getXConnectNextObjectiveId(deviceId, vlanId);
+            if (nextId < 0) {
+                log.error("Cannot install cross-connect broadcast rule in dev:{} " +
+                        "due to missing nextId:{}", deviceId, nextId);
+                return;
+            }
+
+            /*
+             * Driver should treat objectives with MacAddress.NONE and !VlanId.NONE
+             * as the VLAN cross-connect broadcast rules
+             */
+            TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+            sbuilder.matchVlanId(vlanId);
+            sbuilder.matchEthDst(MacAddress.NONE);
+
+            ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
+            fob.withFlag(Flag.SPECIFIC)
+                    .withSelector(sbuilder.build())
+                    .nextStep(nextId)
+                    .withPriority(DEFAULT_PRIORITY)
+                    .fromApp(srManager.appId)
+                    .makePermanent();
+
+            srManager.flowObjectiveService.forward(
+                    deviceId,
+                    fob.add(new SRObjectiveContext(
+                            deviceId,
+                            SRObjectiveContext.ObjectiveType.FORWARDING)
+                    )
+            );
+        });
+    }
 
     private static class SRObjectiveContext implements ObjectiveContext {
         enum ObjectiveType {
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 0f20451..592c0b1 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -69,7 +69,6 @@
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.flowobjective.FlowObjectiveService;
 import org.onosproject.net.host.HostService;
-import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.link.LinkEvent;
 import org.onosproject.net.link.LinkListener;
 import org.onosproject.net.link.LinkService;
@@ -77,8 +76,8 @@
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
-import org.onosproject.net.topology.TopologyService;
 import org.onosproject.segmentrouting.grouphandler.SubnetNextObjectiveStoreKey;
+import org.onosproject.segmentrouting.grouphandler.XConnectNextObjectiveStoreKey;
 import org.onosproject.store.service.EventuallyConsistentMap;
 import org.onosproject.store.service.EventuallyConsistentMapBuilder;
 import org.onosproject.store.service.StorageService;
@@ -102,6 +101,9 @@
 
 @Service
 @Component(immediate = true)
+/**
+ * Segment routing manager.
+ */
 public class SegmentRoutingManager implements SegmentRoutingService {
 
     private static Logger log = LoggerFactory
@@ -111,15 +113,9 @@
     protected CoreService coreService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected TopologyService topologyService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected IntentService intentService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostService hostService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -157,20 +153,31 @@
     @SuppressWarnings("rawtypes")
     private ConcurrentLinkedQueue<Event> eventQueue = new ConcurrentLinkedQueue<Event>();
     private Map<DeviceId, DefaultGroupHandler> groupHandlerMap =
-            new ConcurrentHashMap<DeviceId, DefaultGroupHandler>();
-    // Per device next objective ID store with (device id + neighbor set) as key
-    private EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
+            new ConcurrentHashMap<>();
+    /**
+     * Per device next objective ID store with (device id + neighbor set) as key.
+     */
+    public EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
             nsNextObjStore = null;
-    // Per device next objective ID store with (device id + subnet) as key
-    private EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
+    /**
+     * Per device next objective ID store with (device id + subnet) as key.
+     */
+    public EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
             subnetNextObjStore = null;
-    // Per device next objective ID store with (device id + port) as key
-    private EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
+    /**
+     * Per device next objective ID store with (device id + port) as key.
+     */
+    public EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
             portNextObjStore = null;
+    /**
+     * Per cross-connect objective ID store with VLAN ID as key.
+     */
+    public EventuallyConsistentMap<XConnectNextObjectiveStoreKey, Integer>
+            xConnectNextObjStore = null;
     // Per device, per-subnet assigned-vlans store, with (device id + subnet
     // IPv4 prefix) as key
     private EventuallyConsistentMap<SubnetAssignedVidStoreKey, VlanId>
-        subnetVidStore = null;
+            subnetVidStore = null;
     private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
     private EventuallyConsistentMap<String, Policy> policyStore = null;
 
@@ -204,7 +211,13 @@
 
     private KryoNamespace.Builder kryoBuilder = null;
 
+    /**
+     * The starting value of per-subnet VLAN ID assignment.
+     */
     private static final short ASSIGNED_VLAN_START = 4093;
+    /**
+     * The default VLAN ID assigned to the interfaces without subnet config.
+     */
     public static final short ASSIGNED_VLAN_NO_SUBNET = 4094;
 
     @Activate
@@ -262,6 +275,15 @@
                 .withTimestampProvider((k, v) -> new WallClockTimestamp())
                 .build();
 
+        log.debug("Creating EC map xconnectnextobjectivestore");
+        EventuallyConsistentMapBuilder<XConnectNextObjectiveStoreKey, Integer>
+                xConnectNextObjStoreBuilder = storageService.eventuallyConsistentMapBuilder();
+        xConnectNextObjStore = xConnectNextObjStoreBuilder
+                .withName("xconnectnextobjectivestore")
+                .withSerializer(kryoBuilder)
+                .withTimestampProvider((k, v) -> new WallClockTimestamp())
+                .build();
+
         EventuallyConsistentMapBuilder<String, Tunnel> tunnelMapBuilder =
                 storageService.eventuallyConsistentMapBuilder();
         tunnelStore = tunnelMapBuilder
@@ -394,9 +416,6 @@
      * Vlan id 4094 expected to be used for all ports that are not assigned subnets.
      * Vlan id 4095 is reserved and unused. Only a single vlan id is assigned
      * per subnet.
-     * XXX This method should avoid any vlans configured on the ports, but
-     *     currently the app works only on untagged packets and as a result
-     *     ignores any vlan configuration.
      *
      * @param deviceId switch dpid
      * @param subnet IPv4 prefix for which assigned vlan is desired
@@ -404,6 +423,7 @@
      *         null if no vlan assignment was found and this instance is not
      *         the master for the device.
      */
+    // TODO: We should avoid assigning VLAN IDs that are used by VLAN cross-connection.
     public VlanId getSubnetAssignedVlanId(DeviceId deviceId, Ip4Prefix subnet) {
         VlanId assignedVid = subnetVidStore.get(new SubnetAssignedVidStoreKey(
                                                         deviceId, subnet));
@@ -508,7 +528,26 @@
         if (ghdlr != null) {
             return ghdlr.getPortNextObjectiveId(portNum, treatment, meta);
         } else {
-            log.warn("getPortNextObjectiveId query -  groupHandler for device {}"
+            log.warn("getPortNextObjectiveId query - groupHandler for device {}"
+                    + " not found", deviceId);
+            return -1;
+        }
+    }
+
+    /**
+     * Returns the next objective ID of type broadcast associated with the VLAN
+     * cross-connection.
+     *
+     * @param deviceId Device ID for the cross-connection
+     * @param vlanId VLAN ID for the cross-connection
+     * @return next objective ID or -1 if it was not found
+     */
+    public int getXConnectNextObjectiveId(DeviceId deviceId, VlanId vlanId) {
+        DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId);
+        if (ghdlr != null) {
+            return ghdlr.getXConnectNextObjectiveId(vlanId);
+        } else {
+            log.warn("getPortNextObjectiveId query - groupHandler for device {}"
                     + " not found", deviceId);
             return -1;
         }
@@ -707,9 +746,6 @@
                                            deviceConfiguration,
                                            linkService,
                                            flowObjectiveService,
-                                           nsNextObjStore,
-                                           subnetNextObjStore,
-                                           portNextObjStore,
                                            this);
             } catch (DeviceConfigNotFoundException e) {
                 log.warn(e.getMessage() + " Aborting processDeviceAdded.");
@@ -727,6 +763,8 @@
             DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
             groupHandler.createGroupsFromSubnetConfig();
             routingRulePopulator.populateSubnetBroadcastRule(device.id());
+            groupHandler.createGroupsForXConnect(device.id());
+            routingRulePopulator.populateXConnectBroadcastRule(device.id());
         }
     }
 
@@ -742,10 +780,18 @@
     private class InternalConfigListener implements NetworkConfigListener {
         SegmentRoutingManager segmentRoutingManager;
 
+        /**
+         * Constructs the internal network config listener.
+         *
+         * @param srMgr segment routing manager
+         */
         public InternalConfigListener(SegmentRoutingManager srMgr) {
             this.segmentRoutingManager = srMgr;
         }
 
+        /**
+         * Reads network config and initializes related data structure accordingly.
+         */
         public void configureNetwork() {
             deviceConfiguration = new DeviceConfiguration(segmentRoutingManager.cfgService);
 
@@ -777,9 +823,6 @@
                                                    deviceConfiguration,
                                                    linkService,
                                                    flowObjectiveService,
-                                                   nsNextObjStore,
-                                                   subnetNextObjStore,
-                                                   portNextObjStore,
                                                    segmentRoutingManager);
                     } catch (DeviceConfigNotFoundException e) {
                         log.warn(e.getMessage() + " Aborting configureNetwork.");
@@ -798,6 +841,8 @@
                     DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
                     groupHandler.createGroupsFromSubnetConfig();
                     routingRulePopulator.populateSubnetBroadcastRule(device.id());
+                    groupHandler.createGroupsForXConnect(device.id());
+                    routingRulePopulator.populateXConnectBroadcastRule(device.id());
                 }
             }
 
diff --git a/src/main/java/org/onosproject/segmentrouting/SubnetAssignedVidStoreKey.java b/src/main/java/org/onosproject/segmentrouting/SubnetAssignedVidStoreKey.java
index 84b44c9..b721d0e 100644
--- a/src/main/java/org/onosproject/segmentrouting/SubnetAssignedVidStoreKey.java
+++ b/src/main/java/org/onosproject/segmentrouting/SubnetAssignedVidStoreKey.java
@@ -6,13 +6,18 @@
 import org.onosproject.net.DeviceId;
 
 /**
- * Class definition for key used to map per device subnets to assigned Vlan ids.
- *
+ * Key of assigned VLAN ID store.
  */
 public class SubnetAssignedVidStoreKey {
     private final DeviceId deviceId;
     private final Ip4Prefix subnet;
 
+    /**
+     * Constructs the key of per subnet VLAN ID store.
+     *
+     * @param deviceId device ID of the VLAN cross-connection
+     * @param subnet subnet information
+     */
     public SubnetAssignedVidStoreKey(DeviceId deviceId, Ip4Prefix subnet) {
         this.deviceId = deviceId;
         this.subnet = subnet;
diff --git a/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java b/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
index 5a82e71..8695874 100644
--- a/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
@@ -43,16 +43,54 @@
     private Map<DeviceId, DefaultGroupHandler> groupHandlerMap;
     private LinkService linkService;
 
+    /**
+     * Result of tunnel creation or removal.
+     */
     public enum Result {
+        /**
+         * Success.
+         */
         SUCCESS,
+
+        /**
+         * More than one router needs to specified to created a tunnel.
+         */
         WRONG_PATH,
+
+        /**
+         * The same tunnel exists already.
+         */
         TUNNEL_EXISTS,
+
+        /**
+         * The same tunnel ID exists already.
+         */
         ID_EXISTS,
+
+        /**
+         * Tunnel not found.
+         */
         TUNNEL_NOT_FOUND,
+
+        /**
+         * Cannot remove the tunnel used by a policy.
+         */
         TUNNEL_IN_USE,
+
+        /**
+         * Failed to create/remove groups for the tunnel.
+         */
         INTERNAL_ERROR
     }
 
+    /**
+     * Constructs tunnel handler.
+     *
+     * @param linkService link service
+     * @param deviceConfiguration device configuration
+     * @param groupHandlerMap group handler map
+     * @param tunnelStore tunnel store
+     */
     public TunnelHandler(LinkService linkService,
                          DeviceConfiguration deviceConfiguration,
                          Map<DeviceId, DefaultGroupHandler> groupHandlerMap,
diff --git a/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java b/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
index dbac596..3d2d337 100644
--- a/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
+++ b/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
@@ -19,6 +19,7 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.incubator.net.config.basics.ConfigException;
 import org.onosproject.incubator.net.config.basics.InterfaceConfig;
 import org.onosproject.incubator.net.intf.Interface;
@@ -33,6 +34,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -48,8 +50,8 @@
     private static final Logger log = LoggerFactory
             .getLogger(DeviceConfiguration.class);
     private final List<Integer> allSegmentIds = new ArrayList<>();
-    private final ConcurrentHashMap<DeviceId, SegmentRouterInfo> deviceConfigMap
-        = new ConcurrentHashMap<>();
+    private final Map<DeviceId, SegmentRouterInfo> deviceConfigMap = new ConcurrentHashMap<>();
+    private final Map<VlanId, List<ConnectPoint>> xConnects = new ConcurrentHashMap<>();
 
     private class SegmentRouterInfo {
         int nodeSid;
@@ -62,14 +64,14 @@
         Map<Integer, Set<Integer>> adjacencySids;
 
         public SegmentRouterInfo() {
-            this.gatewayIps = new HashMap<>();
-            this.subnets = new HashMap<>();
+            gatewayIps = new HashMap<>();
+            subnets = new HashMap<>();
         }
     }
 
     /**
-     * Constructor. Reads all the configuration for all devices of type
-     * Segment Router and organizes into various maps for easier access.
+     * Constructs device configuration for all Segment Router devices,
+     * organizing the data into various maps for easier access.
      *
      * @param cfgService config service
      */
@@ -88,8 +90,8 @@
             info.isEdge = config.isEdgeRouter();
             info.adjacencySids = config.adjacencySids();
 
-            this.deviceConfigMap.put(info.deviceId, info);
-            this.allSegmentIds.add(info.nodeSid);
+            deviceConfigMap.put(info.deviceId, info);
+            allSegmentIds.add(info.nodeSid);
         });
 
         // Read gatewayIps and subnets from port subject.
@@ -106,17 +108,42 @@
                 return;
             }
             networkInterfaces.forEach(networkInterface -> {
-                DeviceId dpid = networkInterface.connectPoint().deviceId();
-                PortNumber port = networkInterface.connectPoint().port();
-                SegmentRouterInfo info = this.deviceConfigMap.get(dpid);
+                VlanId vlanId = networkInterface.vlan();
+                ConnectPoint connectPoint = networkInterface.connectPoint();
+                DeviceId dpid = connectPoint.deviceId();
+                PortNumber port = connectPoint.port();
+                SegmentRouterInfo info = deviceConfigMap.get(dpid);
 
                 // skip if there is no corresponding device for this ConenctPoint
                 if (info != null) {
+                    // Extract subnet information
                     Set<InterfaceIpAddress> interfaceAddresses = networkInterface.ipAddresses();
                     interfaceAddresses.forEach(interfaceAddress -> {
                         info.gatewayIps.put(port, interfaceAddress.ipAddress().getIp4Address());
                         info.subnets.put(port, interfaceAddress.subnetAddress().getIp4Prefix());
                     });
+
+                    // Extract VLAN cross-connect information
+                    // Do not setup cross-connect if VLAN is NONE
+                    if (vlanId.equals(VlanId.NONE)) {
+                        return;
+                    }
+                    List<ConnectPoint> connectPoints = xConnects.get(vlanId);
+                    if (connectPoints != null) {
+                        if (connectPoints.size() != 1) {
+                            log.warn("Cross-connect should only have two endpoints. Aborting.");
+                            return;
+                        }
+                        if (!connectPoints.get(0).deviceId().equals(connectPoint.deviceId())) {
+                            log.warn("Cross-connect endpoints must be on the same switch. Aborting.");
+                            return;
+                        }
+                        connectPoints.add(connectPoint);
+                    } else {
+                        connectPoints = new LinkedList<>();
+                        connectPoints.add(connectPoint);
+                        xConnects.put(vlanId, connectPoints);
+                    }
                 }
             });
 
@@ -235,6 +262,11 @@
         return subnetPortMap;
     }
 
+    @Override
+    public Map<VlanId, List<ConnectPoint>> getXConnects() {
+        return xConnects;
+    }
+
     /**
      * Returns the device identifier or data plane identifier (dpid)
      * of a segment router given its segment id.
diff --git a/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java b/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java
index a39c956..9d4d884 100644
--- a/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java
+++ b/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java
@@ -21,6 +21,8 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
 
@@ -93,4 +95,11 @@
      * @return a map that contains all subnet-to-ports mapping of given device
      */
     Map<Ip4Prefix, List<PortNumber>> getSubnetPortsMap(DeviceId deviceId);
+
+    /**
+     * Returns the VLAN cross-connect configuration.
+     *
+     * @return A map of that maps VLAN ID to a list of cross-connect endpoints
+     */
+    Map<VlanId, List<ConnectPoint>> getXConnects();
 }
diff --git a/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java b/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
index f788925..64486c6 100644
--- a/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
+++ b/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
@@ -35,24 +35,24 @@
  * Configuration object for Segment Routing Application.
  */
 public class SegmentRoutingConfig extends Config<DeviceId> {
-    public static final String NAME = "name";
-    public static final String IP = "routerIp";
-    public static final String MAC = "routerMac";
-    public static final String SID = "nodeSid";
-    public static final String EDGE = "isEdgeRouter";
-    public static final String ADJSIDS = "adjacencySids";
-    public static final String ADJSID = "adjSid";
-    public static final String PORTS = "ports";
+    private static final String NAME = "name";
+    private static final String IP = "routerIp";
+    private static final String MAC = "routerMac";
+    private static final String SID = "nodeSid";
+    private static final String EDGE = "isEdgeRouter";
+    private static final String ADJSIDS = "adjacencySids";
+    private static final String ADJSID = "adjSid";
+    private static final String PORTS = "ports";
 
     @Override
     public boolean isValid() {
         return hasOnlyFields(NAME, IP, MAC, SID, EDGE, ADJSIDS, ADJSID, PORTS) &&
-                this.name() != null &&
-                this.routerIp() != null &&
-                this.routerMac() != null &&
-                this.nodeSid() != -1 &&
-                this.isEdgeRouter() != null &&
-                this.adjacencySids() != null;
+                name() != null &&
+                routerIp() != null &&
+                routerMac() != null &&
+                nodeSid() != -1 &&
+                isEdgeRouter() != null &&
+                adjacencySids() != null;
     }
 
     /**
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
index 4866b82..124ffa7 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
@@ -26,7 +26,6 @@
 import org.onosproject.net.link.LinkService;
 import org.onosproject.segmentrouting.SegmentRoutingManager;
 import org.onosproject.segmentrouting.config.DeviceProperties;
-import org.onosproject.store.service.EventuallyConsistentMap;
 
 /**
  * Default ECMP group handler creation module for an edge device.
@@ -47,22 +46,13 @@
  * 8) what about ecmp no label case
  */
 public class DefaultEdgeGroupHandler extends DefaultGroupHandler {
-    // TODO Access stores through srManager
     protected DefaultEdgeGroupHandler(DeviceId deviceId,
                                   ApplicationId appId,
                                   DeviceProperties config,
                                   LinkService linkService,
                                   FlowObjectiveService flowObjService,
-                                  EventuallyConsistentMap<
-                                          NeighborSetNextObjectiveStoreKey,
-                                          Integer> nsNextObjStore,
-                                  EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
-                                          Integer> subnetNextObjStore,
-                                  EventuallyConsistentMap<PortNextObjectiveStoreKey,
-                                          Integer> portNextObjStore,
                                   SegmentRoutingManager srManager) {
-        super(deviceId, appId, config, linkService, flowObjService,
-              nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager);
+        super(deviceId, appId, config, linkService, flowObjService, srManager);
     }
 
     @Override
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
index a4b89c1..4e64c4b 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -35,6 +35,7 @@
 import org.onlab.packet.VlanId;
 import org.onlab.util.KryoNamespace;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
@@ -48,8 +49,6 @@
 import org.onosproject.net.flowobjective.Objective;
 import org.onosproject.net.flowobjective.ObjectiveContext;
 import org.onosproject.net.flowobjective.ObjectiveError;
-import org.onosproject.net.group.DefaultGroupKey;
-import org.onosproject.net.group.GroupKey;
 import org.onosproject.net.link.LinkService;
 import org.onosproject.segmentrouting.SegmentRoutingManager;
 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
@@ -81,12 +80,14 @@
     //local store for ports on this device connected to neighbor-device-id
     protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
             new ConcurrentHashMap<>();
-    protected EventuallyConsistentMap<
-        NeighborSetNextObjectiveStoreKey, Integer> nsNextObjStore = null;
-    protected EventuallyConsistentMap<
-            SubnetNextObjectiveStoreKey, Integer> subnetNextObjStore = null;
-    protected EventuallyConsistentMap<
-            PortNextObjectiveStoreKey, Integer> portNextObjStore = null;
+    protected EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
+            nsNextObjStore = null;
+    protected EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
+            subnetNextObjStore = null;
+    protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
+            portNextObjStore = null;
+    protected EventuallyConsistentMap<XConnectNextObjectiveStoreKey, Integer>
+            xConnectNextObjStore = null;
     private SegmentRoutingManager srManager;
 
     protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
@@ -97,17 +98,10 @@
             .register(GroupBucketIdentifier.class)
             .register(GroupBucketIdentifier.BucketOutputType.class);
 
-    // TODO Access stores through srManager
     protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
                                   DeviceProperties config,
                                   LinkService linkService,
                                   FlowObjectiveService flowObjService,
-                                  EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
-                                          Integer> nsNextObjStore,
-                                  EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
-                                          Integer> subnetNextObjStore,
-                                  EventuallyConsistentMap<PortNextObjectiveStoreKey,
-                                          Integer> portNextObjStore,
                                   SegmentRoutingManager srManager) {
         this.deviceId = checkNotNull(deviceId);
         this.appId = checkNotNull(appId);
@@ -123,9 +117,10 @@
                     + " Skipping value assignment in DefaultGroupHandler");
         }
         this.flowObjectiveService = flowObjService;
-        this.nsNextObjStore = nsNextObjStore;
-        this.subnetNextObjStore = subnetNextObjStore;
-        this.portNextObjStore = portNextObjStore;
+        this.nsNextObjStore = srManager.nsNextObjStore;
+        this.subnetNextObjStore = srManager.subnetNextObjStore;
+        this.portNextObjStore = srManager.portNextObjStore;
+        this.xConnectNextObjStore = srManager.xConnectNextObjStore;
         this.srManager = srManager;
 
         populateNeighborMaps();
@@ -141,8 +136,7 @@
      * @param config interface to retrieve the device properties
      * @param linkService link service object
      * @param flowObjService flow objective service object
-     * @param nsNextObjStore NeighborSet next objective store map
-     * @param subnetNextObjStore subnet next objective store map
+     * @param srManager segment routing manager
      * @throws DeviceConfigNotFoundException if the device configuration is not found
      * @return default group handler type
      */
@@ -152,12 +146,6 @@
                                           DeviceProperties config,
                                           LinkService linkService,
                                           FlowObjectiveService flowObjService,
-                                          EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
-                                          Integer> nsNextObjStore,
-                                          EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
-                                          Integer> subnetNextObjStore,
-                                          EventuallyConsistentMap<PortNextObjectiveStoreKey,
-                                          Integer> portNextObjStore,
                                           SegmentRoutingManager srManager)
                                                   throws DeviceConfigNotFoundException {
         // handle possible exception in the caller
@@ -165,18 +153,12 @@
             return new DefaultEdgeGroupHandler(deviceId, appId, config,
                                                linkService,
                                                flowObjService,
-                                               nsNextObjStore,
-                                               subnetNextObjStore,
-                                               portNextObjStore,
                                                srManager
                                                );
         } else {
             return new DefaultTransitGroupHandler(deviceId, appId, config,
                                                   linkService,
                                                   flowObjService,
-                                                  nsNextObjStore,
-                                                  subnetNextObjStore,
-                                                  portNextObjStore,
                                                   srManager);
         }
     }
@@ -194,6 +176,8 @@
      * discovered on this device.
      *
      * @param newLink new neighbor link
+     * @param isMaster true if local instance is the master
+     *
      */
     public void linkUp(Link newLink, boolean isMaster) {
 
@@ -296,6 +280,7 @@
      * Performs group recovery procedures when a port goes down on this device.
      *
      * @param port port number that has gone down
+     * @param isMaster true if local instance is the master
      */
     public void portDown(PortNumber port, boolean isMaster) {
         if (portDeviceMap.get(port) == null) {
@@ -448,8 +433,8 @@
      */
     public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
                                       TrafficSelector meta) {
-        Integer nextId = portNextObjStore.
-                get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment));
+        Integer nextId = portNextObjStore
+                .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment));
         if (nextId == null) {
             log.trace("getPortNextObjectiveId in device{}: Next objective id "
                     + "not found for {} and {} creating", deviceId, portNum);
@@ -458,7 +443,33 @@
                          new PortNextObjectiveStoreKey(deviceId, portNum, treatment));
             if (nextId == null) {
                 log.warn("getPortNextObjectiveId: unable to create next obj"
-                        + "for dev:{} port{}", deviceId, portNum);
+                        + "for dev:{} port:{}", deviceId, portNum);
+                return -1;
+            }
+        }
+        return nextId;
+    }
+
+    /**
+     * Returns the next objective ID of type broadcast associated with the VLAN
+     * cross-connection.
+     *
+     * @param vlanId VLAN ID for the cross-connection
+     * @return int if found or created, -1 if there are errors during the
+     *         creation of the next objective
+     */
+    public int getXConnectNextObjectiveId(VlanId vlanId) {
+        Integer nextId = xConnectNextObjStore
+                .get(new XConnectNextObjectiveStoreKey(deviceId, vlanId));
+        if (nextId == null) {
+            log.trace("getXConnectNextObjectiveId: Next objective id "
+                    + "not found for device {} and vlan {}. Creating", deviceId, vlanId);
+            createGroupsForXConnect(deviceId);
+            nextId = xConnectNextObjStore.get(
+                    new XConnectNextObjectiveStoreKey(deviceId, vlanId));
+            if (nextId == null) {
+                log.warn("getXConnectNextObjectiveId: Next objective id "
+                        + "not found for device {} and vlan {}.", deviceId, vlanId);
                 return -1;
             }
         }
@@ -655,7 +666,6 @@
 
     /**
      * Creates broadcast groups for all ports in the same configured subnet.
-     *
      */
     public void createGroupsFromSubnetConfig() {
         Map<Ip4Prefix, List<PortNumber>> subnetPortMap =
@@ -700,6 +710,54 @@
         });
     }
 
+    /**
+     * Creates broadcast groups for VLAN cross-connect ports.
+     *
+     * @param deviceId the DPID of the switch
+     */
+    public void createGroupsForXConnect(DeviceId deviceId) {
+        Map<VlanId, List<ConnectPoint>> xConnectsForDevice = deviceConfig.getXConnects();
+
+        xConnectsForDevice.forEach((vlanId, connectPoints) -> {
+            // Only proceed  the xConnect for given device
+            for (ConnectPoint connectPoint : connectPoints) {
+                if (!connectPoint.deviceId().equals(deviceId)) {
+                    return;
+                }
+            }
+
+            // Check if the next obj is already in the store
+            XConnectNextObjectiveStoreKey key =
+                    new XConnectNextObjectiveStoreKey(deviceId, vlanId);
+            if (xConnectNextObjStore.containsKey(key)) {
+                log.debug("Cross-connect Broadcast group for device {} and vlanId {} exists",
+                        deviceId, vlanId);
+                return;
+            }
+
+            TrafficSelector metadata =
+                    DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
+            int nextId = flowObjectiveService.allocateNextId();
+
+            NextObjective.Builder nextObjBuilder = DefaultNextObjective
+                    .builder().withId(nextId)
+                    .withType(NextObjective.Type.BROADCAST).fromApp(appId)
+                    .withMeta(metadata);
+
+            connectPoints.forEach(connectPoint -> {
+                TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+                tBuilder.setOutput(connectPoint.port());
+                nextObjBuilder.addTreatment(tBuilder.build());
+            });
+
+            NextObjective nextObj = nextObjBuilder.add();
+            flowObjectiveService.next(deviceId, nextObj);
+            log.debug("createGroupsForXConnect: Submited next objective {} in device {}",
+                    nextId, deviceId);
+            xConnectNextObjStore.put(key, nextId);
+        });
+    }
+
 
     /**
      * Create simple next objective for a single port. The treatments can include
@@ -730,11 +788,6 @@
         portNextObjStore.put(key, nextId);
     }
 
-
-    public GroupKey getGroupKey(Object obj) {
-        return new DefaultGroupKey(kryo.build().serialize(obj));
-    }
-
     /**
      * Removes groups for the next objective ID given.
      *
@@ -766,6 +819,9 @@
         return false;
     }
 
+    /**
+     * Removes all groups from all next objective stores.
+     */
     public void removeAllGroups() {
         for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry:
                 nsNextObjStore.entrySet()) {
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
index 5bc7ede..9a93409 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
@@ -26,7 +26,6 @@
 import org.onosproject.segmentrouting.SegmentRoutingManager;
 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
 import org.onosproject.segmentrouting.config.DeviceProperties;
-import org.onosproject.store.service.EventuallyConsistentMap;
 
 /**
  * Default ECMP group handler creation module for a transit device.
@@ -41,22 +40,13 @@
  * 2) all ports to D3 + with no label push,
  */
 public class DefaultTransitGroupHandler extends DefaultGroupHandler {
-    // TODO Access stores through srManager
     protected DefaultTransitGroupHandler(DeviceId deviceId,
                                   ApplicationId appId,
                                   DeviceProperties config,
                                   LinkService linkService,
                                   FlowObjectiveService flowObjService,
-                                  EventuallyConsistentMap<
-                                        NeighborSetNextObjectiveStoreKey,
-                                        Integer> nsNextObjStore,
-                                  EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
-                                        Integer> subnetNextObjStore,
-                                  EventuallyConsistentMap<PortNextObjectiveStoreKey,
-                                  Integer> portNextObjStore,
                                   SegmentRoutingManager srManager) {
-        super(deviceId, appId, config, linkService, flowObjService,
-              nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager);
+        super(deviceId, appId, config, linkService, flowObjService, srManager);
     }
 
     @Override
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSetNextObjectiveStoreKey.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSetNextObjectiveStoreKey.java
index 9ace531..ac33036 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSetNextObjectiveStoreKey.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSetNextObjectiveStoreKey.java
@@ -21,22 +21,38 @@
 import org.onosproject.net.DeviceId;
 
 /**
- * Class definition of Key for Neighborset to NextObjective store.
+ * Key of Neighborset next objective store.
  */
 public class NeighborSetNextObjectiveStoreKey {
     private final DeviceId deviceId;
     private final NeighborSet ns;
 
+    /**
+     * Constructs the key of neighbor set next objective store.
+     *
+     * @param deviceId device ID
+     * @param ns neighbor set
+     */
     public NeighborSetNextObjectiveStoreKey(DeviceId deviceId,
                                             NeighborSet ns) {
         this.deviceId = deviceId;
         this.ns = ns;
     }
 
+    /**
+     * Returns the device ID in the key.
+     *
+     * @return device ID
+     */
     public DeviceId deviceId() {
         return this.deviceId;
     }
 
+    /**
+     * Returns the neighbor set in the key.
+     *
+     * @return neighbor set
+     */
     public NeighborSet neighborSet() {
         return this.ns;
     }
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java
index 4b0d518..4f32226 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java
@@ -31,7 +31,6 @@
 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
 import org.onosproject.segmentrouting.config.DeviceProperties;
 import org.onosproject.segmentrouting.grouphandler.GroupBucketIdentifier.BucketOutputType;
-import org.onosproject.store.service.EventuallyConsistentMap;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -51,33 +50,31 @@
     private HashMap<PolicyGroupIdentifier, PolicyGroupIdentifier> dependentGroups = new HashMap<>();
 
     /**
-     * Policy group handler constructor.
+     * Constructs policy group handler.
      *
      * @param deviceId device identifier
      * @param appId application identifier
      * @param config interface to retrieve the device properties
      * @param linkService link service object
      * @param flowObjService flow objective service object
-     * @param nsNextObjStore NeighborSet next objective store map
-     * @param subnetNextObjStore subnet next objective store map
+     * @param srManager segment routing manager
      */
-    // TODO Access stores through srManager
     public PolicyGroupHandler(DeviceId deviceId,
                               ApplicationId appId,
                               DeviceProperties config,
                               LinkService linkService,
                               FlowObjectiveService flowObjService,
-                              EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
-                                      Integer> nsNextObjStore,
-                              EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
-                                      Integer> subnetNextObjStore,
-                              EventuallyConsistentMap<PortNextObjectiveStoreKey,
-                              Integer> portNextObjStore,
                               SegmentRoutingManager srManager) {
-        super(deviceId, appId, config, linkService, flowObjService,
-              nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager);
+        super(deviceId, appId, config, linkService, flowObjService, srManager);
     }
 
+    /**
+     * Creates policy group chain.
+     *
+     * @param id unique identifier associated with the policy group
+     * @param params a list of policy group params
+     * @return policy group identifier
+     */
     public PolicyGroupIdentifier createPolicyGroupChain(String id,
                                                         List<PolicyGroupParams> params) {
         List<GroupBucketIdentifier> bucketIds = new ArrayList<>();
@@ -222,69 +219,18 @@
     }
 
     //TODO: Use nextObjective APIs to handle the group chains
-    /*@Override
-    protected void handleGroupEvent(GroupEvent event) {
-        if (event.type() == GroupEvent.Type.GROUP_ADDED) {
-            if (dependentGroups.get(event.subject().appCookie()) != null) {
-                PolicyGroupIdentifier dependentGroupKey = dependentGroups.get(event.subject().appCookie());
-                dependentGroups.remove(event.subject().appCookie());
-                boolean fullyResolved = true;
-                for (GroupBucketIdentifier bucketId:
-                            dependentGroupKey.bucketIds()) {
-                    if (bucketId.type() != BucketOutputType.GROUP) {
-                        continue;
-                    }
-                    if (dependentGroups.containsKey(bucketId.outGroup())) {
-                        fullyResolved = false;
-                        break;
-                    }
-                }
+    /*
+    @Override
+    protected void handleGroupEvent(GroupEvent event) {}
+    */
 
-                if (fullyResolved) {
-                    List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
-                    for (GroupBucketIdentifier bucketId:
-                                dependentGroupKey.bucketIds()) {
-                        TrafficTreatment.Builder tBuilder =
-                                DefaultTrafficTreatment.builder();
-                        if (bucketId.label() != NeighborSet.NO_EDGE_LABEL) {
-                            tBuilder.pushMpls()
-                                    .setMpls(MplsLabel.
-                                             mplsLabel(bucketId.label()));
-                        }
-                        //TODO: BoS
-                        if (bucketId.type() == BucketOutputType.PORT) {
-                            DeviceId neighbor = portDeviceMap.
-                                        get(bucketId.outPort());
-                            tBuilder.setOutput(bucketId.outPort())
-                                    .setEthDst(deviceConfig.
-                                               getDeviceMac(neighbor))
-                                     .setEthSrc(nodeMacAddr);
-                        } else {
-                            if (groupService.
-                                    getGroup(deviceId,
-                                             getGroupKey(bucketId.
-                                                       outGroup())) == null) {
-                                throw new IllegalStateException();
-                            }
-                            GroupId indirectGroupId = groupService.
-                                    getGroup(deviceId,
-                                             getGroupKey(bucketId.
-                                                         outGroup())).id();
-                            tBuilder.group(indirectGroupId);
-                        }
-                        outBuckets.add(DefaultGroupBucket.
-                                       createSelectGroupBucket(tBuilder.build()));
-                    }
-                    GroupDescription desc = new
-                            DefaultGroupDescription(deviceId,
-                                                    GroupDescription.Type.SELECT,
-                                                    new GroupBuckets(outBuckets));
-                    groupService.addGroup(desc);
-                }
-            }
-        }
-    }*/
-
+    /**
+     * Generates policy group key.
+     *
+     * @param id unique identifier associated with the policy group
+     * @param params a list of policy group params
+     * @return policy group identifier
+     */
     public PolicyGroupIdentifier generatePolicyGroupKey(String id,
                                    List<PolicyGroupParams> params) {
         List<GroupBucketIdentifier> bucketIds = new ArrayList<>();
@@ -354,6 +300,11 @@
         return innermostGroupkey;
     }
 
+    /**
+     * Removes policy group chain.
+     *
+     * @param key policy group identifier
+     */
     public void removeGroupChain(PolicyGroupIdentifier key) {
         checkArgument(key != null);
         List<PolicyGroupIdentifier> groupsToBeDeleted = new ArrayList<>();
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java
index 44a0a2c..ca283f0 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java
@@ -28,7 +28,7 @@
     private List<GroupBucketIdentifier> bucketIds;
 
     /**
-     * Constructor.
+     * Constructs policy group identifier.
      *
      * @param id unique identifier associated with the policy group
      * @param input policy group params associated with this group
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/PortNextObjectiveStoreKey.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/PortNextObjectiveStoreKey.java
index 5555565..ec25ec3 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/PortNextObjectiveStoreKey.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/PortNextObjectiveStoreKey.java
@@ -7,15 +7,23 @@
 import java.util.Objects;
 
 /**
- * Class definition of Key for Device/Port to NextObjective store. Since there
- * can be multiple next objectives to the same physical port, we differentiate
- * between them by including the treatment in the key.
+ * Key of Device/Port to NextObjective store.
+ *
+ * Since there can be multiple next objectives to the same physical port,
+ * we differentiate between them by including the treatment in the key.
  */
 public class PortNextObjectiveStoreKey {
     private final DeviceId deviceId;
     private final PortNumber portNum;
     private final TrafficTreatment treatment;
 
+    /**
+     * Constructs the key of port next objective store.
+     *
+     * @param deviceId device ID
+     * @param portNum port number
+     * @param treatment treatment that will be applied to the interface
+     */
     public PortNextObjectiveStoreKey(DeviceId deviceId, PortNumber portNum,
                                      TrafficTreatment treatment) {
         this.deviceId = deviceId;
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/SubnetNextObjectiveStoreKey.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/SubnetNextObjectiveStoreKey.java
index d6b16c7..8e5c3ce 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/SubnetNextObjectiveStoreKey.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/SubnetNextObjectiveStoreKey.java
@@ -22,12 +22,18 @@
 import java.util.Objects;
 
 /**
- * Class definition of Key for Subnet to NextObjective store.
+ * Key of Subnet to NextObjective store.
  */
 public class SubnetNextObjectiveStoreKey {
     private final DeviceId deviceId;
     private final IpPrefix prefix;
 
+    /**
+     * Constructs the key of subnet next objective store.
+     *
+     * @param deviceId device ID
+     * @param prefix subnet information
+     */
     public SubnetNextObjectiveStoreKey(DeviceId deviceId,
                                        IpPrefix prefix) {
         this.deviceId = deviceId;
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/XConnectNextObjectiveStoreKey.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/XConnectNextObjectiveStoreKey.java
new file mode 100644
index 0000000..f3da880
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/XConnectNextObjectiveStoreKey.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.segmentrouting.grouphandler;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.net.DeviceId;
+
+import java.util.Objects;
+
+/**
+ * Key of VLAN cross-connect next objective store.
+ */
+public class XConnectNextObjectiveStoreKey {
+    private final DeviceId deviceId;
+    private final VlanId vlanId;
+
+    /**
+     * Constructs the key of cross-connect next objective store.
+     *
+     * @param deviceId device ID of the VLAN cross-connection
+     * @param vlanId VLAN ID of the VLAN cross-connection
+     */
+    public XConnectNextObjectiveStoreKey(DeviceId deviceId, VlanId vlanId) {
+        this.deviceId = deviceId;
+        this.vlanId = vlanId;
+    }
+
+    /**
+     * Returns the device ID of this key.
+     *
+     * @return device ID
+     */
+    public DeviceId deviceId() {
+        return this.deviceId;
+    }
+
+    /**
+     * Returns the VLAN ID of this key.
+     *
+     * @return VLAN ID
+     */
+    public VlanId vlanId() {
+        return this.vlanId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof XConnectNextObjectiveStoreKey)) {
+            return false;
+        }
+        XConnectNextObjectiveStoreKey that =
+                (XConnectNextObjectiveStoreKey) o;
+        return (Objects.equals(this.deviceId, that.deviceId) &&
+                Objects.equals(this.vlanId, that.vlanId));
+    }
+
+    // The list of neighbor ids and label are used for comparison.
+    @Override
+    public int hashCode() {
+         return Objects.hash(deviceId, vlanId);
+    }
+
+    @Override
+    public String toString() {
+        return "Device: " + deviceId + " VlanId: " + vlanId;
+    }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java b/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java
index 8e50887..17fd6e6 100644
--- a/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java
+++ b/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java
@@ -21,6 +21,9 @@
 import org.onosproject.segmentrouting.Policy;
 import org.onosproject.segmentrouting.TunnelPolicy;
 
+/**
+ * Codec of Policy class.
+ */
 public final class PolicyCodec extends JsonCodec<Policy> {
 
     // JSON field names
diff --git a/src/main/java/org/onosproject/segmentrouting/web/TunnelCodec.java b/src/main/java/org/onosproject/segmentrouting/web/TunnelCodec.java
index 2f85afd..c9e4a32 100644
--- a/src/main/java/org/onosproject/segmentrouting/web/TunnelCodec.java
+++ b/src/main/java/org/onosproject/segmentrouting/web/TunnelCodec.java
@@ -26,6 +26,9 @@
 import java.util.ArrayList;
 import java.util.List;
 
+/**
+ * Codec of Tunnel class.
+ */
 public final class TunnelCodec extends JsonCodec<Tunnel> {
 
     // JSON field names