You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@edgent.apache.org by cd...@apache.org on 2018/08/01 12:56:06 UTC

[3/3] incubator-edgent git commit: - Added multiple alternatives for server modules with different servlet engines.

- Added multiple alternatives for server modules with different servlet engines.


Project: http://git-wip-us.apache.org/repos/asf/incubator-edgent/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-edgent/commit/d07dff67
Tree: http://git-wip-us.apache.org/repos/asf/incubator-edgent/tree/d07dff67
Diff: http://git-wip-us.apache.org/repos/asf/incubator-edgent/diff/d07dff67

Branch: refs/heads/feature/alternate-servlet-engines
Commit: d07dff673edb8d4c89cefe11b746130d55be7b83
Parents: 572cde0
Author: Christofer Dutz <ch...@c-ware.de>
Authored: Wed Aug 1 14:55:55 2018 +0200
Committer: Christofer Dutz <ch...@c-ware.de>
Committed: Wed Aug 1 14:55:55 2018 +0200

----------------------------------------------------------------------
 DEVELOPMENT.md                                  |   2 +-
 JAVA_SUPPORT.md                                 |   2 +-
 api/execution/pom.xml                           |   1 -
 connectors/csv/pom.xml                          |   1 -
 connectors/jdbc/pom.xml                         |   2 +-
 connectors/kafka/pom.xml                        |   4 +-
 connectors/websocket-jetty/pom.xml              |   2 +-
 connectors/websocket-server/pom.xml             |   2 +-
 console/pom.xml                                 |   4 +-
 console/server-jetty/pom.xml                    | 160 +++++++++++++++
 .../edgent/console/server/HttpServer.java       | 174 ++++++++++++++++
 .../edgent/console/server/ServerUtil.java       |  84 ++++++++
 .../src/main/remote-resources/META-INF/LICENSE  |  42 ++++
 .../src/main/remote-resources/META-INF/NOTICE   |  28 +++
 .../test/console/server/HttpServerPortTest.java |  58 ++++++
 .../test/console/server/HttpServerTest.java     | 172 ++++++++++++++++
 .../test/console/server/ServerUtilTest.java     |  43 ++++
 console/server-tomcat/pom.xml                   |  85 ++++++++
 .../edgent/console/server/HttpServer.java       | 169 ++++++++++++++++
 .../src/main/remote-resources/META-INF/LICENSE  |  42 ++++
 .../src/main/remote-resources/META-INF/NOTICE   |  28 +++
 .../test/console/server/HttpServerPortTest.java |  58 ++++++
 .../test/console/server/HttpServerTest.java     | 172 ++++++++++++++++
 console/server-undertow/pom.xml                 |  84 ++++++++
 .../edgent/console/server/HttpServer.java       | 189 ++++++++++++++++++
 .../src/main/remote-resources/META-INF/LICENSE  |  42 ++++
 .../src/main/remote-resources/META-INF/NOTICE   |  28 +++
 .../test/console/server/HttpServerPortTest.java |  58 ++++++
 .../test/console/server/HttpServerTest.java     | 172 ++++++++++++++++
 console/server/pom.xml                          | 160 ---------------
 .../edgent/console/server/HttpServer.java       | 174 ----------------
 .../edgent/console/server/ServerUtil.java       |  84 --------
 .../src/main/remote-resources/META-INF/LICENSE  |  42 ----
 .../src/main/remote-resources/META-INF/NOTICE   |  28 ---
 .../test/console/server/HttpServerPortTest.java |  58 ------
 .../test/console/server/HttpServerTest.java     | 172 ----------------
 .../test/console/server/ServerUtilTest.java     |  43 ----
 console/servlets/pom.xml                        |   8 +
 distribution/pom.xml                            |   4 +-
 platforms/android/android/hardware/pom.xml      |  11 ++
 platforms/android/android/topology/pom.xml      |  11 ++
 platforms/android/api/execution/pom.xml         |   1 -
 platforms/android/connectors/csv/pom.xml        |   1 -
 platforms/android/connectors/kafka/pom.xml      |   4 +-
 .../android/connectors/websocket-jetty/pom.xml  |   2 +-
 .../android/connectors/websocket-server/pom.xml |   2 +-
 platforms/android/distribution/pom.xml          |   4 +-
 platforms/android/pom.xml                       |   2 +-
 platforms/java7/api/execution/pom.xml           |   1 -
 platforms/java7/connectors/csv/pom.xml          |   1 -
 platforms/java7/connectors/jdbc/pom.xml         |  23 ++-
 platforms/java7/connectors/kafka/pom.xml        |   4 +-
 .../java7/connectors/websocket-jetty/pom.xml    |   2 +-
 .../java7/connectors/websocket-server/pom.xml   |   2 +-
 platforms/java7/console/pom.xml                 |   4 +-
 platforms/java7/console/server-jetty/pom.xml    | 198 +++++++++++++++++++
 .../src/main/remote-resources/META-INF/LICENSE  |  41 ++++
 .../src/main/remote-resources/META-INF/NOTICE   |  28 +++
 platforms/java7/console/server-tomcat/pom.xml   | 145 ++++++++++++++
 .../src/main/remote-resources/META-INF/LICENSE  |  41 ++++
 .../src/main/remote-resources/META-INF/NOTICE   |  28 +++
 platforms/java7/console/server-undertow/pom.xml | 140 +++++++++++++
 .../src/main/remote-resources/META-INF/LICENSE  |  41 ++++
 .../src/main/remote-resources/META-INF/NOTICE   |  28 +++
 platforms/java7/console/server/pom.xml          | 198 -------------------
 .../src/main/remote-resources/META-INF/LICENSE  |  41 ----
 .../src/main/remote-resources/META-INF/NOTICE   |  28 ---
 platforms/java7/console/servlets/pom.xml        |   2 +
 platforms/java7/distribution/pom.xml            |   4 +-
 platforms/java7/pom.xml                         |   2 +-
 platforms/java7/providers/development/pom.xml   |   2 +-
 platforms/java7/test/svt/pom.xml                |   2 +-
 pom.xml                                         |  46 ++++-
 providers/development/pom.xml                   |   2 +-
 test/svt/pom.xml                                |   2 +-
 75 files changed, 2707 insertions(+), 1068 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/DEVELOPMENT.md
----------------------------------------------------------------------
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index e7b71f8..c69f2ae 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -446,7 +446,7 @@ information may help to better understand it.
       license text as described above.  Its own LICENSE and NOTICE is
       copied from the respective files in its `src/main/remote-resources/META-INF`.
       <b>There are copies of those in under the java7 platform as well.</b>
-    * `edgent-console-server:jar` bundles the console-servlets war and as such
+    * `edgent-console-server-jetty:jar` bundles the console-servlets war and as such
       requires the same LICENSE/NOTICE/licenses treatment as the servlets war.
       <b>There are copies of its LICENSE/NOTICE in its  `src/main/remote-resources/META-INF`.
       There are copies of those in under the java7 platform as well.</b>

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/JAVA_SUPPORT.md
----------------------------------------------------------------------
diff --git a/JAVA_SUPPORT.md b/JAVA_SUPPORT.md
index 38555b3..fb770fb 100644
--- a/JAVA_SUPPORT.md
+++ b/JAVA_SUPPORT.md
@@ -145,7 +145,7 @@ and its features are supported in that environment.
 
 | Jar                               | Java 8 SE | Java 7 SE | Android | Notes |
 |-----------------------------------|-----------|-----------|---------|-------|
-|edgent-console-server-&lt;ver&gt;.jar    | yes       | yes       | no      | Uses JMX, Servlet |
+|edgent-console-server-jetty-&lt;ver&gt;.jar    | yes       | yes       | no      | Uses JMX, Servlet |
 |edgent-console-servlets-&lt;ver&gt;.war  | yes       | yes       | no      | Uses JMX, Servlet |
 
 ### Android

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/api/execution/pom.xml
----------------------------------------------------------------------
diff --git a/api/execution/pom.xml b/api/execution/pom.xml
index 0502503..50ff3a3 100644
--- a/api/execution/pom.xml
+++ b/api/execution/pom.xml
@@ -43,7 +43,6 @@
     <dependency>
       <groupId>com.google.code.gson</groupId>
       <artifactId>gson</artifactId>
-      <version>2.8.0</version>
     </dependency>
   </dependencies>
 

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/connectors/csv/pom.xml
----------------------------------------------------------------------
diff --git a/connectors/csv/pom.xml b/connectors/csv/pom.xml
index 2723b5c..899b5d1 100644
--- a/connectors/csv/pom.xml
+++ b/connectors/csv/pom.xml
@@ -34,7 +34,6 @@
     <dependency>
       <groupId>com.google.code.gson</groupId>
       <artifactId>gson</artifactId>
-      <version>2.8.0</version>
     </dependency>
 
     <dependency>

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/connectors/jdbc/pom.xml
----------------------------------------------------------------------
diff --git a/connectors/jdbc/pom.xml b/connectors/jdbc/pom.xml
index 5e7ca2b..b2f1644 100644
--- a/connectors/jdbc/pom.xml
+++ b/connectors/jdbc/pom.xml
@@ -103,7 +103,7 @@
     <dependency>
       <groupId>org.apache.derby</groupId>
       <artifactId>derby</artifactId>
-      <version>10.13.1.1</version>
+      <version>10.14.2.0</version>
       <scope>test</scope>
     </dependency>
   </dependencies>

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/connectors/kafka/pom.xml
----------------------------------------------------------------------
diff --git a/connectors/kafka/pom.xml b/connectors/kafka/pom.xml
index 51c34a0..8a2aee2 100644
--- a/connectors/kafka/pom.xml
+++ b/connectors/kafka/pom.xml
@@ -100,7 +100,7 @@
     <dependency>
       <groupId>org.scala-lang</groupId>
       <artifactId>scala-library</artifactId>
-      <version>2.10.4</version>
+      <version>2.10.7</version>
       <exclusions>
         <exclusion> <!-- not transitive -->
           <groupId>*</groupId>
@@ -122,7 +122,7 @@
     <dependency>
       <groupId>org.apache.zookeeper</groupId>
       <artifactId>zookeeper</artifactId>
-      <version>3.4.6</version>
+      <version>3.4.13</version>
       <exclusions>
         <exclusion> <!-- not transitive -->
           <groupId>*</groupId>

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/connectors/websocket-jetty/pom.xml
----------------------------------------------------------------------
diff --git a/connectors/websocket-jetty/pom.xml b/connectors/websocket-jetty/pom.xml
index eb3df35..57aa13f 100644
--- a/connectors/websocket-jetty/pom.xml
+++ b/connectors/websocket-jetty/pom.xml
@@ -43,7 +43,7 @@
     <dependency>
       <groupId>org.eclipse.jetty.websocket</groupId>
       <artifactId>javax-websocket-client-impl</artifactId>
-      <version>9.3.6.v20151106</version>
+      <version>9.4.8.v20180619</version>
     </dependency>
   </dependencies>
 

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/connectors/websocket-server/pom.xml
----------------------------------------------------------------------
diff --git a/connectors/websocket-server/pom.xml b/connectors/websocket-server/pom.xml
index 50f6e73..9cb92b4 100644
--- a/connectors/websocket-server/pom.xml
+++ b/connectors/websocket-server/pom.xml
@@ -43,7 +43,7 @@
     <dependency>
       <groupId>org.eclipse.jetty.websocket</groupId>
       <artifactId>javax-websocket-server-impl</artifactId>
-      <version>9.3.6.v20151106</version>
+      <version>9.4.8.v20180619</version>
     </dependency>
   </dependencies>
 

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/pom.xml
----------------------------------------------------------------------
diff --git a/console/pom.xml b/console/pom.xml
index 35f7c91..4237258 100644
--- a/console/pom.xml
+++ b/console/pom.xml
@@ -32,7 +32,9 @@
   <name>Apache Edgent (Java 8): Console</name>
 
   <modules>
-    <module>server</module>
+    <module>server-jetty</module>
+    <module>server-tomcat</module>
+    <module>server-undertow</module>
     <module>servlets</module>
   </modules>
 

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-jetty/pom.xml
----------------------------------------------------------------------
diff --git a/console/server-jetty/pom.xml b/console/server-jetty/pom.xml
new file mode 100644
index 0000000..4a333ea
--- /dev/null
+++ b/console/server-jetty/pom.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.edgent</groupId>
+    <artifactId>edgent-console</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>edgent-console-server-jetty</artifactId>
+
+  <name>Apache Edgent (Java 8): Console: Server (Jetty)</name>
+
+  <build>
+    <resources>
+      <resource>
+        <directory>${project.basedir}/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/</targetPath>
+      </resource>
+      <resource>
+        <!--  bundle the licenses of the artifacts we are bundling -->
+        <directory>${project.basedir}/../../src/main/appended-resources/licenses</directory>
+        <targetPath>${project.build.outputDirectory}/META-INF/licenses</targetPath>
+      </resource>
+    </resources>
+    <plugins>
+      <!--
+        Copy the servlets.war into the build output so it is embedded as
+        resource into the jar.
+      -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>copy</id>
+            <phase>generate-resources</phase>
+            <goals>
+              <goal>copy</goal>
+            </goals>
+            <configuration>
+               <!--  Bundle these dependencies in the jar.
+                    
+                  You must maintain the corresponding info if you
+                  add/remove artifacts or change their versions:
+                    - entry in src/main/resources/META-INF/LICENSE
+                    - entry in src/main/resources/META-INF/NOTICE when appropriate                    
+ 
+                  See console/servlets and its pom,LICENSE,NOTICE.
+                  
+                  You must maintain the info in this project's
+                  corresponding platforms/java7 files.
+               -->
+              <artifactItems>
+                <artifactItem>
+                  <groupId>org.apache.edgent</groupId>
+                  <artifactId>edgent-console-servlets</artifactId>
+                  <version>${project.version}</version>
+                  <type>war</type>
+                  <outputDirectory>${project.build.outputDirectory}/resources</outputDirectory>
+                  <destFileName>servlets.war</destFileName>
+                </artifactItem>
+              </artifactItems>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+	 <plugin>
+	    <groupId>org.apache.maven.plugins</groupId>
+	    <artifactId>maven-surefire-plugin</artifactId>
+	    <configuration>
+	        <!-- needed because of HttpServer impl and HttpServerPortTest -->
+	        <reuseForks>false</reuseForks>
+	    </configuration>
+	  </plugin>
+   </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-http</artifactId>
+      <version>${jetty.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-io</artifactId>
+      <version>${jetty.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-security</artifactId>
+      <version>${jetty.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-server</artifactId>
+      <version>${jetty.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlet</artifactId>
+      <version>${jetty.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-util</artifactId>
+      <version>${jetty.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-webapp</artifactId>
+      <version>${jetty.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-xml</artifactId>
+      <version>${jetty.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.7.12</version>
+    </dependency>
+
+    <!--
+        This artifact is needed by the maven-dependency-plugin
+        by marking this dependency optional, it is not included
+        in the resulting artifact, but Maven ensures it is built
+        prior to this module.
+    -->
+    <dependency>
+      <groupId>org.apache.edgent</groupId>
+      <artifactId>edgent-console-servlets</artifactId>
+      <version>1.3.0-SNAPSHOT</version>
+      <type>war</type>
+      <optional>true</optional>
+    </dependency>
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-jetty/src/main/java/org/apache/edgent/console/server/HttpServer.java
----------------------------------------------------------------------
diff --git a/console/server-jetty/src/main/java/org/apache/edgent/console/server/HttpServer.java b/console/server-jetty/src/main/java/org/apache/edgent/console/server/HttpServer.java
new file mode 100644
index 0000000..dd89093
--- /dev/null
+++ b/console/server-jetty/src/main/java/org/apache/edgent/console/server/HttpServer.java
@@ -0,0 +1,174 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.apache.edgent.console.server;
+
+import java.io.File;
+
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The "Edgent Console".
+ * <p>
+ * The Console's HTTP server starts with a random available port unless
+ * a port is specified via the {@code edgent.console.port} system property. 
+ */
+public class HttpServer {
+
+  private static final Logger logger = LoggerFactory.getLogger(HttpServer.class);
+
+	/**
+	 * The only constructor.  A private no-argument constructor.  Called only once from the static HttpServerHolder class.
+	 */
+    private HttpServer() {
+    }
+    
+    /** 
+	 * The static class that creates the singleton HttpServer object.
+	 */
+    private static class HttpServerHolder {
+        // use port 0 if system prop not set, so we know the server will always start
+        private static final Server JETTYSERVER = new Server(Integer.getInteger("edgent.console.port", 0));
+        private static final WebAppContext WEBAPP = new WebAppContext();
+        private static final HttpServer INSTANCE = new HttpServer();
+        private static boolean INITIALIZED = false;
+    }
+
+    /**
+     * Gets the jetty server associated with this class
+     * @return the org.eclipse.jetty.server.Server
+     */
+    private static Server getJettyServer() {
+        return HttpServerHolder.JETTYSERVER;
+    }
+    /**
+     * Initialization of the context path for the web application "/console" occurs in this method
+     * and the handler for the web application is set.  This only occurs once.
+     * @return HttpServer: the singleton instance of this class
+     * @throws Exception on failure
+     */
+    public static HttpServer getInstance() throws Exception {
+        if (!HttpServerHolder.INITIALIZED) {
+            logger.info("initializing");
+            HttpServerHolder.WEBAPP.setContextPath("/console");
+            ServletContextHandler contextJobs = new ServletContextHandler(ServletContextHandler.SESSIONS);
+            contextJobs.setContextPath("/jobs");
+            ServletContextHandler contextMetrics = new ServletContextHandler(ServletContextHandler.SESSIONS);
+            contextMetrics.setContextPath("/metrics");
+            ServerUtil sUtil = new ServerUtil();
+            File servletsJarFile = sUtil.getWarFilePath();
+            if (servletsJarFile.exists()){
+            	HttpServerHolder.WEBAPP.setWar(servletsJarFile.getAbsolutePath());
+            } else {
+                throw new RuntimeException("Unable to find WAR archive in " + servletsJarFile.getAbsolutePath());
+            }
+
+            HttpServerHolder.WEBAPP.addAliasCheck(new AllowSymLinkAliasChecker());
+            ContextHandlerCollection contexts = new ContextHandlerCollection();
+            contexts.setHandlers(new Handler[] { contextJobs, contextMetrics, HttpServerHolder.WEBAPP });
+            HttpServerHolder.JETTYSERVER.setHandler(contexts);
+            HttpServerHolder.INITIALIZED = true;
+        }
+        return HttpServerHolder.INSTANCE;
+    }
+
+    /**
+     * 
+     * @return the ServerConnector object for the jetty server
+     */
+    private static ServerConnector getServerConnector() {
+        return (ServerConnector) HttpServerHolder.JETTYSERVER.getConnectors()[0];
+    }
+
+    /**
+     * 
+     * @return a String containing the context path to the console web application
+     * @throws Exception on failure
+     */
+    public String getConsoleContextPath() throws Exception {
+        return HttpServerHolder.WEBAPP.getContextPath();
+    }
+
+    /**
+     * Starts the jetty web server
+     * @throws Exception on failure
+     */
+    public void startServer() throws Exception {
+        getJettyServer().start();
+    }
+
+    /**
+     * Stops the jetty web server
+     * @throws Exception
+     */
+    @SuppressWarnings("unused")
+    private static void stopServer() throws Exception {
+        getJettyServer().stop();
+    }
+
+    /**
+     * Checks to see if the jetty web server is started
+     * @return a boolean: true if the server is started, false if not
+     */
+    public boolean isServerStarted() {
+        if (getJettyServer().isStarted() || getJettyServer().isStarting() || getJettyServer().isRunning()) {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    /**
+     * Checks to see if the server is in a "stopping" or "stopped" state
+     * @return a boolean: true if the server is stopping or stopped, false otherwise
+     */
+    public boolean isServerStopped() {
+        if (getJettyServer().isStopping() || getJettyServer().isStopped()) {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+    /**
+     * Returns the port number the console is running on.  Each time the console is started a different port number may be returned.
+     * @return an int: the port number the jetty server is listening on
+     */
+    public int getConsolePortNumber() {
+        return getServerConnector().getLocalPort();
+    }
+    
+    /**
+     * Returns the url for the web application at the "console" context path.  Localhost is always assumed
+     * @return the url for the web application at the "console" context path.
+     * @throws Exception on failure
+     */
+    public String getConsoleUrl() throws Exception {
+        return new String("http://localhost" + ":" + getConsolePortNumber() + getConsoleContextPath());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-jetty/src/main/java/org/apache/edgent/console/server/ServerUtil.java
----------------------------------------------------------------------
diff --git a/console/server-jetty/src/main/java/org/apache/edgent/console/server/ServerUtil.java b/console/server-jetty/src/main/java/org/apache/edgent/console/server/ServerUtil.java
new file mode 100644
index 0000000..f8c6fde
--- /dev/null
+++ b/console/server-jetty/src/main/java/org/apache/edgent/console/server/ServerUtil.java
@@ -0,0 +1,84 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.apache.edgent.console.server;
+
+import java.io.*;
+
+public class ServerUtil {
+
+	/**
+	 *  The public constructor of this utility class for use by the HttpServer class.
+	 */
+    public ServerUtil() {
+    }
+
+    /**
+     * Returns the File object representing the "webapps" directory
+     * @return a File object or null if the "webapps" directory is not found
+     */
+    public File getWarFilePath() {
+        File war = new File("target/war-resources/servlets.war");
+        if(!war.exists()) {
+            // Eventually create the directory for serving the war.
+            if(!war.getParentFile().exists()) {
+                if(!war.getParentFile().mkdirs()) {
+                    throw new RuntimeException("Could not create output directory at " + war.getAbsolutePath());
+                }
+            }
+
+            // Dump the servlet.war into the output directory.
+            InputStream stream = null;
+            FileOutputStream fileOutputStream = null;
+            try {
+                stream = ServerUtil.class.getResourceAsStream("/resources/servlets.war");
+                if(stream == null) {
+                    throw new RuntimeException("Could not get resource '/resources/servlets.war' from classpath.");
+                }
+
+                int readBytes;
+                byte[] buffer = new byte[4096];
+                fileOutputStream = new FileOutputStream(war);
+                while ((readBytes = stream.read(buffer)) > 0) {
+                    fileOutputStream.write(buffer, 0, readBytes);
+                }
+            } catch (IOException e) {
+                throw new RuntimeException("Could not dump resource 'resources/servlets.war' from classpath.", e);
+            } finally {
+                if(stream != null) {
+                    try {
+                        stream.close();
+                    } catch (IOException e) {
+                        // Ignore.
+                    }
+                }
+                if(fileOutputStream != null) {
+                    try {
+                        fileOutputStream.close();
+                    } catch (IOException e) {
+                        // Ignore.
+                    }
+                }
+            }
+        }
+
+        return war;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-jetty/src/main/remote-resources/META-INF/LICENSE
----------------------------------------------------------------------
diff --git a/console/server-jetty/src/main/remote-resources/META-INF/LICENSE b/console/server-jetty/src/main/remote-resources/META-INF/LICENSE
new file mode 100644
index 0000000..8d0e1a0
--- /dev/null
+++ b/console/server-jetty/src/main/remote-resources/META-INF/LICENSE
@@ -0,0 +1,42 @@
+
+===============================================================================
+APACHE EDGENT SUBCOMPONENTS:
+
+This binary includes a number of subcomponents with separate
+copyright notices and license terms.  Your use of this binary
+is subject to the terms and conditions of the following licenses.
+
+===============================================================================
+License: Apache License Version 2.0
+For details, see META-INF/licenses/apache-license-version-2.0.txt
+
+gson (com.google.code.gson:gson:2.2.4)
+metrics-core (io.dropwizard.metrics:metrics-core:3.1.2)
+
+===============================================================================
+License: MIT
+
+jquery (org.webjars:jquery:1.11.2)
+    For details, see META-INF/licenses/jquery-1_11_2-MIT.txt.
+
+jquery-ui (org.webjars:jquery-ui:1.11.4)
+    For details, see META-INF/licenses/jquery-ui-1_11_4-MIT.txt.
+
+d3.legend.js (included inside the resources/servlets.war!/js/ext/d3.legend/d3.legend.js)
+    https://gist.githubusercontent.com/ZJONSSON/3918369/raw/bf9bce6b68a3b70f87450f155436ca4a84af1ba4/d3.legend.js
+    With Edgent specific modifications.
+    For details, see META-INF/licenses/d3.legend-MIT.txt.
+
+===============================================================================
+License: BSD 3-Clause
+
+d3 (org.webjars.bower:d3:3.3.9)
+    For details, see META-INF/licenses/d3-3_3_9-BSD.txt.
+
+===============================================================================
+License: BSD 2-Clause
+
+d3-plugins-sankey (org.webjars.bower:d3-plugins-sankey:1.1.0)
+    For details, see META-INF/licenses/d3-plugins-sankey-1_1_0-BSD.txt.
+
+===============================================================================

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-jetty/src/main/remote-resources/META-INF/NOTICE
----------------------------------------------------------------------
diff --git a/console/server-jetty/src/main/remote-resources/META-INF/NOTICE b/console/server-jetty/src/main/remote-resources/META-INF/NOTICE
new file mode 100644
index 0000000..9c9e7e9
--- /dev/null
+++ b/console/server-jetty/src/main/remote-resources/META-INF/NOTICE
@@ -0,0 +1,28 @@
+===============================================================================
+
+Portions of this software were developed by IBM Corp.
+Copyright IBM Corp. 2015, 2016
+
+===============================================================================
+
+APACHE EDGENT SUBCOMPONENTS:
+
+This product includes a number of subcomponents with separate
+copyright notices and license terms.  The following notices apply.
+
+-------------------------------------------------------------------------------
+metrics-core (io.dropwizard.metrics:metrics-core:3.1.2)
+
+Metrics
+Copyright 2010-2013 Coda Hale and Yammer, Inc.
+
+This product includes software developed by Coda Hale and Yammer, Inc.
+
+This product includes code derived from the JSR-166 project (ThreadLocalRandom,
+Striped64, LongAdder) with the following comments:
+
+          Written by Doug Lea with assistance from members of JCP JSR-166
+          Expert Group and released to the public domain, as explained at
+          http://creativecommons.org/publicdomain/zero/1.0/
+
+===============================================================================

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-jetty/src/test/java/org/apache/edgent/test/console/server/HttpServerPortTest.java
----------------------------------------------------------------------
diff --git a/console/server-jetty/src/test/java/org/apache/edgent/test/console/server/HttpServerPortTest.java b/console/server-jetty/src/test/java/org/apache/edgent/test/console/server/HttpServerPortTest.java
new file mode 100644
index 0000000..64e8b22
--- /dev/null
+++ b/console/server-jetty/src/test/java/org/apache/edgent/test/console/server/HttpServerPortTest.java
@@ -0,0 +1,58 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.apache.edgent.test.console.server;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import org.apache.edgent.console.server.HttpServer;
+import org.junit.Test;
+
+/**
+ * This is a separate test because the HttpServer implementation
+ * precludes changing the console port within a jvm instance.
+ */
+public class HttpServerPortTest {
+	
+	  int getAvailablePort() throws IOException {
+	    try (Socket s = new Socket()) {
+	      s.bind(null);
+	      return s.getLocalPort();
+	    }
+	  }
+
+    @Test
+    public void testOverridePortNumber() throws Exception {
+      // count on the OS not immediately reusing an available local port.
+    	int port = getAvailablePort();
+      int port2 = getAvailablePort();
+      assumeTrue(port != port2);
+      
+      System.setProperty("edgent.console.port", String.valueOf(port));
+      HttpServer myHttpServer = HttpServer.getInstance();
+      myHttpServer.startServer();
+      
+      int portNum = myHttpServer.getConsolePortNumber();
+      assertEquals(port, portNum);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-jetty/src/test/java/org/apache/edgent/test/console/server/HttpServerTest.java
----------------------------------------------------------------------
diff --git a/console/server-jetty/src/test/java/org/apache/edgent/test/console/server/HttpServerTest.java b/console/server-jetty/src/test/java/org/apache/edgent/test/console/server/HttpServerTest.java
new file mode 100644
index 0000000..46bd511
--- /dev/null
+++ b/console/server-jetty/src/test/java/org/apache/edgent/test/console/server/HttpServerTest.java
@@ -0,0 +1,172 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.apache.edgent.test.console.server;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.edgent.console.server.HttpServer;
+import org.junit.Test;
+
+public class HttpServerTest {
+	
+	public static final String consoleWarNotFoundMessage =  
+			"console.war not found.  Run 'ant' from the top level edgent directory, or 'ant' from 'console/servlets' to create console.war under the webapps directory.";
+
+
+    @Test
+    public void testGetInstance()  {
+    	HttpServer myHttpServer = null;
+    	boolean warNotFoundExceptionThrown = false;
+        try {
+        	myHttpServer = HttpServer.getInstance();
+        } catch (Exception warNotFoundException) {
+        	System.out.println(warNotFoundException.getMessage());
+        	if (warNotFoundException.getMessage().equals(consoleWarNotFoundMessage)) {
+        		warNotFoundExceptionThrown = true;
+        		assertEquals("", "");
+        	}
+        } finally {
+        	if (!warNotFoundExceptionThrown) {
+        		assertNotNull("HttpServer getInstance is null", myHttpServer);
+        	} else {
+        		assertNull("HttpServer getInstance is null because console.war could not be found", myHttpServer);
+        	}
+        }
+    }
+
+    @Test
+    public void startServer() throws Exception {
+    	HttpServer myHttpServer = null;
+    	boolean warNotFoundExceptionThrown = false;
+        try {
+        	myHttpServer = HttpServer.getInstance();
+        } catch (Exception warNotFoundException) {
+        	System.out.println(warNotFoundException.getMessage());
+        	if (warNotFoundException.getMessage().equals(consoleWarNotFoundMessage)) {
+        		warNotFoundExceptionThrown = true;
+        		assertEquals("", "");
+        	}
+        } finally {
+        	if ((!warNotFoundExceptionThrown) && (myHttpServer != null)){
+                myHttpServer.startServer();
+                assertTrue(myHttpServer.isServerStarted());
+        	} else {
+        		assertNull("HttpServer getInstance is null because console.war could not be found", myHttpServer);
+        	}
+        }
+    }
+
+    @Test
+    public void isServerStopped() throws Exception {
+    	HttpServer myHttpServer = null;
+    	boolean warNotFoundExceptionThrown = false;
+        try {
+        	myHttpServer = HttpServer.getInstance();
+        } catch (Exception warNotFoundException) {
+        	System.out.println(warNotFoundException.getMessage());
+        	if (warNotFoundException.getMessage().equals(consoleWarNotFoundMessage)) {
+        		warNotFoundExceptionThrown = true;
+        		assertEquals("", "");
+        	}
+        } finally {
+			if ((!warNotFoundExceptionThrown) && (myHttpServer != null)){
+                myHttpServer.startServer();
+                assertFalse(myHttpServer.isServerStopped());
+        	} else {
+        		assertNull("HttpServer getInstance is null because console.war could not be found", myHttpServer);
+        	}
+        }
+    }
+
+    @Test
+    public void getConsolePath() throws Exception {
+    	HttpServer myHttpServer = null;
+    	boolean warNotFoundExceptionThrown = false;
+        try {
+        	myHttpServer = HttpServer.getInstance();
+        } catch (Exception warNotFoundException) {
+        	System.out.println(warNotFoundException.getMessage());
+        	if (warNotFoundException.getMessage().equals(consoleWarNotFoundMessage)) {
+        		warNotFoundExceptionThrown = true;
+        		assertEquals("", "");
+        	}
+        } finally {
+			if ((!warNotFoundExceptionThrown) && (myHttpServer != null)){
+        		assertEquals("/console", myHttpServer.getConsoleContextPath());
+        	} else {
+        		assertNull("HttpServer getInstance is null because console.war could not be found", myHttpServer);
+        	}
+        }
+        
+    }
+
+    @Test
+    public void getConsoleUrl() throws Exception {
+    	
+    	HttpServer myHttpServer = null;
+    	boolean warNotFoundExceptionThrown = false;
+        try {
+        	myHttpServer = HttpServer.getInstance();
+        } catch (Exception warNotFoundException) {
+        	System.out.println(warNotFoundException.getMessage());
+        	if (warNotFoundException.getMessage().equals(consoleWarNotFoundMessage)) {
+        		warNotFoundExceptionThrown = true;
+        		assertEquals("", "");
+        	}
+        } finally {
+			if ((!warNotFoundExceptionThrown) && (myHttpServer != null)){
+                myHttpServer.startServer();
+                int portNum = myHttpServer.getConsolePortNumber();
+                String context = myHttpServer.getConsoleContextPath();
+                assertEquals("http://localhost:" + portNum + context, myHttpServer.getConsoleUrl());
+        	} else {
+        		assertNull("HttpServer getInstance is null because console.war could not be found", myHttpServer);
+        	}
+        }
+    }
+
+    @Test
+    public void getConsolePortNumber() throws Exception {
+    	HttpServer myHttpServer = null;
+    	boolean warNotFoundExceptionThrown = false;
+        try {
+        	myHttpServer = HttpServer.getInstance();
+        } catch (Exception warNotFoundException) {
+        	System.out.println(warNotFoundException.getMessage());
+        	if (warNotFoundException.getMessage().equals(consoleWarNotFoundMessage)) {
+        		warNotFoundExceptionThrown = true;
+        		assertEquals("", "");
+        	}
+        } finally {
+			if ((!warNotFoundExceptionThrown) && (myHttpServer != null)){
+                myHttpServer.startServer();
+                int portNum = myHttpServer.getConsolePortNumber();
+                assertTrue("the port number is not in integer range: " + Integer.toString(portNum),
+                        (Integer.MAX_VALUE > portNum) && (0 < portNum));
+        	} else {
+        		assertNull("HttpServer getInstance is null because console.war could not be found", myHttpServer);
+        	}
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-jetty/src/test/java/org/apache/edgent/test/console/server/ServerUtilTest.java
----------------------------------------------------------------------
diff --git a/console/server-jetty/src/test/java/org/apache/edgent/test/console/server/ServerUtilTest.java b/console/server-jetty/src/test/java/org/apache/edgent/test/console/server/ServerUtilTest.java
new file mode 100644
index 0000000..cea0ac4
--- /dev/null
+++ b/console/server-jetty/src/test/java/org/apache/edgent/test/console/server/ServerUtilTest.java
@@ -0,0 +1,43 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.apache.edgent.test.console.server;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+
+import org.apache.edgent.console.server.ServerUtil;
+import org.junit.Test;
+
+
+public class ServerUtilTest {
+
+    @Test
+    public void testServerUtil() {
+        ServerUtil serverUtil = new ServerUtil();
+        assertNotNull("ServerUtil is null", serverUtil);
+    }
+
+    @Test
+    public void testGetPath() throws IOException {
+        ServerUtil serverUtil = new ServerUtil();
+        assertNotNull("Get AbsoluteWarFilePath is null", serverUtil.getWarFilePath());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-tomcat/pom.xml
----------------------------------------------------------------------
diff --git a/console/server-tomcat/pom.xml b/console/server-tomcat/pom.xml
new file mode 100644
index 0000000..92d6c8a
--- /dev/null
+++ b/console/server-tomcat/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.edgent</groupId>
+    <artifactId>edgent-console</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>edgent-console-server-tomcat</artifactId>
+
+  <name>Apache Edgent (Java 8): Console: Server (Tomcat)</name>
+
+  <build>
+    <resources>
+      <resource>
+        <directory>${project.basedir}/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/</targetPath>
+      </resource>
+      <resource>
+        <!--  bundle the licenses of the artifacts we are bundling -->
+        <directory>${project.basedir}/../../src/main/appended-resources/licenses</directory>
+        <targetPath>${project.build.outputDirectory}/META-INF/licenses</targetPath>
+      </resource>
+    </resources>
+    <plugins>
+	    <plugin>
+	      <groupId>org.apache.maven.plugins</groupId>
+	      <artifactId>maven-surefire-plugin</artifactId>
+	      <configuration>
+          <!-- needed because of HttpServer impl and HttpServerPortTest -->
+          <reuseForks>false</reuseForks>
+          <!-- Make tomcat start within the target directory -->
+          <workingDirectory>${project.build.directory}</workingDirectory>
+          <basedir>${project.build.directory}</basedir>
+	      </configuration>
+	    </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <version>3.1.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.tomcat.embed</groupId>
+      <artifactId>tomcat-embed-core</artifactId>
+      <version>${tomcat.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.7.25</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.edgent</groupId>
+      <artifactId>edgent-console-servlets</artifactId>
+      <version>1.3.0-SNAPSHOT</version>
+      <classifier>classes</classifier>
+    </dependency>
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-tomcat/src/main/java/org/apache/edgent/console/server/HttpServer.java
----------------------------------------------------------------------
diff --git a/console/server-tomcat/src/main/java/org/apache/edgent/console/server/HttpServer.java b/console/server-tomcat/src/main/java/org/apache/edgent/console/server/HttpServer.java
new file mode 100644
index 0000000..212cc69
--- /dev/null
+++ b/console/server-tomcat/src/main/java/org/apache/edgent/console/server/HttpServer.java
@@ -0,0 +1,169 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.apache.edgent.console.server;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.edgent.console.servlets.ConsoleJobServlet;
+import org.apache.edgent.console.servlets.ConsoleMetricsServlet;
+import org.apache.edgent.console.servlets.ConsoleServlet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+/**
+ * The "Edgent Console".
+ * <p>
+ * The Console's HTTP server starts with a random available port unless
+ * a port is specified via the {@code edgent.console.port} system property. 
+ */
+public class HttpServer {
+
+  private static final Logger logger = LoggerFactory.getLogger(HttpServer.class);
+
+	/**
+	 * The only constructor.  A private no-argument constructor.  Called only once from the static HttpServerHolder class.
+	 */
+    private HttpServer() {
+    }
+    
+    /** 
+	 * The static class that creates the singleton HttpServer object.
+	 */
+    private static class HttpServerHolder {
+        // use port 0 if system prop not set, so we know the server will always start
+        private static final Tomcat TOMCATSERVER;
+        static {
+            TOMCATSERVER = new Tomcat();
+            TOMCATSERVER.setPort(Integer.getInteger("edgent.console.port", 0));
+            // Trigger the creation of the default connector
+            TOMCATSERVER.getConnector();
+        }
+
+        private static Context CONSOLE_CONTEXT;
+        private static final HttpServer INSTANCE = new HttpServer();
+        private static boolean INITIALIZED = false;
+    }
+
+    /**
+     * Gets the tomcat server associated with this class
+     * @return the org.apache.catalina.startup.Tomcat
+     */
+    private static Tomcat getTomcatServer() {
+        return HttpServerHolder.TOMCATSERVER;
+    }
+
+    /**
+     * Initialization of the context path for the web application "/console" occurs in this method
+     * and the handler for the web application is set.  This only occurs once.
+     * @return HttpServer: the singleton instance of this class
+     */
+    public static HttpServer getInstance() {
+        if (!HttpServerHolder.INITIALIZED) {
+            logger.info("initializing");
+
+            Tomcat tomcat = HttpServerHolder.TOMCATSERVER;
+
+            // Initialize the console context
+            Context context = tomcat.addContext("/console", new File(".").getAbsolutePath());
+            HttpServerHolder.CONSOLE_CONTEXT = context;
+
+            // Initialize the console servlet.
+            Class consoleServletClass = ConsoleServlet.class;
+            String consoleServletName = consoleServletClass.getSimpleName();
+            Tomcat.addServlet(context, consoleServletName, consoleServletClass.getName());
+            context.addServletMappingDecoded("/console/*", consoleServletName);
+
+            // Initialize the jobs servlet.
+            Class jobsServletClass = ConsoleJobServlet.class;
+            String jobsServletName = jobsServletClass.getSimpleName();
+            Tomcat.addServlet(context, jobsServletName, jobsServletClass.getName());
+            context.addServletMappingDecoded("/console/jobs/*", jobsServletName);
+
+            // Initialize the metrics servlet.
+            Class metricsServletClass = ConsoleMetricsServlet.class;
+            String metricsServletName = metricsServletClass.getSimpleName();
+            Tomcat.addServlet(context, metricsServletName, metricsServletClass.getName());
+            context.addServletMappingDecoded("/console/metrics/*", metricsServletName);
+
+            HttpServerHolder.INITIALIZED = true;
+        }
+        return HttpServerHolder.INSTANCE;
+    }
+
+    /**
+     * 
+     * @return a String containing the context path to the console web application
+     */
+    public String getConsoleContextPath() {
+        return HttpServerHolder.CONSOLE_CONTEXT.getPath();
+    }
+
+    /**
+     * Starts the tomcat web server
+     * @throws Exception on failure
+     */
+    public void startServer() throws Exception {
+        getTomcatServer().start();
+    }
+
+    /**
+     * Stops the tomcat web server
+     * @throws Exception on failure
+     */
+    @SuppressWarnings("unused")
+    private static void stopServer() throws Exception {
+        getTomcatServer().stop();
+    }
+
+    /**
+     * Checks to see if the tomcat web server is started
+     * @return a boolean: true if the server is started, false if not
+     */
+    public boolean isServerStarted() {
+        return getTomcatServer().getConnector().getState().isAvailable();
+    }
+
+    /**
+     * Checks to see if the server is in a "stopping" or "stopped" state
+     * @return a boolean: true if the server is stopping or stopped, false otherwise
+     */
+    public boolean isServerStopped() {
+        return !getTomcatServer().getConnector().getState().isAvailable();
+    }
+
+    /**
+     * Returns the port number the console is running on.  Each time the console is started a different port number may be returned.
+     * @return an int: the port number the jetty server is listening on
+     */
+    public int getConsolePortNumber() {
+        return getTomcatServer().getConnector().getLocalPort();
+    }
+    
+    /**
+     * Returns the url for the web application at the "console" context path.  Localhost is always assumed
+     * @return the url for the web application at the "console" context path.
+     */
+    public String getConsoleUrl() {
+        return "http://localhost" + ":" + getConsolePortNumber() + getConsoleContextPath();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-tomcat/src/main/remote-resources/META-INF/LICENSE
----------------------------------------------------------------------
diff --git a/console/server-tomcat/src/main/remote-resources/META-INF/LICENSE b/console/server-tomcat/src/main/remote-resources/META-INF/LICENSE
new file mode 100644
index 0000000..8d0e1a0
--- /dev/null
+++ b/console/server-tomcat/src/main/remote-resources/META-INF/LICENSE
@@ -0,0 +1,42 @@
+
+===============================================================================
+APACHE EDGENT SUBCOMPONENTS:
+
+This binary includes a number of subcomponents with separate
+copyright notices and license terms.  Your use of this binary
+is subject to the terms and conditions of the following licenses.
+
+===============================================================================
+License: Apache License Version 2.0
+For details, see META-INF/licenses/apache-license-version-2.0.txt
+
+gson (com.google.code.gson:gson:2.2.4)
+metrics-core (io.dropwizard.metrics:metrics-core:3.1.2)
+
+===============================================================================
+License: MIT
+
+jquery (org.webjars:jquery:1.11.2)
+    For details, see META-INF/licenses/jquery-1_11_2-MIT.txt.
+
+jquery-ui (org.webjars:jquery-ui:1.11.4)
+    For details, see META-INF/licenses/jquery-ui-1_11_4-MIT.txt.
+
+d3.legend.js (included inside the resources/servlets.war!/js/ext/d3.legend/d3.legend.js)
+    https://gist.githubusercontent.com/ZJONSSON/3918369/raw/bf9bce6b68a3b70f87450f155436ca4a84af1ba4/d3.legend.js
+    With Edgent specific modifications.
+    For details, see META-INF/licenses/d3.legend-MIT.txt.
+
+===============================================================================
+License: BSD 3-Clause
+
+d3 (org.webjars.bower:d3:3.3.9)
+    For details, see META-INF/licenses/d3-3_3_9-BSD.txt.
+
+===============================================================================
+License: BSD 2-Clause
+
+d3-plugins-sankey (org.webjars.bower:d3-plugins-sankey:1.1.0)
+    For details, see META-INF/licenses/d3-plugins-sankey-1_1_0-BSD.txt.
+
+===============================================================================

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-tomcat/src/main/remote-resources/META-INF/NOTICE
----------------------------------------------------------------------
diff --git a/console/server-tomcat/src/main/remote-resources/META-INF/NOTICE b/console/server-tomcat/src/main/remote-resources/META-INF/NOTICE
new file mode 100644
index 0000000..9c9e7e9
--- /dev/null
+++ b/console/server-tomcat/src/main/remote-resources/META-INF/NOTICE
@@ -0,0 +1,28 @@
+===============================================================================
+
+Portions of this software were developed by IBM Corp.
+Copyright IBM Corp. 2015, 2016
+
+===============================================================================
+
+APACHE EDGENT SUBCOMPONENTS:
+
+This product includes a number of subcomponents with separate
+copyright notices and license terms.  The following notices apply.
+
+-------------------------------------------------------------------------------
+metrics-core (io.dropwizard.metrics:metrics-core:3.1.2)
+
+Metrics
+Copyright 2010-2013 Coda Hale and Yammer, Inc.
+
+This product includes software developed by Coda Hale and Yammer, Inc.
+
+This product includes code derived from the JSR-166 project (ThreadLocalRandom,
+Striped64, LongAdder) with the following comments:
+
+          Written by Doug Lea with assistance from members of JCP JSR-166
+          Expert Group and released to the public domain, as explained at
+          http://creativecommons.org/publicdomain/zero/1.0/
+
+===============================================================================

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-tomcat/src/test/java/org/apache/edgent/test/console/server/HttpServerPortTest.java
----------------------------------------------------------------------
diff --git a/console/server-tomcat/src/test/java/org/apache/edgent/test/console/server/HttpServerPortTest.java b/console/server-tomcat/src/test/java/org/apache/edgent/test/console/server/HttpServerPortTest.java
new file mode 100644
index 0000000..64e8b22
--- /dev/null
+++ b/console/server-tomcat/src/test/java/org/apache/edgent/test/console/server/HttpServerPortTest.java
@@ -0,0 +1,58 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.apache.edgent.test.console.server;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import org.apache.edgent.console.server.HttpServer;
+import org.junit.Test;
+
+/**
+ * This is a separate test because the HttpServer implementation
+ * precludes changing the console port within a jvm instance.
+ */
+public class HttpServerPortTest {
+	
+	  int getAvailablePort() throws IOException {
+	    try (Socket s = new Socket()) {
+	      s.bind(null);
+	      return s.getLocalPort();
+	    }
+	  }
+
+    @Test
+    public void testOverridePortNumber() throws Exception {
+      // count on the OS not immediately reusing an available local port.
+    	int port = getAvailablePort();
+      int port2 = getAvailablePort();
+      assumeTrue(port != port2);
+      
+      System.setProperty("edgent.console.port", String.valueOf(port));
+      HttpServer myHttpServer = HttpServer.getInstance();
+      myHttpServer.startServer();
+      
+      int portNum = myHttpServer.getConsolePortNumber();
+      assertEquals(port, portNum);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-tomcat/src/test/java/org/apache/edgent/test/console/server/HttpServerTest.java
----------------------------------------------------------------------
diff --git a/console/server-tomcat/src/test/java/org/apache/edgent/test/console/server/HttpServerTest.java b/console/server-tomcat/src/test/java/org/apache/edgent/test/console/server/HttpServerTest.java
new file mode 100644
index 0000000..46bd511
--- /dev/null
+++ b/console/server-tomcat/src/test/java/org/apache/edgent/test/console/server/HttpServerTest.java
@@ -0,0 +1,172 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.apache.edgent.test.console.server;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.edgent.console.server.HttpServer;
+import org.junit.Test;
+
+public class HttpServerTest {
+	
+	public static final String consoleWarNotFoundMessage =  
+			"console.war not found.  Run 'ant' from the top level edgent directory, or 'ant' from 'console/servlets' to create console.war under the webapps directory.";
+
+
+    @Test
+    public void testGetInstance()  {
+    	HttpServer myHttpServer = null;
+    	boolean warNotFoundExceptionThrown = false;
+        try {
+        	myHttpServer = HttpServer.getInstance();
+        } catch (Exception warNotFoundException) {
+        	System.out.println(warNotFoundException.getMessage());
+        	if (warNotFoundException.getMessage().equals(consoleWarNotFoundMessage)) {
+        		warNotFoundExceptionThrown = true;
+        		assertEquals("", "");
+        	}
+        } finally {
+        	if (!warNotFoundExceptionThrown) {
+        		assertNotNull("HttpServer getInstance is null", myHttpServer);
+        	} else {
+        		assertNull("HttpServer getInstance is null because console.war could not be found", myHttpServer);
+        	}
+        }
+    }
+
+    @Test
+    public void startServer() throws Exception {
+    	HttpServer myHttpServer = null;
+    	boolean warNotFoundExceptionThrown = false;
+        try {
+        	myHttpServer = HttpServer.getInstance();
+        } catch (Exception warNotFoundException) {
+        	System.out.println(warNotFoundException.getMessage());
+        	if (warNotFoundException.getMessage().equals(consoleWarNotFoundMessage)) {
+        		warNotFoundExceptionThrown = true;
+        		assertEquals("", "");
+        	}
+        } finally {
+        	if ((!warNotFoundExceptionThrown) && (myHttpServer != null)){
+                myHttpServer.startServer();
+                assertTrue(myHttpServer.isServerStarted());
+        	} else {
+        		assertNull("HttpServer getInstance is null because console.war could not be found", myHttpServer);
+        	}
+        }
+    }
+
+    @Test
+    public void isServerStopped() throws Exception {
+    	HttpServer myHttpServer = null;
+    	boolean warNotFoundExceptionThrown = false;
+        try {
+        	myHttpServer = HttpServer.getInstance();
+        } catch (Exception warNotFoundException) {
+        	System.out.println(warNotFoundException.getMessage());
+        	if (warNotFoundException.getMessage().equals(consoleWarNotFoundMessage)) {
+        		warNotFoundExceptionThrown = true;
+        		assertEquals("", "");
+        	}
+        } finally {
+			if ((!warNotFoundExceptionThrown) && (myHttpServer != null)){
+                myHttpServer.startServer();
+                assertFalse(myHttpServer.isServerStopped());
+        	} else {
+        		assertNull("HttpServer getInstance is null because console.war could not be found", myHttpServer);
+        	}
+        }
+    }
+
+    @Test
+    public void getConsolePath() throws Exception {
+    	HttpServer myHttpServer = null;
+    	boolean warNotFoundExceptionThrown = false;
+        try {
+        	myHttpServer = HttpServer.getInstance();
+        } catch (Exception warNotFoundException) {
+        	System.out.println(warNotFoundException.getMessage());
+        	if (warNotFoundException.getMessage().equals(consoleWarNotFoundMessage)) {
+        		warNotFoundExceptionThrown = true;
+        		assertEquals("", "");
+        	}
+        } finally {
+			if ((!warNotFoundExceptionThrown) && (myHttpServer != null)){
+        		assertEquals("/console", myHttpServer.getConsoleContextPath());
+        	} else {
+        		assertNull("HttpServer getInstance is null because console.war could not be found", myHttpServer);
+        	}
+        }
+        
+    }
+
+    @Test
+    public void getConsoleUrl() throws Exception {
+    	
+    	HttpServer myHttpServer = null;
+    	boolean warNotFoundExceptionThrown = false;
+        try {
+        	myHttpServer = HttpServer.getInstance();
+        } catch (Exception warNotFoundException) {
+        	System.out.println(warNotFoundException.getMessage());
+        	if (warNotFoundException.getMessage().equals(consoleWarNotFoundMessage)) {
+        		warNotFoundExceptionThrown = true;
+        		assertEquals("", "");
+        	}
+        } finally {
+			if ((!warNotFoundExceptionThrown) && (myHttpServer != null)){
+                myHttpServer.startServer();
+                int portNum = myHttpServer.getConsolePortNumber();
+                String context = myHttpServer.getConsoleContextPath();
+                assertEquals("http://localhost:" + portNum + context, myHttpServer.getConsoleUrl());
+        	} else {
+        		assertNull("HttpServer getInstance is null because console.war could not be found", myHttpServer);
+        	}
+        }
+    }
+
+    @Test
+    public void getConsolePortNumber() throws Exception {
+    	HttpServer myHttpServer = null;
+    	boolean warNotFoundExceptionThrown = false;
+        try {
+        	myHttpServer = HttpServer.getInstance();
+        } catch (Exception warNotFoundException) {
+        	System.out.println(warNotFoundException.getMessage());
+        	if (warNotFoundException.getMessage().equals(consoleWarNotFoundMessage)) {
+        		warNotFoundExceptionThrown = true;
+        		assertEquals("", "");
+        	}
+        } finally {
+			if ((!warNotFoundExceptionThrown) && (myHttpServer != null)){
+                myHttpServer.startServer();
+                int portNum = myHttpServer.getConsolePortNumber();
+                assertTrue("the port number is not in integer range: " + Integer.toString(portNum),
+                        (Integer.MAX_VALUE > portNum) && (0 < portNum));
+        	} else {
+        		assertNull("HttpServer getInstance is null because console.war could not be found", myHttpServer);
+        	}
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-undertow/pom.xml
----------------------------------------------------------------------
diff --git a/console/server-undertow/pom.xml b/console/server-undertow/pom.xml
new file mode 100644
index 0000000..e921136
--- /dev/null
+++ b/console/server-undertow/pom.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.edgent</groupId>
+    <artifactId>edgent-console</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>edgent-console-server-undertow</artifactId>
+
+  <name>Apache Edgent (Java 8): Console: Server (Undertow)</name>
+
+  <properties>
+    <undertow.version>2.0.11.Final</undertow.version>
+  </properties>
+
+  <build>
+    <resources>
+      <resource>
+        <directory>${project.basedir}/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/</targetPath>
+      </resource>
+      <resource>
+        <!--  bundle the licenses of the artifacts we are bundling -->
+        <directory>${project.basedir}/../../src/main/appended-resources/licenses</directory>
+        <targetPath>${project.build.outputDirectory}/META-INF/licenses</targetPath>
+      </resource>
+    </resources>
+    <plugins>
+	    <plugin>
+	      <groupId>org.apache.maven.plugins</groupId>
+	      <artifactId>maven-surefire-plugin</artifactId>
+	      <configuration>
+          <!-- needed because of HttpServer impl and HttpServerPortTest -->
+          <reuseForks>false</reuseForks>
+          <!-- Make tomcat start within the target directory -->
+          <workingDirectory>${project.build.directory}</workingDirectory>
+          <basedir>${project.build.directory}</basedir>
+	      </configuration>
+	    </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>io.undertow</groupId>
+      <artifactId>undertow-servlet</artifactId>
+      <version>${undertow.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.7.25</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.edgent</groupId>
+      <artifactId>edgent-console-servlets</artifactId>
+      <version>1.3.0-SNAPSHOT</version>
+      <classifier>classes</classifier>
+    </dependency>
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-undertow/src/main/java/org/apache/edgent/console/server/HttpServer.java
----------------------------------------------------------------------
diff --git a/console/server-undertow/src/main/java/org/apache/edgent/console/server/HttpServer.java b/console/server-undertow/src/main/java/org/apache/edgent/console/server/HttpServer.java
new file mode 100644
index 0000000..801f15b
--- /dev/null
+++ b/console/server-undertow/src/main/java/org/apache/edgent/console/server/HttpServer.java
@@ -0,0 +1,189 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.apache.edgent.console.server;
+
+import io.undertow.Handlers;
+import io.undertow.Undertow;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.handlers.PathHandler;
+import io.undertow.servlet.Servlets;
+import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.servlet.api.DeploymentManager;
+import org.apache.edgent.console.servlets.ConsoleJobServlet;
+import org.apache.edgent.console.servlets.ConsoleMetricsServlet;
+import org.apache.edgent.console.servlets.ConsoleServlet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetSocketAddress;
+import java.util.List;
+
+/**
+ * The "Edgent Console".
+ * <p>
+ * The Console's HTTP server starts with a random available port unless
+ * a port is specified via the {@code edgent.console.port} system property. 
+ */
+public class HttpServer {
+
+  private static final Logger logger = LoggerFactory.getLogger(HttpServer.class);
+
+	/**
+	 * The only constructor.  A private no-argument constructor.  Called only once from the static HttpServerHolder class.
+	 */
+    private HttpServer() {
+    }
+    
+    /** 
+	 * The static class that creates the singleton HttpServer object.
+	 */
+    private static class HttpServerHolder {
+        private static String context = "/console";
+        private static int port = Integer.getInteger("edgent.console.port", 0);
+        // use port 0 if system prop not set, so we know the server will always start
+        private static Undertow SERVER;
+
+        private static final HttpServer INSTANCE = new HttpServer();
+        private static boolean INITIALIZED = false;
+    }
+
+    /**
+     * Gets the undertow server associated with this class
+     * @return the io.undertow.Undertow
+     */
+    private static Undertow getUndertowServer() {
+        return HttpServerHolder.SERVER;
+    }
+
+    /**
+     * Initialization of the context path for the web application "/console" occurs in this method
+     * and the handler for the web application is set.  This only occurs once.
+     * @return HttpServer: the singleton instance of this class
+     */
+    public static HttpServer getInstance() throws Exception {
+        if (!HttpServerHolder.INITIALIZED) {
+            logger.info("initializing");
+
+            DeploymentInfo servletBuilder = Servlets.deployment()
+                .setClassLoader(HttpServer.class.getClassLoader())
+                .setContextPath("/console")
+                .setDeploymentName("console.war")
+                .addServlets(
+                    Servlets.servlet(ConsoleServlet.class.getSimpleName(), ConsoleServlet.class)
+                        .addMapping("/console/*"),
+                    Servlets.servlet(ConsoleJobServlet.class.getSimpleName(), ConsoleJobServlet.class)
+                        .addMapping("/console/jobs/*"),
+                    Servlets.servlet(ConsoleMetricsServlet.class.getSimpleName(), ConsoleMetricsServlet.class)
+                        .addMapping("/console/metrics/*")
+                );
+
+            DeploymentManager manager = Servlets.defaultContainer().addDeployment(servletBuilder);
+            manager.deploy();
+
+            HttpHandler servletHandler = manager.start();
+            PathHandler path = Handlers.path(Handlers.redirect(HttpServerHolder.context))
+                .addPrefixPath(HttpServerHolder.context, servletHandler);
+
+            HttpServerHolder.SERVER = Undertow.builder()
+                .addHttpListener(HttpServerHolder.port, "localhost")
+                .setHandler(path)
+                .build();
+
+            HttpServerHolder.INITIALIZED = true;
+        }
+        return HttpServerHolder.INSTANCE;
+    }
+
+    /**
+     * 
+     * @return a String containing the context path to the console web application
+     */
+    public String getConsoleContextPath() {
+        return HttpServerHolder.context;
+    }
+
+    /**
+     * Starts the tomcat web server
+     */
+    public void startServer() {
+        if(!isServerStarted()) {
+            getUndertowServer().start();
+        }
+    }
+
+    /**
+     * Stops the tomcat web server
+     * @throws Exception on failure
+     */
+    @SuppressWarnings("unused")
+    private static void stopServer() {
+        getUndertowServer().stop();
+    }
+
+    /**
+     * Checks to see if the tomcat web server is started
+     * @return a boolean: true if the server is started, false if not
+     */
+    public boolean isServerStarted() {
+        try {
+            HttpServerHolder.SERVER.getListenerInfo();
+            return true;
+        } catch(IllegalStateException e) {
+            if ("UT000138: Server not started".equals(e.getMessage())) {
+                return false;
+            } else {
+                throw e;
+            }
+        }
+    }
+
+    /**
+     * Checks to see if the server is in a "stopping" or "stopped" state
+     * @return a boolean: true if the server is stopping or stopped, false otherwise
+     */
+    public boolean isServerStopped() {
+        return !isServerStarted();
+    }
+
+    /**
+     * Returns the port number the console is running on.  Each time the console is started a different port number may be returned.
+     * @return an int: the port number the jetty server is listening on
+     */
+    public int getConsolePortNumber() {
+        List<Undertow.ListenerInfo> listenerInfos = HttpServerHolder.SERVER.getListenerInfo();
+        if(!listenerInfos.isEmpty()) {
+            Undertow.ListenerInfo listenerInfo = listenerInfos.iterator().next();
+            if(listenerInfo.getAddress() instanceof InetSocketAddress) {
+                InetSocketAddress address = (InetSocketAddress) listenerInfo.getAddress();
+                return address.getPort();
+            }
+        }
+        return HttpServerHolder.port;
+    }
+    
+    /**
+     * Returns the url for the web application at the "console" context path.  Localhost is always assumed
+     * @return the url for the web application at the "console" context path.
+     */
+    public String getConsoleUrl() {
+        return "http://localhost" + ":" + getConsolePortNumber() + getConsoleContextPath();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-undertow/src/main/remote-resources/META-INF/LICENSE
----------------------------------------------------------------------
diff --git a/console/server-undertow/src/main/remote-resources/META-INF/LICENSE b/console/server-undertow/src/main/remote-resources/META-INF/LICENSE
new file mode 100644
index 0000000..8d0e1a0
--- /dev/null
+++ b/console/server-undertow/src/main/remote-resources/META-INF/LICENSE
@@ -0,0 +1,42 @@
+
+===============================================================================
+APACHE EDGENT SUBCOMPONENTS:
+
+This binary includes a number of subcomponents with separate
+copyright notices and license terms.  Your use of this binary
+is subject to the terms and conditions of the following licenses.
+
+===============================================================================
+License: Apache License Version 2.0
+For details, see META-INF/licenses/apache-license-version-2.0.txt
+
+gson (com.google.code.gson:gson:2.2.4)
+metrics-core (io.dropwizard.metrics:metrics-core:3.1.2)
+
+===============================================================================
+License: MIT
+
+jquery (org.webjars:jquery:1.11.2)
+    For details, see META-INF/licenses/jquery-1_11_2-MIT.txt.
+
+jquery-ui (org.webjars:jquery-ui:1.11.4)
+    For details, see META-INF/licenses/jquery-ui-1_11_4-MIT.txt.
+
+d3.legend.js (included inside the resources/servlets.war!/js/ext/d3.legend/d3.legend.js)
+    https://gist.githubusercontent.com/ZJONSSON/3918369/raw/bf9bce6b68a3b70f87450f155436ca4a84af1ba4/d3.legend.js
+    With Edgent specific modifications.
+    For details, see META-INF/licenses/d3.legend-MIT.txt.
+
+===============================================================================
+License: BSD 3-Clause
+
+d3 (org.webjars.bower:d3:3.3.9)
+    For details, see META-INF/licenses/d3-3_3_9-BSD.txt.
+
+===============================================================================
+License: BSD 2-Clause
+
+d3-plugins-sankey (org.webjars.bower:d3-plugins-sankey:1.1.0)
+    For details, see META-INF/licenses/d3-plugins-sankey-1_1_0-BSD.txt.
+
+===============================================================================

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-undertow/src/main/remote-resources/META-INF/NOTICE
----------------------------------------------------------------------
diff --git a/console/server-undertow/src/main/remote-resources/META-INF/NOTICE b/console/server-undertow/src/main/remote-resources/META-INF/NOTICE
new file mode 100644
index 0000000..9c9e7e9
--- /dev/null
+++ b/console/server-undertow/src/main/remote-resources/META-INF/NOTICE
@@ -0,0 +1,28 @@
+===============================================================================
+
+Portions of this software were developed by IBM Corp.
+Copyright IBM Corp. 2015, 2016
+
+===============================================================================
+
+APACHE EDGENT SUBCOMPONENTS:
+
+This product includes a number of subcomponents with separate
+copyright notices and license terms.  The following notices apply.
+
+-------------------------------------------------------------------------------
+metrics-core (io.dropwizard.metrics:metrics-core:3.1.2)
+
+Metrics
+Copyright 2010-2013 Coda Hale and Yammer, Inc.
+
+This product includes software developed by Coda Hale and Yammer, Inc.
+
+This product includes code derived from the JSR-166 project (ThreadLocalRandom,
+Striped64, LongAdder) with the following comments:
+
+          Written by Doug Lea with assistance from members of JCP JSR-166
+          Expert Group and released to the public domain, as explained at
+          http://creativecommons.org/publicdomain/zero/1.0/
+
+===============================================================================

http://git-wip-us.apache.org/repos/asf/incubator-edgent/blob/d07dff67/console/server-undertow/src/test/java/org/apache/edgent/test/console/server/HttpServerPortTest.java
----------------------------------------------------------------------
diff --git a/console/server-undertow/src/test/java/org/apache/edgent/test/console/server/HttpServerPortTest.java b/console/server-undertow/src/test/java/org/apache/edgent/test/console/server/HttpServerPortTest.java
new file mode 100644
index 0000000..82fc79a
--- /dev/null
+++ b/console/server-undertow/src/test/java/org/apache/edgent/test/console/server/HttpServerPortTest.java
@@ -0,0 +1,58 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.apache.edgent.test.console.server;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import org.apache.edgent.console.server.HttpServer;
+import org.junit.Test;
+
+/**
+ * This is a separate test because the HttpServer implementation
+ * precludes changing the console port within a jvm instance.
+ */
+public class HttpServerPortTest {
+	
+	  int getAvailablePort() throws IOException {
+	    try (Socket s = new Socket()) {
+	      s.bind(null);
+	      return s.getLocalPort();
+	    }
+	  }
+
+    @Test
+    public void testOverridePortNumber() throws Exception {
+      // count on the OS not immediately reusing an available local port.
+      int port = getAvailablePort();
+      int port2 = getAvailablePort();
+      assumeTrue(port != port2);
+      
+      System.setProperty("edgent.console.port", String.valueOf(port));
+      HttpServer myHttpServer = HttpServer.getInstance();
+      myHttpServer.startServer();
+      
+      int portNum = myHttpServer.getConsolePortNumber();
+      assertEquals(port, portNum);
+    }
+
+}