[CORD-2903] Improve SR/Multicast APIs

Change-Id: Id44af87569e0a83129c96504b21c69e1d455f785
(cherry picked from commit a2858a34a26e02e9d2dd66dc562d0f6fb2c4bdd1)
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index eff78e5..c200d74 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -675,6 +675,12 @@
     }
 
     @Override
+    public Multimap<ConnectPoint, List<ConnectPoint>> getMcastTrees(IpAddress mcastIp,
+                                                                    ConnectPoint sourcecp) {
+        return mcastHandler.getMcastTrees(mcastIp, sourcecp);
+    }
+
+    @Override
     public Map<IpAddress, NodeId> getMcastLeaders(IpAddress mcastIp) {
         return mcastHandler.getMcastLeaders(mcastIp);
     }
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
index aabed47..87ba7f3 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.segmentrouting;
 
+import com.google.common.collect.Multimap;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onosproject.cluster.NodeId;
@@ -237,7 +238,10 @@
      *
      * @param mcastIp the group ip
      * @return the mapping mcastIp-device to mcast role
+     *
+     * @deprecated in 1.12 ("Magpie") release.
      */
+    @Deprecated
     Map<McastStoreKey, McastRole> getMcastRoles(IpAddress mcastIp);
 
     /**
@@ -245,9 +249,23 @@
      *
      * @param mcastIp the group ip
      * @return the mapping egress point to mcast path
+     *
+     * @deprecated in 1.12 ("Magpie") release.
      */
+    @Deprecated
     Map<ConnectPoint, List<ConnectPoint>> getMcastPaths(IpAddress mcastIp);
 
+
+    /**
+     * Returns the associated trees to the mcast group.
+     *
+     * @param mcastIp the group ip
+     * @param sourcecp the source connect point
+     * @return the mapping egress point to mcast path
+     */
+    Multimap<ConnectPoint, List<ConnectPoint>> getMcastTrees(IpAddress mcastIp,
+                                                             ConnectPoint sourcecp);
+
     /**
      * Return the leaders of the mcast groups.
      *
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/McastNextListCommand.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/McastNextListCommand.java
index a713130..e5d8b12 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/McastNextListCommand.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/McastNextListCommand.java
@@ -57,7 +57,6 @@
         IpAddress mcastGroup = null;
         if (!isNullOrEmpty(gAddr)) {
             mcastGroup = IpAddress.valueOf(gAddr);
-
         }
         // Get SR service
         SegmentRoutingService srService = get(SegmentRoutingService.class);
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/McastTreeListCommand.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/McastTreeListCommand.java
index d1de73c..a1ee1f2 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/McastTreeListCommand.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/McastTreeListCommand.java
@@ -16,7 +16,10 @@
 
 package org.onosproject.segmentrouting.cli;
 
-import com.google.common.collect.ArrayListMultimap;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Multimap;
 import org.apache.karaf.shell.commands.Command;
 import org.apache.karaf.shell.commands.Option;
@@ -24,21 +27,13 @@
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.mcast.cli.McastGroupCompleter;
 import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
 import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.mcast.McastRole;
-import org.onosproject.segmentrouting.storekey.McastStoreKey;
 
-import java.util.Collection;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Strings.isNullOrEmpty;
-import static org.onosproject.segmentrouting.mcast.McastRole.EGRESS;
-import static org.onosproject.segmentrouting.mcast.McastRole.INGRESS;
-import static org.onosproject.segmentrouting.mcast.McastRole.TRANSIT;
 
 /**
  * Command to show the list of mcast trees.
@@ -51,9 +46,9 @@
     McastGroupCompleter completer;
 
     // Format for group line
-    private static final String G_FORMAT_MAPPING = "group=%s, ingress=%s, transit=%s, egress=%s";
+    private static final String G_FORMAT_MAPPING = "group=%s";
     // Format for sink line
-    private static final String S_FORMAT_MAPPING = "\tsink=%s\tpath=%s";
+    private static final String S_FORMAT_MAPPING = "  sink=%s\tpath=%s";
 
     @Option(name = "-gAddr", aliases = "--groupAddress",
             description = "IP Address of the multicast group",
@@ -61,53 +56,76 @@
             required = false, multiValued = false)
     String gAddr = null;
 
+    @Option(name = "-src", aliases = "--connectPoint",
+            description = "Source port of:XXXXXXXXXX/XX",
+            valueToShowInHelp = "of:0000000000000001/1",
+            required = false, multiValued = false)
+    String source = null;
+
     @Override
     protected void execute() {
-        // Verify mcast group
-        IpAddress mcastGroup = null;
-        if (!isNullOrEmpty(gAddr)) {
-            mcastGroup = IpAddress.valueOf(gAddr);
-
-        }
-        // Get SR service
+        // Get SR service and the handled mcast groups
         SegmentRoutingService srService = get(SegmentRoutingService.class);
-        // Get the mapping
-        Map<McastStoreKey, McastRole> keyToRole = srService.getMcastRoles(mcastGroup);
-        // Reduce to the set of mcast groups
-        Set<IpAddress> mcastGroups = keyToRole.keySet().stream()
-                .map(McastStoreKey::mcastIp)
-                .collect(Collectors.toSet());
-        // Print the trees for each group
+        Set<IpAddress> mcastGroups = ImmutableSet.copyOf(srService.getMcastLeaders(null)
+                                                                         .keySet());
+
+        if (!isNullOrEmpty(gAddr)) {
+            mcastGroups = mcastGroups.stream()
+                    .filter(mcastIp -> mcastIp.equals(IpAddress.valueOf(gAddr)))
+                    .collect(Collectors.toSet());
+        }
+
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode root = mapper.createObjectNode();
+
+        // Print the trees for each group or build json objects
         mcastGroups.forEach(group -> {
-            // Create a new map for the group
-            Multimap<McastRole, DeviceId> roleDeviceIdMap = ArrayListMultimap.create();
-            keyToRole.entrySet()
-                    .stream()
-                    // Filter only the elements related to this group
-                    .filter(entry -> entry.getKey().mcastIp().equals(group))
-                    // For each create a new entry in the group related map
-                    .forEach(entry -> roleDeviceIdMap.put(entry.getValue(), entry.getKey().deviceId()));
-            // Print the map
-            printMcastRole(group,
-                           roleDeviceIdMap.get(INGRESS),
-                           roleDeviceIdMap.get(TRANSIT),
-                           roleDeviceIdMap.get(EGRESS)
-            );
-            // Get sinks paths
-            Map<ConnectPoint, List<ConnectPoint>> mcastPaths = srService.getMcastPaths(group);
-            // Print the paths
-            mcastPaths.forEach(this::printMcastSink);
+            // We want to use source cp only for a specific group
+            ConnectPoint sourcecp = null;
+            if (!isNullOrEmpty(source) &&
+                    !isNullOrEmpty(gAddr)) {
+                sourcecp = ConnectPoint.deviceConnectPoint(source);
+            }
+            Multimap<ConnectPoint, List<ConnectPoint>> mcastTree = srService.getMcastTrees(group,
+                                                                                           sourcecp);
+            // Build a json object for each group
+            if (outputJson()) {
+                root.putPOJO(group.toString(), json(mcastTree));
+            } else {
+                // Banner and then the trees
+                printMcastGroup(group);
+                mcastTree.forEach(this::printMcastSink);
+            }
         });
+
+        // Print the json object at the end
+        if (outputJson()) {
+            print("%s", root);
+        }
+
     }
 
-    private void printMcastRole(IpAddress mcastGroup,
-                                Collection<DeviceId> ingress,
-                                Collection<DeviceId> transit,
-                                Collection<DeviceId> egress) {
-        print(G_FORMAT_MAPPING, mcastGroup, ingress, transit, egress);
+    private void printMcastGroup(IpAddress mcastGroup) {
+        print(G_FORMAT_MAPPING, mcastGroup);
     }
 
     private void printMcastSink(ConnectPoint sink, List<ConnectPoint> path) {
         print(S_FORMAT_MAPPING, sink, path);
     }
-}
+
+    private ObjectNode json(Multimap<ConnectPoint, List<ConnectPoint>> mcastTree) {
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode jsonSinks = mapper.createObjectNode();
+        mcastTree.asMap().forEach((sink, paths) -> {
+            ArrayNode jsonPaths = mapper.createArrayNode();
+            paths.forEach(path -> {
+                ArrayNode jsonPath = mapper.createArrayNode();
+                path.forEach(connectPoint -> jsonPath.add(connectPoint.toString()));
+                jsonPaths.addPOJO(jsonPath);
+            });
+            jsonSinks.putPOJO(sink.toString(), jsonPaths);
+        });
+        return jsonSinks;
+    }
+
+}
\ No newline at end of file
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastCacheKey.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastCacheKey.java
index 930432d..55d2131 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastCacheKey.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastCacheKey.java
@@ -45,6 +45,7 @@
      *
      * @deprecated in 1.12 ("Magpie") release.
      */
+    @Deprecated
     public McastCacheKey(IpAddress mcastIp, ConnectPoint sink) {
         checkNotNull(mcastIp, "mcastIp cannot be null");
         checkNotNull(sink, "sink cannot be null");
@@ -85,6 +86,7 @@
      *
      * @deprecated in 1.12 ("Magpie") release.
      */
+    @Deprecated
     public ConnectPoint sink() {
         return sink;
     }
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastHandler.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastHandler.java
index 9a53e13..6d8495d 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastHandler.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastHandler.java
@@ -20,10 +20,12 @@
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.RemovalCause;
 import com.google.common.cache.RemovalNotification;
+import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.VlanId;
@@ -1767,6 +1769,16 @@
                                           entry -> entry.getValue().value().id()));
     }
 
+    /**
+     * Returns the associated roles to the mcast groups or to the single
+     * group if mcastIp is present.
+     *
+     * @param mcastIp the group ip
+     * @return the mapping mcastIp-device to mcast role
+     *
+     * @deprecated in 1.12 ("Magpie") release.
+     */
+    @Deprecated
     public Map<McastStoreKey, McastRole> getMcastRoles(IpAddress mcastIp) {
         // If mcast ip is present
         if (mcastIp != null) {
@@ -1781,6 +1793,15 @@
                                           entry -> entry.getValue().value()));
     }
 
+    /**
+     * Returns the associated paths to the mcast group.
+     *
+     * @param mcastIp the group ip
+     * @return the mapping egress point to mcast path
+     *
+     * @deprecated in 1.12 ("Magpie") release.
+     */
+    @Deprecated
     public Map<ConnectPoint, List<ConnectPoint>> getMcastPaths(IpAddress mcastIp) {
         Map<ConnectPoint, List<ConnectPoint>> mcastPaths = Maps.newHashMap();
         // Get the source
@@ -1798,6 +1819,50 @@
         return mcastPaths;
     }
 
+    /**
+     * Returns the associated trees to the mcast group.
+     *
+     * @param mcastIp the group ip
+     * @param sourcecp the source connect point
+     * @return the mapping egress point to mcast path
+     */
+    public Multimap<ConnectPoint, List<ConnectPoint>> getMcastTrees(IpAddress mcastIp,
+                                                                    ConnectPoint sourcecp) {
+        Multimap<ConnectPoint, List<ConnectPoint>> mcastTrees = HashMultimap.create();
+        // Get the sources
+        Set<ConnectPoint> sources = mcastUtils.getSources(mcastIp);
+
+        // If we are providing the source, let's filter out
+        if (sourcecp != null) {
+            sources = sources.stream()
+                    .filter(source -> source.equals(sourcecp))
+                    .collect(Collectors.toSet());
+        }
+
+        // Source cannot be null, we don't know the starting point
+        if (!sources.isEmpty()) {
+            sources.forEach(source -> {
+                // Init steps
+                Map<ConnectPoint, List<ConnectPoint>> mcastPaths = Maps.newHashMap();
+                Set<DeviceId> visited = Sets.newHashSet();
+                List<ConnectPoint> currentPath = Lists.newArrayList(source);
+                // Build recursively the mcast paths
+                buildMcastPaths(source.deviceId(), visited, mcastPaths, currentPath, mcastIp);
+                mcastPaths.forEach(mcastTrees::put);
+            });
+        }
+        return mcastTrees;
+    }
+
+    /**
+     * Build recursively the mcast paths.
+     *
+     * @param toVisit the node to visit
+     * @param visited the visited nodes
+     * @param mcastPaths the current mcast paths
+     * @param currentPath the current path
+     * @param mcastIp the group ip
+     */
     private void buildMcastPaths(DeviceId toVisit, Set<DeviceId> visited,
                                  Map<ConnectPoint, List<ConnectPoint>> mcastPaths,
                                  List<ConnectPoint> currentPath, IpAddress mcastIp) {
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java
index 924794b..fc89fde 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java
@@ -55,7 +55,6 @@
 import org.slf4j.Logger;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -234,10 +233,11 @@
      *
      * @param mcastIp multicast IP
      * @return source connect point or null if not found
+     *
+     * @deprecated in 1.12 ("Magpie") release.
      */
-    // FIXME To be addressed with multiple sources support
+    @Deprecated
     ConnectPoint getSource(IpAddress mcastIp) {
-        // FIXME we should support different types of routes
         McastRoute mcastRoute = srManager.multicastRouteService.getRoutes().stream()
                 .filter(mcastRouteInternal -> mcastRouteInternal.group().equals(mcastIp))
                 .findFirst().orElse(null);
@@ -247,6 +247,21 @@
     }
 
     /**
+     * Gets sources connect points of given multicast group.
+     *
+     * @param mcastIp multicast IP
+     * @return sources connect points or empty set if not found
+     */
+    Set<ConnectPoint> getSources(IpAddress mcastIp) {
+        // FIXME we should support different types of routes
+        McastRoute mcastRoute = srManager.multicastRouteService.getRoutes().stream()
+                .filter(mcastRouteInternal -> mcastRouteInternal.group().equals(mcastIp))
+                .findFirst().orElse(null);
+        return mcastRoute == null ? ImmutableSet.of() :
+                srManager.multicastRouteService.sources(mcastRoute);
+    }
+
+    /**
      * Gets sinks of given multicast group.
      *
      * @param mcastIp multicast IP
@@ -258,7 +273,7 @@
                 .filter(mcastRouteInternal -> mcastRouteInternal.group().equals(mcastIp))
                 .findFirst().orElse(null);
         return mcastRoute == null ?
-                Collections.emptyMap() :
+                ImmutableMap.of() :
                 srManager.multicastRouteService.routeData(mcastRoute).sinks();
     }
 
diff --git a/apps/segmentrouting/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/segmentrouting/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 4a3329a..71e80ef 100644
--- a/apps/segmentrouting/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/segmentrouting/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -79,6 +79,7 @@
             <action class="org.onosproject.segmentrouting.cli.McastTreeListCommand"/>
             <optional-completers>
                 <entry key="-gAddr" value-ref="mcastGroupCompleter"/>
+                <entry key="-src" value-ref="connectpointCompleter"/>
             </optional-completers>
         </command>
         <command>
@@ -93,6 +94,7 @@
     <bean id="deviceIdCompleter" class="org.onosproject.cli.net.DeviceIdCompleter"/>
     <bean id="pseudowireIdCompleter" class="org.onosproject.segmentrouting.cli.PseudowireIdCompleter"/>
     <bean id="mcastGroupCompleter" class="org.onosproject.mcast.cli.McastGroupCompleter"/>
+    <bean id="connectpointCompleter" class="org.onosproject.cli.net.ConnectPointCompleter"/>
 
 </blueprint>
 
diff --git a/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/McastWebResource.java b/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/McastWebResource.java
new file mode 100644
index 0000000..8202eed
--- /dev/null
+++ b/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/McastWebResource.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.web;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.rest.AbstractWebResource;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+
+/**
+ * Query multicast trees.
+ */
+@Path("mcast")
+public class McastWebResource extends AbstractWebResource {
+
+    private ObjectNode encodeMcastTrees(String gAddr, String source) {
+        SegmentRoutingService srService = get(SegmentRoutingService.class);
+        Set<IpAddress> mcastGroups = ImmutableSet.copyOf(srService.getMcastLeaders(null)
+                                                                 .keySet());
+
+        if (!isNullOrEmpty(gAddr)) {
+            mcastGroups = mcastGroups.stream()
+                    .filter(mcastIp -> mcastIp.equals(IpAddress.valueOf(gAddr)))
+                    .collect(Collectors.toSet());
+        }
+
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode root = mapper.createObjectNode();
+
+        // Print the trees for each group or build json objects
+        mcastGroups.forEach(group -> {
+            // We want to use source cp only for a specific group
+            ConnectPoint sourcecp = null;
+            if (!isNullOrEmpty(source) &&
+                    !isNullOrEmpty(gAddr)) {
+                sourcecp = ConnectPoint.deviceConnectPoint(source);
+            }
+            Multimap<ConnectPoint, List<ConnectPoint>> mcastTree = srService.getMcastTrees(group, sourcecp);
+            // Build a json object for each group
+            root.putPOJO(group.toString(), json(mcastTree));
+
+        });
+        return root;
+    }
+
+    private ObjectNode json(Multimap<ConnectPoint, List<ConnectPoint>> mcastTree) {
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode jsonSinks = mapper.createObjectNode();
+        mcastTree.asMap().forEach((sink, paths) -> {
+            ArrayNode jsonPaths = mapper.createArrayNode();
+            paths.forEach(path -> {
+                ArrayNode jsonPath = mapper.createArrayNode();
+                path.forEach(connectPoint -> jsonPath.add(connectPoint.toString()));
+                jsonPaths.addPOJO(jsonPath);
+            });
+            jsonSinks.putPOJO(sink.toString(), jsonPaths);
+        });
+        return jsonSinks;
+    }
+
+    /**
+     * Get all multicast trees.
+     * Returns an object of the multicast trees.
+     *
+     * @return status of OK
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getMcastTrees() {
+        ObjectNode root = encodeMcastTrees(null, null);
+        return ok(root).build();
+    }
+
+    /**
+     * Get the multicast trees of a group.
+     *
+     * @param group group IP address
+     * @return 200 OK with a multicast routes
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("{group}")
+    public Response getRoute(@PathParam("group") String group) {
+        ObjectNode root = encodeMcastTrees(group, null);
+        return ok(root).build();
+    }
+
+    /**
+     * Get the multicast tree of a group.
+     *
+     * @param group group IP address
+     * @param sourcecp source connect point
+     * @return 200 OK with a multicast routes
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("{group}/{sourcecp}")
+    public Response getRoute(@PathParam("group") String group,
+                             @PathParam("sourcecp") String sourcecp) {
+        ObjectNode root = encodeMcastTrees(group, sourcecp);
+        return ok(root).build();
+    }
+
+}
diff --git a/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/SegmentRoutingWebApplication.java b/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/SegmentRoutingWebApplication.java
index 4aa1a71..75646d1 100644
--- a/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/SegmentRoutingWebApplication.java
+++ b/apps/segmentrouting/web/src/main/java/org/onosproject/segmentrouting/web/SegmentRoutingWebApplication.java
@@ -26,6 +26,6 @@
 public class SegmentRoutingWebApplication extends AbstractWebApplication {
     @Override
     public Set<Class<?>> getClasses() {
-        return getClasses(PseudowireWebResource.class);
+        return getClasses(PseudowireWebResource.class, McastWebResource.class);
     }
 }