[ONOS-2706] Insert Support for REST API model schema in  Swagger UI

Change-Id: Iad158d90f203101fd23328f30f2507b7677a1997
diff --git a/apps/dhcp/app/src/main/java/org/onosproject/dhcp/rest/DhcpWebResource.java b/apps/dhcp/app/src/main/java/org/onosproject/dhcp/rest/DhcpWebResource.java
index 1d4174b..3b5baf9 100644
--- a/apps/dhcp/app/src/main/java/org/onosproject/dhcp/rest/DhcpWebResource.java
+++ b/apps/dhcp/app/src/main/java/org/onosproject/dhcp/rest/DhcpWebResource.java
@@ -51,6 +51,7 @@
      * Shows lease, renewal and rebinding times in seconds.
      *
      * @return 200 OK
+     * @rsModel DhcpConfigGet
      */
     @GET
     @Path("config")
@@ -77,14 +78,13 @@
         final Map<HostId, IpAssignment> intents = service.listMapping();
         ArrayNode arrayNode = root.putArray("mappings");
         intents.entrySet().forEach(i -> arrayNode.add(mapper().createObjectNode()
-                .put("host", i.getKey().toString())
-                .put("ip", i.getValue().ipAddress().toString())));
+                                                              .put("host", i.getKey().toString())
+                                                              .put("ip", i.getValue().ipAddress().toString())));
 
         return ok(root.toString()).build();
     }
 
 
-
     /**
      * Get all available IPs.
      * Shows all the IPs in the free pool of the DHCP Server.
@@ -106,6 +106,7 @@
      * Post a new static MAC/IP binding.
      * Registers a static binding to the DHCP server, and displays the current set of bindings.
      *
+     * @rsModel DhcpConfigPut
      * @param stream JSON stream
      * @return 200 OK
      */
@@ -114,7 +115,6 @@
     @Consumes(MediaType.APPLICATION_JSON)
     public Response setMapping(InputStream stream) {
         ObjectNode root = mapper().createObjectNode();
-
         try {
             ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
             JsonNode macID = jsonTree.get("mac");
@@ -122,16 +122,18 @@
             if (macID != null && ip != null) {
 
                 if (!service.setStaticMapping(MacAddress.valueOf(macID.asText()),
-                        Ip4Address.valueOf(ip.asText()), false, Lists.newArrayList())) {
-                    throw new IllegalArgumentException("Static Mapping Failed. The IP maybe unavailable.");
+                                              Ip4Address.valueOf(ip.asText()),
+                                              false, Lists.newArrayList())) {
+                    throw new IllegalArgumentException("Static Mapping Failed. " +
+                                                               "The IP maybe unavailable.");
                 }
             }
 
             final Map<HostId, IpAssignment> intents = service.listMapping();
             ArrayNode arrayNode = root.putArray("mappings");
             intents.entrySet().forEach(i -> arrayNode.add(mapper().createObjectNode()
-                    .put("host", i.getKey().toString())
-                    .put("ip", i.getValue().ipAddress().toString())));
+                                                                  .put("host", i.getKey().toString())
+                                                                  .put("ip", i.getValue().ipAddress().toString())));
         } catch (IOException e) {
             throw new IllegalArgumentException(e.getMessage());
         }
@@ -157,8 +159,8 @@
         final Map<HostId, IpAssignment> intents = service.listMapping();
         ArrayNode arrayNode = root.putArray("mappings");
         intents.entrySet().forEach(i -> arrayNode.add(mapper().createObjectNode()
-                .put("host", i.getKey().toString())
-                .put("ip", i.getValue().ipAddress().toString())));
+                                                              .put("host", i.getKey().toString())
+                                                              .put("ip", i.getValue().ipAddress().toString())));
 
         return ok(root.toString()).build();
     }
diff --git a/apps/dhcp/app/src/main/resources/definitions/DhcpConfigGet.json b/apps/dhcp/app/src/main/resources/definitions/DhcpConfigGet.json
new file mode 100644
index 0000000..9e451b3
--- /dev/null
+++ b/apps/dhcp/app/src/main/resources/definitions/DhcpConfigGet.json
@@ -0,0 +1,26 @@
+{
+  "type": "object",
+  "required": [
+    "leaseTime",
+    "renewalTime",
+    "rebindingTime"
+  ],
+  "properties": {
+    "leaseTime": {
+      "type": "integer",
+      "format": "int64",
+      "example": "250"
+    },
+    "renewalTime": {
+      "type": "integer",
+      "format": "int64",
+      "example": "250"
+    },
+    "rebindingTime": {
+      "type": "integer",
+      "format": "int64",
+      "example": "250"
+    }
+  }
+}
+
diff --git a/apps/dhcp/app/src/main/resources/definitions/DhcpConfigPut.json b/apps/dhcp/app/src/main/resources/definitions/DhcpConfigPut.json
new file mode 100644
index 0000000..a8eb537
--- /dev/null
+++ b/apps/dhcp/app/src/main/resources/definitions/DhcpConfigPut.json
@@ -0,0 +1,17 @@
+{
+  "type": "object",
+  "required": [
+    "mac",
+    "ip"],
+  "properties": {
+    "mac": {
+      "type": "String",
+      "example": "be:48:89:d5:75:59"
+    },
+    "ip": {
+      "type": "String",
+      "example": "10.128.12.4"
+    }
+  }
+}
+
diff --git a/docs/external.xml b/docs/external.xml
index 69ac057..f2c568a 100644
--- a/docs/external.xml
+++ b/docs/external.xml
@@ -49,7 +49,8 @@
                 <version>2.10.1</version>
                 <configuration>
                     <show>package</show>
-                    <excludePackageNames>@external-excludes</excludePackageNames>
+                    <excludePackageNames>@external-excludes
+                    </excludePackageNames>
                     <docfilessubdirs>true</docfilessubdirs>
                     <doctitle>ONOS Java API (1.4.0-SNAPSHOT)</doctitle>
                     <groups>
@@ -58,7 +59,8 @@
                             <packages>@external-apis</packages>
                         </group>
                         <group>
-                            <title>Incubator for Network Model &amp; Services</title>
+                            <title>Incubator for Network Model &amp; Services
+                            </title>
                             <packages>@external-incubator-apis</packages>
                         </group>
                         <group>
@@ -66,6 +68,13 @@
                             <packages>@utils</packages>
                         </group>
                     </groups>
+                    <tags>
+                        <tag>
+                            <name>rsModel</name>
+                            <placement>m</placement>
+                            <head>Json model for REST api:</head>
+                        </tag>
+                    </tags>
                 </configuration>
             </plugin>
         </plugins>
diff --git a/docs/internal.xml b/docs/internal.xml
index 602479e..e0ba360 100644
--- a/docs/internal.xml
+++ b/docs/internal.xml
@@ -51,14 +51,16 @@
                     <show>package</show>
                     <docfilessubdirs>true</docfilessubdirs>
                     <doctitle>ONOS Java API (1.4.0-SNAPSHOT)</doctitle>
-                    <excludePackageNames>@internal-excludes</excludePackageNames>
+                    <excludePackageNames>@internal-excludes
+                    </excludePackageNames>
                     <groups>
                         <group>
                             <title>Network Model &amp; Services</title>
                             <packages>@internal-apis</packages>
                         </group>
                         <group>
-                            <title>Incubator for Network Model &amp; Services</title>
+                            <title>Incubator for Network Model &amp; Services
+                            </title>
                             <packages>@internal-incubator-apis</packages>
                         </group>
                         <group>
@@ -70,7 +72,9 @@
                             <packages>@internal-stores</packages>
                         </group>
                         <group>
-                            <title>Incubator for Core Subsystems &amp; Distributed Stores</title>
+                            <title>Incubator for Core Subsystems &amp;
+                                Distributed Stores
+                            </title>
                             <packages>@internal-incubator</packages>
                         </group>
                         <group>
@@ -114,10 +118,18 @@
                             <packages>@internal-apps</packages>
                         </group>
                         <group>
-                            <title>Test Instrumentation &amp; Applications</title>
+                            <title>Test Instrumentation &amp; Applications
+                            </title>
                             <packages>@internal-test-apps</packages>
                         </group>
                     </groups>
+                    <tags>
+                        <tag>
+                            <name>rsModel</name>
+                            <placement>m</placement>
+                            <head>Json model for REST api:</head>
+                        </tag>
+                    </tags>
                 </configuration>
             </plugin>
         </plugins>
diff --git a/pom.xml b/pom.xml
index 89930f5..937794c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,7 @@
     <description>Open Network Operating System root project</description>
 
     <modules>
+        <module>tools/package/maven-plugin</module>
         <module>utils</module>
         <module>core</module>
         <module>web</module>
@@ -545,7 +546,20 @@
                         <rerunFailingTestsCount>1</rerunFailingTestsCount>
                     </configuration>
                 </plugin>
-
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-javadoc-plugin</artifactId>
+                    <version>2.10.3</version>
+                    <configuration>
+                        <tags>
+                            <tag>
+                                <name>rsModel</name>
+                                <placement>m</placement>
+                                <head>Json model for REST api:</head>
+                            </tag>
+                        </tags>
+                    </configuration>
+                </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-jar-plugin</artifactId>
@@ -631,8 +645,8 @@
                 <plugin>
                     <groupId>org.onosproject</groupId>
                     <artifactId>onos-maven-plugin</artifactId>
-                    <version>1.5</version>
-                      <executions>
+                    <version>1.6-SNAPSHOT</version>
+                    <executions>
                         <execution>
                             <id>cfg</id>
                             <phase>generate-resources</phase>
diff --git a/tools/package/maven-plugin/pom.xml b/tools/package/maven-plugin/pom.xml
index 54839b1..c2b5d8a 100644
--- a/tools/package/maven-plugin/pom.xml
+++ b/tools/package/maven-plugin/pom.xml
@@ -85,6 +85,11 @@
             <artifactId>jackson-annotations</artifactId>
             <version>2.4.2</version>
         </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.4</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/tools/package/maven-plugin/src/main/java/org/onosproject/maven/OnosSwaggerMojo.java b/tools/package/maven-plugin/src/main/java/org/onosproject/maven/OnosSwaggerMojo.java
index ea84745..a75127a 100644
--- a/tools/package/maven-plugin/src/main/java/org/onosproject/maven/OnosSwaggerMojo.java
+++ b/tools/package/maven-plugin/src/main/java/org/onosproject/maven/OnosSwaggerMojo.java
@@ -18,6 +18,7 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Charsets;
 import com.google.common.io.ByteStreams;
 import com.google.common.io.Files;
 import com.thoughtworks.qdox.JavaProjectBuilder;
@@ -124,11 +125,13 @@
             ObjectNode root = initializeRoot();
             ArrayNode tags = mapper.createArrayNode();
             ObjectNode paths = mapper.createObjectNode();
+            ObjectNode definitions = mapper.createObjectNode();
 
             root.set("tags", tags);
             root.set("paths", paths);
+            root.set("definitions", definitions);
 
-            builder.getClasses().forEach(jc -> processClass(jc, paths, tags));
+            builder.getClasses().forEach(jc -> processClass(jc, paths, tags, definitions));
 
             if (paths.size() > 0) {
                 getLog().info("Generating ONOS REST API documentation...");
@@ -172,7 +175,7 @@
 
     // Checks whether javaClass has a path tag associated with it and if it does
     // processes its methods and creates a tag for the class on the root
-    void processClass(JavaClass javaClass, ObjectNode paths, ArrayNode tags) {
+    void processClass(JavaClass javaClass, ObjectNode paths, ArrayNode tags, ObjectNode definitions) {
         // If the class does not have a Path tag then ignore it
         JavaAnnotation annotation = getPathAnnotation(javaClass);
         if (annotation == null) {
@@ -199,7 +202,7 @@
         ArrayNode tagArray = mapper.createArrayNode();
         tagArray.add(tagPath);
 
-        processAllMethods(javaClass, resourcePath, paths, tagArray);
+        processAllMethods(javaClass, resourcePath, paths, tagArray, definitions);
     }
 
     private JavaAnnotation getPathAnnotation(JavaClass javaClass) {
@@ -211,7 +214,7 @@
     // Checks whether a class's methods are REST methods and then places all the
     // methods under a specific path into the paths node
     private void processAllMethods(JavaClass javaClass, String resourcePath,
-                                   ObjectNode paths, ArrayNode tagArray) {
+                                   ObjectNode paths, ArrayNode tagArray, ObjectNode definitions) {
         // map of the path to its methods represented by an ObjectNode
         Map<String, ObjectNode> pathMap = new HashMap<>();
 
@@ -221,7 +224,7 @@
                 if (name.equals(POST) || name.equals(GET) || name.equals(DELETE) || name.equals(PUT)) {
                     // substring(12) removes "javax.ws.rs."
                     String method = annotation.getType().toString().substring(12).toLowerCase();
-                    processRestMethod(javaMethod, method, pathMap, resourcePath, tagArray);
+                    processRestMethod(javaMethod, method, pathMap, resourcePath, tagArray, definitions);
                 }
             });
         });
@@ -236,9 +239,10 @@
 
     private void processRestMethod(JavaMethod javaMethod, String method,
                                    Map<String, ObjectNode> pathMap,
-                                   String resourcePath, ArrayNode tagArray) {
+                                   String resourcePath, ArrayNode tagArray, ObjectNode definitions) {
         String fullPath = resourcePath, consumes = "", produces = "",
                 comment = javaMethod.getComment();
+        DocletTag tag = javaMethod.getTagByName("rsModel");
         for (JavaAnnotation annotation : javaMethod.getAnnotations()) {
             String name = annotation.getType().getName();
             if (name.equals(PATH)) {
@@ -256,12 +260,19 @@
         methodNode.set("tags", tagArray);
 
         addSummaryDescriptions(methodNode, comment);
-        processParameters(javaMethod, methodNode);
+        addJsonSchemaDefinition(definitions, tag);
+        addJsonSchemaDefinition(definitions, tag);
+
+        processParameters(javaMethod, methodNode, method, tag);
 
         processConsumesProduces(methodNode, "consumes", consumes);
         processConsumesProduces(methodNode, "produces", produces);
-
-        addResponses(methodNode);
+        if (tag == null || ((method.toLowerCase().equals("post") || method.toLowerCase().equals("put"))
+                && !(tag.getParameters().size() > 1))) {
+            addResponses(methodNode, tag, false);
+        } else {
+            addResponses(methodNode, tag, true);
+        }
 
         ObjectNode operations = pathMap.get(fullPath);
         if (operations == null) {
@@ -273,6 +284,24 @@
         }
     }
 
+    private void addJsonSchemaDefinition(ObjectNode definitions, DocletTag tag) {
+        File definitionsDirectory = new File(srcDirectory + "/src/main/resources/definitions");
+        if (tag != null) {
+            tag.getParameters().stream().forEach(param -> {
+                try {
+                    File config = new File(definitionsDirectory.getAbsolutePath() + "/"
+                                                   + param + ".json");
+                    String lines = Files.readLines(config, Charsets.UTF_8).stream().reduce((t, u) -> t + u).
+                            get();
+                    definitions.putPOJO(param, lines);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            });
+
+        }
+    }
+
     private void processConsumesProduces(ObjectNode methodNode, String type, String io) {
         if (!io.equals("")) {
             ArrayNode array = mapper.createArrayNode();
@@ -299,13 +328,19 @@
 
     // Temporary solution to add responses to a method
     // TODO Provide annotations in the web resources for responses and parse them
-    private void addResponses(ObjectNode methodNode) {
+    private void addResponses(ObjectNode methodNode, DocletTag tag, boolean responseJson) {
         ObjectNode responses = mapper.createObjectNode();
         methodNode.set("responses", responses);
 
         ObjectNode success = mapper.createObjectNode();
         success.put("description", "successful operation");
         responses.set("200", success);
+        if (tag != null && responseJson) {
+            ObjectNode schema = mapper.createObjectNode();
+            tag.getParameters().stream().forEach(
+                    param -> schema.put("$ref", "#/definitions/" + param));
+            success.set("schema", schema);
+        }
 
         ObjectNode defaultObj = mapper.createObjectNode();
         defaultObj.put("description", "Unexpected error");
@@ -329,7 +364,7 @@
     }
 
     // Processes parameters of javaMethod and enters the proper key-values into the methodNode
-    private void processParameters(JavaMethod javaMethod, ObjectNode methodNode) {
+    private void processParameters(JavaMethod javaMethod, ObjectNode methodNode, String method, DocletTag tag) {
         ArrayNode parameters = mapper.createArrayNode();
         methodNode.set("parameters", parameters);
         boolean required = true;
@@ -346,7 +381,8 @@
 
             if (pathType != null) { //the parameter is a path or query parameter
                 individualParameterNode.put("name",
-                                            pathType.getNamedParameter("value").toString().replace("\"", ""));
+                                            pathType.getNamedParameter("value")
+                                                    .toString().replace("\"", ""));
                 if (pathType.getType().getName().equals(PATH_PARAM)) {
                     individualParameterNode.put("in", "path");
                 } else if (pathType.getType().getName().equals(QUERY_PARAM)) {
@@ -357,10 +393,16 @@
                 individualParameterNode.put("name", annotationName);
                 individualParameterNode.put("in", "body");
 
-                // TODO add actual hardcoded schemas and a type
-                // body parameters must have a schema associated with them
-                ArrayNode schema = mapper.createArrayNode();
-                individualParameterNode.set("schema", schema);
+                // Adds the reference to the Json model for the input
+                // that goes in the post or put operation
+                if (tag != null && (method.toLowerCase().equals("post") ||
+                        method.toLowerCase().equals("put"))) {
+                    ObjectNode schema = mapper.createObjectNode();
+                    tag.getParameters().stream().forEach(param -> {
+                        schema.put("$ref", "#/definitions/" + param);
+                    });
+                    individualParameterNode.set("schema", schema);
+                }
             }
             for (DocletTag p : javaMethod.getTagsByName("param")) {
                 if (p.getValue().contains(annotationName)) {