ONOS-2486 Adding swagger-based REST API documentation.

Change-Id: I237d973d73549ad30ddc638c1c201f024d344c70
diff --git a/web/api/src/main/java/org/onosproject/rest/impl/ApiDocManager.java b/web/api/src/main/java/org/onosproject/rest/impl/ApiDocManager.java
new file mode 100644
index 0000000..8a31b02
--- /dev/null
+++ b/web/api/src/main/java/org/onosproject/rest/impl/ApiDocManager.java
@@ -0,0 +1,73 @@
+/*
+ * 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.rest.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.rest.ApiDocProvider;
+import org.onosproject.rest.ApiDocService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Implementation of the REST API documentation tracker.
+ */
+@Component(immediate = true)
+@Service
+public class ApiDocManager implements ApiDocService {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    // Set of doc providers
+    private final Map<String, ApiDocProvider> providers = Maps.newConcurrentMap();
+
+    @Activate
+    public void activate() {
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public void register(ApiDocProvider provider) {
+        providers.put(provider.key(), provider);
+    }
+
+    @Override
+    public void unregister(ApiDocProvider provider) {
+        providers.remove(provider.name());
+    }
+
+    @Override
+    public Set<ApiDocProvider> getDocProviders() {
+        return ImmutableSet.copyOf(providers.values());
+    }
+
+    @Override
+    public ApiDocProvider getDocProvider(String key) {
+        return providers.get(key);
+    }
+}
diff --git a/web/api/src/main/java/org/onosproject/rest/impl/package-info.java b/web/api/src/main/java/org/onosproject/rest/impl/package-info.java
new file mode 100644
index 0000000..8fa8468
--- /dev/null
+++ b/web/api/src/main/java/org/onosproject/rest/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * REST API related service.
+ */
+package org.onosproject.rest.impl;
\ No newline at end of file
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/ApiDocResource.java b/web/api/src/main/java/org/onosproject/rest/resources/ApiDocResource.java
new file mode 100644
index 0000000..fa124f5
--- /dev/null
+++ b/web/api/src/main/java/org/onosproject/rest/resources/ApiDocResource.java
@@ -0,0 +1,165 @@
+/*
+ * 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.rest.resources;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.rest.AbstractInjectionResource;
+import org.onosproject.rest.ApiDocService;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Response;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.SequenceInputStream;
+
+import static com.google.common.collect.ImmutableList.of;
+import static com.google.common.io.ByteStreams.toByteArray;
+import static javax.ws.rs.core.MediaType.*;
+import static org.onlab.util.Tools.nullIsNotFound;
+
+/**
+ * REST API documentation.
+ */
+@Path("docs")
+public class ApiDocResource extends AbstractInjectionResource {
+
+    private static final String CONTENT_TYPE = "Content-Type";
+    private static final String STYLESHEET = "text/css";
+    private static final String SCRIPT = "text/javascript";
+    private static final String DOCS = "/docs/";
+
+    private static final String INJECT_START = "<!-- {API-START} -->";
+    private static final String INJECT_END = "<!-- {API-END} -->";
+
+    /**
+     * Get all registered REST API docs.
+     * Returns array of all registered API docs.
+     *
+     * @return 200 OK
+     */
+    @GET
+    @Path("apis")
+    public Response getApiList() {
+        ObjectNode root = mapper().createObjectNode();
+        ArrayNode apis = newArray(root, "apis");
+        get(ApiDocService.class).getDocProviders().forEach(p -> apis.add(p.name()));
+        return ok(root.toString()).build();
+    }
+
+    /**
+     * Get Swagger UI JSON.
+     *
+     * @param key REST API web context
+     * @return 200 OK
+     */
+    @GET
+    @Path("apis/{key: .*?}/swagger.json")
+    public Response getApi(@PathParam("key") String key) {
+        String k = key.startsWith("/") ? key : "/" + key;
+        InputStream stream = nullIsNotFound(get(ApiDocService.class).getDocProvider(k),
+                                            "REST API not found for " + k).docs();
+        return ok(nullIsNotFound(stream, "REST API docs not found for " + k))
+                .header(CONTENT_TYPE, APPLICATION_JSON).build();
+    }
+
+    /**
+     * Get REST API model schema.
+     *
+     * @param key REST API web context
+     * @return 200 OK
+     */
+    @GET
+    @Path("apis/{key: .*?}/model.json")
+    public Response getApiModel(@PathParam("name") String key) {
+        String k = key.startsWith("/") ? key : "/" + key;
+        InputStream stream = nullIsNotFound(get(ApiDocService.class).getDocProvider(k),
+                                            "REST API not found for " + k).model();
+        return ok(nullIsNotFound(stream, "REST API model not found for " + k))
+                .header(CONTENT_TYPE, APPLICATION_JSON).build();
+    }
+
+    /**
+     * Get Swagger UI main index page.
+     *
+     * @return 200 OK
+     */
+    @GET
+    @Path("/")
+    public Response getDefault() throws IOException {
+        return getIndex();
+    }
+
+    /**
+     * Get Swagger UI main index page.
+     *
+     * @return 200 OK
+     */
+    @GET
+    @Path("index.html")
+    public Response getIndex() throws IOException {
+        InputStream stream = getClass().getClassLoader().getResourceAsStream(DOCS + "index.html");
+        nullIsNotFound(stream, "index.html not found");
+
+        String index = new String(toByteArray(stream));
+
+        int p1s = split(index, 0, INJECT_START);
+        int p1e = split(index, p1s, INJECT_END);
+        int p2s = split(index, p1e, null);
+
+        StreamEnumeration streams =
+                new StreamEnumeration(of(stream(index, 0, p1s),
+                                         includeOptions(get(ApiDocService.class)),
+                                         stream(index, p1e, p2s)));
+
+        return ok(new SequenceInputStream(streams))
+                .header(CONTENT_TYPE, TEXT_HTML).build();
+    }
+
+    private InputStream includeOptions(ApiDocService service) {
+        StringBuilder sb = new StringBuilder();
+        service.getDocProviders().forEach(p -> {
+            sb.append("<option value=\"").append(p.key()).append("\"")
+                    .append(p.key().equals("/onos/v1") ? " selected>" : ">")
+                    .append(p.name())
+                    .append("</option>");
+        });
+        return new ByteArrayInputStream(sb.toString().getBytes());
+    }
+
+    /**
+     * Get Swagger UI resource.
+     *
+     * @return 200 OK
+     */
+    @GET
+    @Path("{resource: .*}")
+    public Response getResource(@PathParam("resource") String resource) throws IOException {
+        InputStream stream = getClass().getClassLoader().getResourceAsStream(DOCS + resource);
+        return ok(nullIsNotFound(stream, resource + " not found"))
+                .header(CONTENT_TYPE, contentType(resource)).build();
+    }
+
+    static String contentType(String resource) {
+        return resource.endsWith(".html") ? TEXT_HTML :
+                resource.endsWith(".css") ? STYLESHEET :
+                        resource.endsWith(".js") ? SCRIPT :
+                                APPLICATION_OCTET_STREAM;
+    }
+}
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/ApplicationsWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/ApplicationsWebResource.java
index 91148db..636fc33 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/ApplicationsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/ApplicationsWebResource.java
@@ -35,32 +35,54 @@
 import java.util.Set;
 
 /**
- * REST resource for interacting with the inventory of applications.
+ * Manage inventory of applications.
  */
 @Path("applications")
 public class ApplicationsWebResource extends AbstractWebResource {
 
+    /**
+     * Get all installed applications.
+     * Returns array of all installed applications.
+     *
+     * @return 200 OK
+     */
     @GET
-    public Response getApplications() {
+    public Response getApps() {
         ApplicationAdminService service = get(ApplicationAdminService.class);
         Set<Application> apps = service.getApplications();
         return ok(encodeArray(Application.class, "applications", apps)).build();
     }
 
+    /**
+     * Get application details.
+     * Returns details of the specified application.
+     *
+     * @param name application name
+     * @return 200 OK; 404; 401
+     */
     @GET
     @Path("{name}")
-    public Response getApplication(@PathParam("name") String name) {
+    public Response getApp(@PathParam("name") String name) {
         ApplicationAdminService service = get(ApplicationAdminService.class);
         ApplicationId appId = service.getId(name);
         return response(service, appId);
     }
 
+    /**
+     * Install a new application.
+     * Uploads application archive stream and optionally activates the
+     * application.
+     *
+     * @param activate true to activate app also
+     * @param stream   application archive stream
+     * @return 200 OK; 404; 401
+     */
     @POST
     @Consumes(MediaType.APPLICATION_OCTET_STREAM)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response installApplication(@QueryParam("activate")
-                                           @DefaultValue("false") boolean activate,
-                                       InputStream stream) {
+    public Response installApp(@QueryParam("activate")
+                               @DefaultValue("false") boolean activate,
+                               InputStream stream) {
         ApplicationAdminService service = get(ApplicationAdminService.class);
         Application app = service.install(stream);
         if (activate) {
@@ -69,30 +91,51 @@
         return ok(codec(Application.class).encode(app, this)).build();
     }
 
+    /**
+     * Uninstall application.
+     * Uninstalls the specified application deactivating it first if necessary.
+     *
+     * @param name application name
+     * @return 200 OK; 404; 401
+     */
     @DELETE
     @Produces(MediaType.APPLICATION_JSON)
     @Path("{name}")
-    public Response uninstallApplication(@PathParam("name") String name) {
+    public Response uninstallApp(@PathParam("name") String name) {
         ApplicationAdminService service = get(ApplicationAdminService.class);
         ApplicationId appId = service.getId(name);
         service.uninstall(appId);
         return Response.ok().build();
     }
 
+    /**
+     * Activate application.
+     * Activates the specified application.
+     *
+     * @param name application name
+     * @return 200 OK; 404; 401
+     */
     @POST
     @Produces(MediaType.APPLICATION_JSON)
     @Path("{name}/active")
-    public Response activateApplication(@PathParam("name") String name) {
+    public Response activateApp(@PathParam("name") String name) {
         ApplicationAdminService service = get(ApplicationAdminService.class);
         ApplicationId appId = service.getId(name);
         service.activate(appId);
         return response(service, appId);
     }
 
+    /**
+     * De-activate application.
+     * De-activates the specified application.
+     *
+     * @param name application name
+     * @return 200 OK; 404; 401
+     */
     @DELETE
     @Produces(MediaType.APPLICATION_JSON)
     @Path("{name}/active")
-    public Response deactivateApplication(@PathParam("name") String name) {
+    public Response deactivateApp(@PathParam("name") String name) {
         ApplicationAdminService service = get(ApplicationAdminService.class);
         ApplicationId appId = service.getId(name);
         service.deactivate(appId);
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/ClusterWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/ClusterWebResource.java
index 690637e..ee608b7 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/ClusterWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/ClusterWebResource.java
@@ -37,19 +37,32 @@
 import static org.onlab.util.Tools.nullIsNotFound;
 
 /**
- * REST resource for interacting with the ONOS cluster subsystem.
+ * Manage cluster of ONOS instances.
  */
 @Path("cluster")
 public class ClusterWebResource extends AbstractWebResource {
 
     public static final String NODE_NOT_FOUND = "Node is not found";
 
+    /**
+     * Get all cluster nodes.
+     * Returns array of all cluster nodes.
+     *
+     * @return 200 OK
+     */
     @GET
     public Response getClusterNodes() {
         Iterable<ControllerNode> nodes = get(ClusterService.class).getNodes();
         return ok(encodeArray(ControllerNode.class, "nodes", nodes)).build();
     }
 
+    /**
+     * Get cluster node details.
+     * Returns details of the specified cluster node.
+     *
+     * @param id cluster node identifier
+     * @return 200 OK
+     */
     @GET
     @Path("{id}")
     public Response getClusterNode(@PathParam("id") String id) {
@@ -58,6 +71,13 @@
         return ok(codec(ControllerNode.class).encode(node, this)).build();
     }
 
+    /**
+     * Forms cluster of ONOS instances.
+     * Forms ONOS cluster using the uploaded JSON definition.
+     *
+     * @param config cluster definition
+     * @return 200 OK
+     */
     @POST
     @Path("configuration")
     public Response formCluster(InputStream config) throws IOException {
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/ComponentConfigWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/ComponentConfigWebResource.java
index 1155be2..4adb6c5 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/ComponentConfigWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/ComponentConfigWebResource.java
@@ -33,11 +33,17 @@
 import static org.onlab.util.Tools.nullIsNotFound;
 
 /**
- * REST resource for cluster-wide component configuration.
+ * Manage component configurations.
  */
 @Path("configuration")
 public class ComponentConfigWebResource extends AbstractWebResource {
 
+    /**
+     * Get all component configurations.
+     * Returns collection of all registered component configurations.
+     *
+     * @return 200 OK
+     */
     @GET
     public Response getComponentConfigs() {
         ComponentConfigService service = get(ComponentConfigService.class);
@@ -47,6 +53,12 @@
         return ok(root).build();
     }
 
+    /**
+     * Get configuration of the specified component.
+     *
+     * @param component component name
+     * @return 200 OK
+     */
     @GET
     @Path("{component}")
     public Response getComponentConfigs(@PathParam("component") String component) {
@@ -65,10 +77,17 @@
         props.forEach(p -> compNode.put(p.name(), p.value()));
     }
 
+    /**
+     * Selectively set configuration properties.
+     * Sets only the properties present in the JSON request.
+     *
+     * @param component component name
+     * @return 200 OK
+     */
     @POST
     @Path("{component}")
-    public Response setComponentConfigs(@PathParam("component") String component,
-                                        InputStream request) throws IOException {
+    public Response setConfigs(@PathParam("component") String component,
+                               InputStream request) throws IOException {
         ComponentConfigService service = get(ComponentConfigService.class);
         ObjectNode props = (ObjectNode) mapper().readTree(request);
         props.fieldNames().forEachRemaining(k -> service.setProperty(component, k,
@@ -76,10 +95,17 @@
         return Response.noContent().build();
     }
 
+    /**
+     * Selectively clear configuration properties.
+     * Clears only the properties present in the JSON request.
+     *
+     * @param component component name
+     * @return 200 OK
+     */
     @DELETE
     @Path("{component}")
-    public Response unsetComponentConfigs(@PathParam("component") String component,
-                                          InputStream request) throws IOException {
+    public Response unsetConfigs(@PathParam("component") String component,
+                                 InputStream request) throws IOException {
         ComponentConfigService service = get(ComponentConfigService.class);
         ObjectNode props = (ObjectNode) mapper().readTree(request);
         props.fieldNames().forEachRemaining(k -> service.unsetProperty(component, k));
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/ConfigWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/ConfigWebResource.java
index 746b76a..b0db475 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/ConfigWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/ConfigWebResource.java
@@ -37,14 +37,20 @@
 import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
 
 /**
- * Resource that acts as an ancillary provider for uploading pre-configured
- * devices, ports and links.
+ * Inject devices, ports, links and end-station hosts.
  */
 @Path("config")
 public class ConfigWebResource extends BaseResource {
 
     private static Logger log = LoggerFactory.getLogger(ConfigWebResource.class);
 
+    /**
+     * Upload device, port, link and host data.
+     *
+     * @param input JSON blob
+     * @return 200 OK
+     * @throws IOException
+     */
     @POST
     @Path("topology")
     @Consumes(MediaType.APPLICATION_JSON)
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/DevicesWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/DevicesWebResource.java
index 1604752..05756e5 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/DevicesWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/DevicesWebResource.java
@@ -34,19 +34,32 @@
 import static org.onosproject.net.DeviceId.deviceId;
 
 /**
- * REST resource for interacting with the inventory of infrastructure devices.
+ * Manage inventory of infrastructure devices.
  */
 @Path("devices")
 public class DevicesWebResource extends AbstractWebResource {
 
     public static final String DEVICE_NOT_FOUND = "Device is not found";
 
+    /**
+     * Get all infrastructure devices.
+     * Returns array of all discovered infrastructure devices.
+     *
+     * @return 200 OK
+     */
     @GET
     public Response getDevices() {
         Iterable<Device> devices = get(DeviceService.class).getDevices();
         return ok(encodeArray(Device.class, "devices", devices)).build();
     }
 
+    /**
+     * Get details of infrastructure device.
+     * Returns details of the specified infrastructure device.
+     *
+     * @param id device identifier
+     * @return 200 OK
+     */
     @GET
     @Path("{id}")
     public Response getDevice(@PathParam("id") String id) {
@@ -55,6 +68,14 @@
         return ok(codec(Device.class).encode(device, this)).build();
     }
 
+    /**
+     * Remove infrastructure device.
+     * Administratively deletes the specified device from the inventory of
+     * known devices.
+     *
+     * @param id device identifier
+     * @return 200 OK
+     */
     @DELETE
     @Path("{id}")
     public Response removeDevice(@PathParam("id") String id) {
@@ -64,6 +85,13 @@
         return ok(codec(Device.class).encode(device, this)).build();
     }
 
+    /**
+     * Get ports of infrastructure device.
+     * Returns details of the specified infrastructure device.
+     *
+     * @param id device identifier
+     * @return 200 OK
+     */
     @GET
     @Path("{id}/ports")
     public Response getDevicePorts(@PathParam("id") String id) {
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
index 45529a4..325e191 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
@@ -45,7 +45,7 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 /**
- * REST resource for interacting with the inventory of flows.
+ * Query and program flow rules.
  */
 
 @Path("flows")
@@ -57,14 +57,13 @@
     final ArrayNode flowsNode = root.putArray("flows");
 
     /**
-     * Gets an array containing all the intents in the system.
+     * Get all flow entries. Returns array of all flow rules in the system.
      *
      * @return array of all the intents in the system
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     public Response getFlows() {
-
         final Iterable<Device> devices = get(DeviceService.class).getDevices();
         for (final Device device : devices) {
             final Iterable<FlowEntry> deviceEntries = service.getFlowEntries(device.id());
@@ -79,9 +78,10 @@
     }
 
     /**
-     * Gets the flows for a device, where the device is specified by Id.
+     * Get flow entries of a device. Returns array of all flow rules for the
+     * specified device.
      *
-     * @param deviceId Id of device to look up
+     * @param deviceId device identifier
      * @return flow data as an array
      */
     @GET
@@ -101,10 +101,11 @@
     }
 
     /**
-     * Gets the flows for a device, where the device is specified by Id.
+     * Get flow rule. Returns the flow entry specified by the device id and
+     * flow rule id.
      *
-     * @param deviceId Id of device to look up
-     * @param flowId   Id of flow to look up
+     * @param deviceId device identifier
+     * @param flowId   flow rule identifier
      * @return flow data as an array
      */
     @GET
@@ -127,10 +128,11 @@
     }
 
     /**
-     * Creates a flow rule from a POST of a JSON string and attempts to apply it.
+     * Create new flow rule. Creates and installs a new flow rule for the
+     * specified device.
      *
      * @param deviceId device identifier
-     * @param stream input JSON
+     * @param stream   flow rule JSON
      * @return status of the request - CREATED if the JSON is correct,
      * BAD_REQUEST if the JSON is invalid
      */
@@ -163,16 +165,16 @@
     }
 
     /**
-     * Removes the flows for a given device with the given flow id.
+     * Remove flow rule. Removes the specified flow rule.
      *
-     * @param deviceId Id of device to look up
-     * @param flowId   Id of flow to look up
+     * @param deviceId device identifier
+     * @param flowId   flow rule identifier
      */
     @DELETE
     @Produces(MediaType.APPLICATION_JSON)
     @Path("{deviceId}/{flowId}")
     public void deleteFlowByDeviceIdAndFlowId(@PathParam("deviceId") String deviceId,
-                                                  @PathParam("flowId") long flowId) {
+                                              @PathParam("flowId") long flowId) {
         final Iterable<FlowEntry> deviceEntries =
                 service.getFlowEntries(DeviceId.deviceId(deviceId));
 
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/HostsWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/HostsWebResource.java
index 9d3fd7f..b89f5ad 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/HostsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/HostsWebResource.java
@@ -56,7 +56,7 @@
 import static org.onosproject.net.HostId.hostId;
 
 /**
- * REST resource for interacting with the inventory of hosts.
+ * Manage inventory of end-station hosts.
  */
 @Path("hosts")
 public class HostsWebResource extends AbstractWebResource {
@@ -65,6 +65,12 @@
     UriInfo uriInfo;
     public static final String HOST_NOT_FOUND = "Host is not found";
 
+    /**
+     * Get all end-station hosts.
+     * Returns array of all known end-station hosts.
+     *
+     * @return 200 OK
+     */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     public Response getHosts() {
@@ -73,6 +79,13 @@
         return ok(root).build();
     }
 
+    /**
+     * Get details of end-station host.
+     * Returns detailed properties of the specified end-station host.
+     *
+     * @param id host identifier
+     * @return 200 OK
+     */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     @Path("{id}")
@@ -83,6 +96,14 @@
         return ok(root).build();
     }
 
+    /**
+     * Get details of end-station host with MAC/VLAN.
+     * Returns detailed properties of the specified end-station host.
+     *
+     * @param mac  host MAC address
+     * @param vlan host VLAN identifier
+     * @return 200 OK
+     */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     @Path("{mac}/{vlan}")
@@ -157,6 +178,7 @@
 
         /**
          * Creates and adds new host based on given data and returns its host ID.
+         *
          * @param node JsonNode containing host information
          * @return host ID of new host created
          */
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/IntentsWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/IntentsWebResource.java
index 09fdd2f..b29e3e8 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/IntentsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/IntentsWebResource.java
@@ -55,9 +55,8 @@
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * REST resource for interacting with the inventory of intents.
+ * Query, submit and withdraw network intents.
  */
-
 @Path("intents")
 public class IntentsWebResource extends AbstractWebResource {
     @Context
@@ -69,7 +68,8 @@
     public static final String INTENT_NOT_FOUND = "Intent is not found";
 
     /**
-     * Gets an array containing all the intents in the system.
+     * Get all intents.
+     * Returns array containing all the intents in the system.
      *
      * @return array of all the intents in the system
      */
@@ -82,10 +82,11 @@
     }
 
     /**
-     * Gets a single intent by Id.
+     * Get intent by application and key.
+     * Returns details of the specified intent.
      *
-     * @param appId the Application ID
-     * @param key the Intent key value to look up
+     * @param appId application identifier
+     * @param key   intent key
      * @return intent data
      */
     @GET
@@ -125,70 +126,16 @@
         @Override
         public void event(IntentEvent event) {
             if (Objects.equals(event.subject().key(), key) &&
-                (event.type() == IntentEvent.Type.WITHDRAWN ||
-                        event.type() == IntentEvent.Type.FAILED)) {
+                    (event.type() == IntentEvent.Type.WITHDRAWN ||
+                            event.type() == IntentEvent.Type.FAILED)) {
                 latch.countDown();
             }
         }
     }
 
     /**
-     * Uninstalls a single intent by Id.
-     *
-     * @param appId the Application ID
-     * @param keyString the Intent key value to look up
-     */
-    @DELETE
-    @Path("{appId}/{key}")
-    public void deleteIntentById(@PathParam("appId") String appId,
-                                  @PathParam("key") String keyString) {
-        final ApplicationId app = get(CoreService.class).getAppId(appId);
-
-        Intent intent = get(IntentService.class).getIntent(Key.of(keyString, app));
-        IntentService service = get(IntentService.class);
-
-        if (intent == null) {
-            intent = service
-                    .getIntent(Key.of(Long.decode(keyString), app));
-        }
-        if (intent == null) {
-            // No such intent.  REST standards recommend a positive status code
-            // in this case.
-            return;
-        }
-
-
-        Key key = intent.key();
-
-        // set up latch and listener to track uninstall progress
-        CountDownLatch latch = new CountDownLatch(1);
-
-        IntentListener listener = new DeleteListener(key, latch);
-        service.addListener(listener);
-
-        try {
-            // request the withdraw
-            service.withdraw(intent);
-
-            try {
-                latch.await(WITHDRAW_EVENT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
-            } catch (InterruptedException e) {
-                log.info("REST Delete operation timed out waiting for intent {}", key);
-            }
-            // double check the state
-            IntentState state = service.getIntentState(key);
-            if (state == WITHDRAWN || state == FAILED) {
-                service.purge(intent);
-            }
-
-        } finally {
-            // clean up the listener
-            service.removeListener(listener);
-        }
-    }
-
-    /**
-     * Creates an intent from a POST of a JSON string and attempts to apply it.
+     * Submit a new intent.
+     * Creates and submits intent from the JSON request.
      *
      * @param stream input JSON
      * @return status of the request - CREATED if the JSON is correct,
@@ -215,4 +162,60 @@
         }
     }
 
+    /**
+     * Withdraw intent.
+     * Withdraws the specified intent from the system.
+     *
+     * @param appId application identifier
+     * @param key   intent key
+     */
+    @DELETE
+    @Path("{appId}/{key}")
+    public void deleteIntentById(@PathParam("appId") String appId,
+                                 @PathParam("key") String key) {
+        final ApplicationId app = get(CoreService.class).getAppId(appId);
+
+        Intent intent = get(IntentService.class).getIntent(Key.of(key, app));
+        IntentService service = get(IntentService.class);
+
+        if (intent == null) {
+            intent = service
+                    .getIntent(Key.of(Long.decode(key), app));
+        }
+        if (intent == null) {
+            // No such intent.  REST standards recommend a positive status code
+            // in this case.
+            return;
+        }
+
+
+        Key k = intent.key();
+
+        // set up latch and listener to track uninstall progress
+        CountDownLatch latch = new CountDownLatch(1);
+
+        IntentListener listener = new DeleteListener(k, latch);
+        service.addListener(listener);
+
+        try {
+            // request the withdraw
+            service.withdraw(intent);
+
+            try {
+                latch.await(WITHDRAW_EVENT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                log.info("REST Delete operation timed out waiting for intent {}", k);
+            }
+            // double check the state
+            IntentState state = service.getIntentState(k);
+            if (state == WITHDRAWN || state == FAILED) {
+                service.purge(intent);
+            }
+
+        } finally {
+            // clean up the listener
+            service.removeListener(listener);
+        }
+    }
+
 }
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/LinksWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/LinksWebResource.java
index 1922d03..c627019 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/LinksWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/LinksWebResource.java
@@ -30,7 +30,7 @@
 import static org.onosproject.net.PortNumber.portNumber;
 
 /**
- * REST resource for interacting with the inventory of infrastructure links.
+ * Manage inventory of infrastructure links.
  */
 @Path("links")
 public class LinksWebResource extends AbstractWebResource {
@@ -41,6 +41,15 @@
         EGRESS
     }
 
+    /**
+     * Get infrastructure links.
+     * Returns array of all links, or links for the specified device or port.
+     *
+     * @param deviceId  (optional) device identifier
+     * @param port      (optional) port number
+     * @param direction (optional) direction qualifier
+     * @return 200 OK
+     */
     @GET
     public Response getLinks(@QueryParam("device") String deviceId,
                              @QueryParam("port") String port,
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
index 348fa63..9e2b627 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
@@ -33,13 +33,13 @@
 import java.io.InputStream;
 
 /**
- * REST resource for injecting and retrieving common network configuration.
+ * Manage network configurations.
  */
 @Path("network/configuration")
 public class NetworkConfigWebResource extends AbstractWebResource {
 
     /**
-     * Returns entire network configuration base.
+     * Get entire network configuration base.
      *
      * @return network configuration JSON
      */
@@ -55,7 +55,7 @@
     }
 
     /**
-     * Returns the network configuration for the specified subject class.
+     * Get all network configuration for a subject class.
      *
      * @param subjectKey subject class key
      * @return network configuration JSON
@@ -72,7 +72,7 @@
     }
 
     /**
-     * Returns the network configuration for the specified subject.
+     * Get all network configuration for a subject.
      *
      * @param subjectKey subject class key
      * @param subject    subject key
@@ -87,13 +87,12 @@
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = mapper().createObjectNode();
         produceSubjectJson(service, root,
-                service.getSubjectFactory(subjectKey).createSubject(subject));
+                           service.getSubjectFactory(subjectKey).createSubject(subject));
         return ok(root).build();
     }
 
     /**
-     * Returns the network configuration for the specified subject and given
-     * configuration class.
+     * Get specific network configuration for a subject.
      *
      * @param subjectKey subject class key
      * @param subject    subject key
@@ -126,7 +125,7 @@
 
 
     /**
-     * Uploads network configuration in bulk.
+     * Upload bulk network configuration.
      *
      * @param request network configuration JSON rooted at the top node
      * @throws IOException if unable to parse the request
@@ -140,12 +139,12 @@
         ObjectNode root = (ObjectNode) mapper().readTree(request);
         root.fieldNames()
                 .forEachRemaining(sk -> consumeJson(service, (ObjectNode) root.path(sk),
-                        service.getSubjectFactory(sk)));
+                                                    service.getSubjectFactory(sk)));
         return Response.ok().build();
     }
 
     /**
-     * Uploads network configuration for the specified subject class.
+     * Upload multiple network configurations for a subject class.
      *
      * @param subjectKey subject class key
      * @param request    network configuration JSON rooted at the top node
@@ -165,7 +164,7 @@
     }
 
     /**
-     * Uploads network configuration for the specified subject.
+     * Upload mutliple network configurations for a subject.
      *
      * @param subjectKey subject class key
      * @param subject    subject key
@@ -183,14 +182,13 @@
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = (ObjectNode) mapper().readTree(request);
         consumeSubjectJson(service, root,
-                service.getSubjectFactory(subjectKey).createSubject(subject),
-                subjectKey);
+                           service.getSubjectFactory(subjectKey).createSubject(subject),
+                           subjectKey);
         return Response.ok().build();
     }
 
     /**
-     * Uploads network configuration for the specified subject and given
-     * configuration class.
+     * Upload specific network configuration for a subject.
      *
      * @param subjectKey subject class key
      * @param subject    subject key
@@ -210,16 +208,16 @@
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = (ObjectNode) mapper().readTree(request);
         service.applyConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
-                service.getConfigClass(subjectKey, configKey), root);
+                            service.getConfigClass(subjectKey, configKey), root);
         return Response.ok().build();
     }
 
     private void consumeJson(NetworkConfigService service, ObjectNode classNode,
                              SubjectFactory subjectFactory) {
         classNode.fieldNames().forEachRemaining(s ->
-                consumeSubjectJson(service, (ObjectNode) classNode.path(s),
-                        subjectFactory.createSubject(s),
-                        subjectFactory.subjectKey()));
+                                                        consumeSubjectJson(service, (ObjectNode) classNode.path(s),
+                                                                           subjectFactory.createSubject(s),
+                                                                           subjectFactory.subjectKey()));
     }
 
     private void consumeSubjectJson(NetworkConfigService service,
@@ -227,12 +225,47 @@
                                     String subjectKey) {
         subjectNode.fieldNames().forEachRemaining(c ->
             service.applyConfig(subject, service.getConfigClass(subjectKey, c),
-                    (ObjectNode) subjectNode.path(c)));
+                                (ObjectNode) subjectNode.path(c)));
     }
 
 
     /**
-     * Clears network configuration for the specified subject.
+     * Clear entire network configuration base.
+     *
+     * @return empty response
+     */
+    @DELETE
+    @SuppressWarnings("unchecked")
+    public Response delete() {
+        NetworkConfigService service = get(NetworkConfigService.class);
+        service.getSubjectClasses()
+                .forEach(subjectClass -> service.getSubjects(subjectClass)
+                        .forEach(subject -> service.getConfigs(subject)
+                                .forEach(config -> service
+                                        .removeConfig(subject, config.getClass()))));
+        return Response.ok().build();
+    }
+
+    /**
+     * Clear all network configurations for a subject class.
+     *
+     * @param subjectKey subject class key
+     * @return empty response
+     */
+    @DELETE
+    @Path("{subjectKey}")
+    @SuppressWarnings("unchecked")
+    public Response delete(@PathParam("subjectKey") String subjectKey) {
+        NetworkConfigService service = get(NetworkConfigService.class);
+        service.getSubjects(service.getSubjectFactory(subjectKey).getClass())
+                .forEach(subject -> service.getConfigs(subject)
+                        .forEach(config -> service
+                                .removeConfig(subject, config.getClass())));
+        return Response.ok().build();
+    }
+
+    /**
+     * Clear all network configurations for a subject.
      *
      * @param subjectKey subject class key
      * @param subject    subject key
@@ -250,8 +283,7 @@
     }
 
     /**
-     * Clears network configuration for the specified subject and given
-     * configuration class.
+     * Clear specific network configuration for a subject.
      *
      * @param subjectKey subject class key
      * @param subject    subject key
@@ -266,45 +298,8 @@
                            @PathParam("configKey") String configKey) {
         NetworkConfigService service = get(NetworkConfigService.class);
         service.removeConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
-                service.getConfigClass(subjectKey, configKey));
+                             service.getConfigClass(subjectKey, configKey));
         return Response.ok().build();
     }
 
-
-    /**
-     * Clears all network configurations.
-     *
-     * @return empty response
-     */
-    @DELETE
-    @SuppressWarnings("unchecked")
-    public Response delete() {
-        NetworkConfigService service = get(NetworkConfigService.class);
-        service.getSubjectClasses()
-                .forEach(subjectClass -> service.getSubjects(subjectClass)
-                        .forEach(subject -> service.getConfigs(subject)
-                                .forEach(config -> service
-                                        .removeConfig(subject, config.getClass()))));
-        return Response.ok().build();
-    }
-
-
-    // TODO: this one below doesn't work correctly
-    /**
-     * Clears network configuration for the specified subject class.
-     *
-     * @param subjectKey subject class key
-     * @return empty response
-     */
-    @DELETE
-    @Path("{subjectKey}/")
-    @SuppressWarnings("unchecked")
-    public Response delete(@PathParam("subjectKey") String subjectKey) {
-        NetworkConfigService service = get(NetworkConfigService.class);
-        service.getSubjects(service.getSubjectFactory(subjectKey).getClass())
-                .forEach(subject -> service.getConfigs(subject)
-                        .forEach(config -> service
-                                .removeConfig(subject, config.getClass())));
-        return Response.ok().build();
-    }
 }
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/PathsWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/PathsWebResource.java
index 42bd4e1..baa1b1e 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/PathsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/PathsWebResource.java
@@ -33,7 +33,7 @@
 import org.onosproject.rest.AbstractWebResource;
 
 /**
- * REST resource for interacting with path calculations.
+ * Compute paths in the network graph.
  */
 @Path("paths")
 public class PathsWebResource extends AbstractWebResource {
@@ -46,19 +46,15 @@
      * @return HostId if the id is valid, null otherwise
      */
     private HostId isHostId(String id) {
-
-        if (!id.matches("..:..:..:..:..:../.*")) {
-            return null;
-        }
-
-        return HostId.hostId(id);
+        return id.matches("..:..:..:..:..:../.*") ? HostId.hostId(id) : null;
     }
 
     /**
-     * Gets the paths between two elements.
+     * Get all shortest paths between any two hosts or devices.
+     * Returns array of all shortest paths between any two elements.
      *
-     * @param src source
-     * @param dst destination
+     * @param src source identifier
+     * @param dst destination identifier
      * @return path data
      */
     @GET
@@ -81,10 +77,8 @@
             dstElement = DeviceId.deviceId(dst);
         }
 
-        Set<org.onosproject.net.Path> paths =
-                pathService.getPaths(srcElement, dstElement);
-        ObjectNode root =
-                    encodeArray(org.onosproject.net.Path.class, "paths", paths);
+        Set<org.onosproject.net.Path> paths = pathService.getPaths(srcElement, dstElement);
+        ObjectNode root = encodeArray(org.onosproject.net.Path.class, "paths", paths);
         return ok(root).build();
     }
 
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java
index 36bb0ad..2ffa229 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java
@@ -44,7 +44,7 @@
 import static org.onosproject.net.PortNumber.portNumber;
 
 /**
- * Statistics REST APIs.
+ * Query flow statistics.
  */
 @Path("statistics")
 public class StatisticsWebResource  extends AbstractWebResource {
@@ -52,7 +52,7 @@
     UriInfo uriInfo;
 
     /**
-     * Gets the Load statistics for all links, or for a specific link.
+     * Get load statistics for all links or for a specific link.
      *
      * @param deviceId (optional) device ID for a specific link
      * @param port (optional) port number for a specified link
@@ -77,7 +77,6 @@
         JsonCodec<Load> loadCodec = codec(Load.class);
         StatisticService statsService = getService(StatisticService.class);
 
-
         StreamSupport.stream(Spliterators.spliteratorUnknownSize(
                 links.iterator(), Spliterator.ORDERED), false)
                 .forEach(link -> {
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/TopologyWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/TopologyWebResource.java
index 55b6803..f6ae825 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/TopologyWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/TopologyWebResource.java
@@ -39,16 +39,15 @@
 import static org.onlab.util.Tools.nullIsNotFound;
 
 /**
- * REST resource for interacting with the inventory of clusters.
+ * Query network topology graph and its components.
  */
-
 @Path("topology")
 public class TopologyWebResource extends AbstractWebResource {
 
     public static final String CLUSTER_NOT_FOUND = "Cluster is not found";
 
     /**
-     * Gets the topology overview for a REST GET operation.
+     * Get overview of current topology.
      *
      * @return topology overview
      */
@@ -61,7 +60,7 @@
     }
 
     /**
-     * Gets the topology clusters overview for a REST GET operation.
+     * Get overview of topology SCCs.
      *
      * @return topology clusters overview
      */
@@ -77,7 +76,7 @@
     }
 
     /**
-     * Gets details for a topology cluster for a REST GET operation.
+     * Get details of a specific SCC.
      *
      * @param clusterId id of the cluster to query
      * @return topology cluster details
@@ -100,7 +99,7 @@
     }
 
     /**
-     * Gets devices for a topology cluster for a REST GET operation.
+     * Get devices in a specific SCC.
      *
      * @param clusterId id of the cluster to query
      * @return topology cluster devices
@@ -123,7 +122,7 @@
     }
 
     /**
-     * Gets links for a topology cluster for a REST GET operation.
+     * Get links in specific SCC.
      *
      * @param clusterId id of the cluster to query
      * @return topology cluster links
@@ -171,18 +170,16 @@
     }
 
     /**
-     * Gets the broadcast flag of a connect point for a REST GET operation.
+     * Test if a connect point is in broadcast set.
      *
-     * @param connectPointString string representation of the connect point to query.
-     *                           Format is deviceid:portnumber
+     * @param connectPointString deviceid:portnumber
      * @return JSON representation of true if the connect point is broadcast,
      *         false otherwise
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     @Path("broadcast/{connectPoint}")
-    public Response getConnectPointBroadcast(
-            @PathParam("connectPoint") String connectPointString) {
+    public Response getConnectPointBroadcast(@PathParam("connectPoint") String connectPointString) {
         Topology topology = get(TopologyService.class).currentTopology();
 
         DeviceId deviceId = DeviceId.deviceId(getDeviceId(connectPointString));
@@ -197,18 +194,16 @@
     }
 
     /**
-     * Gets the infrastructure flag of a connect point for a REST GET operation.
+     * Test if a connect point is infrastructure or edge.
      *
-     * @param connectPointString string representation of the connect point to query.
-     *                           Format is deviceid:portnumber
+     * @param connectPointString deviceid:portnumber
      * @return JSON representation of true if the connect point is broadcast,
      *         false otherwise
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     @Path("infrastructure/{connectPoint}")
-    public Response getConnectPointInfrastructure(
-            @PathParam("connectPoint") String connectPointString) {
+    public Response getConnectPointInfrastructure(@PathParam("connectPoint") String connectPointString) {
         Topology topology = get(TopologyService.class).currentTopology();
 
         DeviceId deviceId = DeviceId.deviceId(getDeviceId(connectPointString));