Adding DefaultServlet to support static resources

Adding DefaultServlet to make embeded jetty not only
support Jersey, but also support static resources, like
html, css and js.

Issue-ID: POLICY-2311
Signed-off-by: Hengye <yehui.wang@est.tech>
Change-Id: I71309036627d75dcc56947b395688a4e2f22c0ce
diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java
index 0adf782..030bf93 100644
--- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java
+++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java
@@ -3,6 +3,7 @@
  * ONAP
  * ================================================================================
  * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2020 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -83,7 +84,8 @@
      * @param restClass JAX-RS API Class
      *
      * @throws IllegalArgumentException unable to process because of invalid input
-     * @throws IllegalStateException unable to process because of invalid state
+     * @throws IllegalStateException unable to process because of invalid state, for example
+     *         different types of servlets map to the same servletPath
      */
     void addServletClass(String servletPath, String restClass);
 
@@ -94,11 +96,24 @@
      * @param restPackage JAX-RS package to scan
      *
      * @throws IllegalArgumentException unable to process because of invalid input
-     * @throws IllegalStateException unable to process because of invalid state
+     * @throws IllegalStateException unable to process because of invalid state, for example
+     *         different types of servlets map to the same servletPath
      */
     void addServletPackage(String servletPath, String restPackage);
 
     /**
+     * Add org.eclipse.jetty.servlet.DefaultServlet into context
+     *
+     * @param servletPath servlet path
+     * @param resourceBase static resources folder
+     *
+     * @throws IllegalArgumentException unable to process because of invalid input
+     * @throws IllegalStateException unable to process because of invalid state, for example
+     *         different types of servlets map to the same servletPath
+     */
+    void addServletResource(String servletPath, String resourceBase);
+
+    /**
      * Blocking start of the http server.
      *
      * @param maxWaitTime max time to wait for the start to take place
diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java
index d809479..462f079 100644
--- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java
+++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java
@@ -3,7 +3,7 @@
  * policy-endpoints
  * ================================================================================
  * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
- * Modifications Copyright (C) 2019 Nordix Foundation.
+ * Modifications Copyright (C) 2019-2020 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
 import io.swagger.jersey.config.JerseyJaxrsConfig;
 import java.util.HashMap;
 import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jetty.servlet.DefaultServlet;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.glassfish.jersey.server.ServerProperties;
 import org.onap.policy.common.utils.network.NetworkUtil;
@@ -71,15 +72,36 @@
      */
     protected static final String SWAGGER_INIT_CLASSNAMES_PARAM_VALUE =
             "io.swagger.jaxrs.listing.ApiListingResource," + "io.swagger.jaxrs.listing.SwaggerSerializers";
+
+    /**
+     * Servlet Holder Resource Base Path.
+     */
+    protected static final String SERVLET_HOLDER_RESOURCE_BASE = "resourceBase";
+
+    /**
+     * Servlet Holder Directory Allowed.
+     */
+    protected static final String SERVLET_HOLDER_DIR_ALLOWED = "dirAllowed";
+
+    /**
+     * Servlet Holder Path Information Only.
+     */
+    protected static final String SERVLET_HOLDER_PATH_INFO_ONLY = "pathInfoOnly";
+
     /**
      * Logger.
      */
     protected static Logger logger = LoggerFactory.getLogger(JettyJerseyServer.class);
 
     /**
-     * Container for servlets.
+     * Container for jersey servlets.
      */
-    protected HashMap<String, ServletHolder> servlets = new HashMap<>();
+    protected HashMap<String, ServletHolder> jerseyServlets = new HashMap<>();
+
+    /**
+     * Container for default servlets.
+     */
+    protected HashMap<String, ServletHolder> defaultServlets = new HashMap<>();
 
     /**
      * Swagger ID.
@@ -144,9 +166,9 @@
      *
      * @throws IllegalArgumentException if invalid arguments are provided
      */
-    protected synchronized ServletHolder getServlet(String servletPath) {
+    protected synchronized ServletHolder getJerseyServlet(String servletPath) {
 
-        return servlets.computeIfAbsent(servletPath, key -> {
+        return jerseyServlets.computeIfAbsent(servletPath, key -> {
 
             ServletHolder jerseyServlet =
                     context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, servletPath);
@@ -156,10 +178,23 @@
         });
     }
 
+    /**
+     * Retrieves cached default servlet based on servlet path.
+     *
+     * @param servletPath servlet path
+     * @return the jetty servlet holder
+     *
+     * @throws IllegalArgumentException if invalid arguments are provided
+     */
+    protected synchronized ServletHolder getDefaultServlet(String servPath) {
+
+        return defaultServlets.computeIfAbsent(servPath, key -> context.addServlet(DefaultServlet.class, servPath));
+    }
+
     @Override
     public synchronized void addServletPackage(String servletPath, String restPackage) {
         String servPath = servletPath;
-        if (restPackage == null || restPackage.isEmpty()) {
+        if (StringUtils.isBlank(restPackage)) {
             throw new IllegalArgumentException("No discoverable REST package provided");
         }
 
@@ -167,7 +202,7 @@
             servPath = "/*";
         }
 
-        ServletHolder jerseyServlet = this.getServlet(servPath);
+        ServletHolder jerseyServlet = this.getJerseyServlet(servPath);
 
         initStandardParams(jerseyServlet);
 
@@ -189,7 +224,7 @@
     @Override
     public synchronized void addServletClass(String servletPath, String restClass) {
 
-        if (restClass == null || restClass.isEmpty()) {
+        if (StringUtils.isBlank(restClass)) {
             throw new IllegalArgumentException("No discoverable REST class provided");
         }
 
@@ -197,7 +232,7 @@
             servletPath = "/*";
         }
 
-        ServletHolder jerseyServlet = this.getServlet(servletPath);
+        ServletHolder jerseyServlet = this.getJerseyServlet(servletPath);
 
         initStandardParams(jerseyServlet);
 
@@ -216,6 +251,28 @@
         }
     }
 
+    @Override
+    public synchronized void addServletResource(String servletPath, String resoureBase) {
+
+        if (StringUtils.isBlank(resoureBase)) {
+            throw new IllegalArgumentException("No resourceBase provided");
+        }
+
+        if (servletPath == null || servletPath.isEmpty()) {
+            servletPath = "/*";
+        }
+
+        ServletHolder defaultServlet = this.getDefaultServlet(servletPath);
+
+        defaultServlet.setInitParameter(SERVLET_HOLDER_RESOURCE_BASE, resoureBase);
+        defaultServlet.setInitParameter(SERVLET_HOLDER_DIR_ALLOWED, "false");
+        defaultServlet.setInitParameter(SERVLET_HOLDER_PATH_INFO_ONLY, "true");
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("{}: added REST class: {}", this, defaultServlet.dump());
+        }
+    }
+
     /**
      * Adds "standard" parameters to the initParameter set. Sets swagger parameters, if specified, and sets the class
      * provider property. This can be invoked multiple times, but only the first actually causes any changes to the
@@ -255,8 +312,9 @@
     @Override
     public String toString() {
         StringBuilder builder = new StringBuilder();
-        builder.append("JettyJerseyServer [servlets=").append(servlets).append(", swaggerId=").append(swaggerId)
-                .append(", toString()=").append(super.toString()).append("]");
+        builder.append("JettyJerseyServer [Jerseyservlets=").append(jerseyServlets).append(", Defaultservlets=")
+                .append(defaultServlets).append(", swaggerId=").append(swaggerId).append(", toString()=")
+                .append(super.toString()).append("]");
         return builder.toString();
     }
 }