diff --git a/hbase-http/pom.xml b/hbase-http/pom.xml
index 667b3f5..0bc20db 100644
--- a/hbase-http/pom.xml
+++ b/hbase-http/pom.xml
@@ -203,6 +203,22 @@
org.eclipse.jetty
+ jetty-plus
+
+
+ org.eclipse.jetty
+ apache-jsp
+
+
+ org.eclipse.jetty
+ apache-jstl
+
+
+ org.eclipse.jetty
+ jetty-servlet
+
+
+ org.eclipse.jetty
jetty-server
diff --git a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
index af72ab8..a44ba36 100644
--- a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
+++ b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
@@ -27,6 +27,7 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
@@ -58,9 +59,13 @@ import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
import org.apache.hadoop.security.authorize.AccessControlList;
import org.apache.hadoop.util.Shell;
+import org.apache.tomcat.InstanceManager;
+import org.apache.tomcat.SimpleInstanceManager;
import org.apache.yetus.audience.InterfaceAudience;
import org.apache.yetus.audience.InterfaceStability;
+import org.eclipse.jetty.apache.jsp.JettyJasperInitializer;
import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.plus.annotation.ContainerInitializer;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
@@ -111,6 +116,10 @@ public class HttpServer implements FilterContainer {
= "hbase.http.filter.initializers";
static final String HTTP_MAX_THREADS = "hbase.http.max.threads";
+ public static final String HTTP_COMPILE_JSP_DYNAMICALLY_ENABLED =
+ "hbase.http.compile.jsp.dynamically.enabled";
+ public static final boolean HTTP_COMPILE_JSP_DYNAMICALLY_ENABLED_DEFAULT = false;
+
public static final String HTTP_UI_AUTHENTICATION = "hbase.security.authentication.ui";
static final String HTTP_AUTHENTICATION_PREFIX = "hbase.security.authentication.";
static final String HTTP_SPNEGO_AUTHENTICATION_PREFIX = HTTP_AUTHENTICATION_PREFIX
@@ -651,6 +660,13 @@ public class HttpServer implements FilterContainer {
addNoCacheFilter(webAppContext);
defaultContexts.put(logContext, true);
}
+ if (conf.getBoolean(HTTP_COMPILE_JSP_DYNAMICALLY_ENABLED,
+ HTTP_COMPILE_JSP_DYNAMICALLY_ENABLED_DEFAULT)) {
+ webAppContext.setAttribute("org.eclipse.jetty.containerInitializers" , Arrays
+ .asList (new ContainerInitializer(new JettyJasperInitializer(), null)));
+ webAppContext.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager());
+ }
+
// set up the context for "/static/*"
ServletContextHandler staticContext = new ServletContextHandler(parent, "/static");
staticContext.setResourceBase(appDir + "/static");
@@ -1071,8 +1087,11 @@ public class HttpServer implements FilterContainer {
try {
// clear & stop webAppContext attributes to avoid memory leaks.
- webAppContext.clearAttributes();
webAppContext.stop();
+ // stop first then clear up attributes. Otherwise, there may be a race
+ //condition when destroy JspServlet
+ webAppContext.clearAttributes();
+
} catch (Exception e) {
LOG.error("Error while stopping web app context for webapp "
+ webAppContext.getDisplayName(), e);
diff --git a/hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestHttpServerDynamicallyCompileJsp.java b/hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestHttpServerDynamicallyCompileJsp.java
new file mode 100644
index 0000000..ffe95d9
--- /dev/null
+++ b/hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestHttpServerDynamicallyCompileJsp.java
@@ -0,0 +1,99 @@
+/**
+ * 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.hadoop.hbase.http;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.testclassification.MiscTests;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.apache.hadoop.net.NetUtils;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URL;
+
+@Category({MiscTests.class, SmallTests.class})
+public class TestHttpServerDynamicallyCompileJsp extends HttpServerFunctionalTest {
+ @ClassRule
+ public static final HBaseClassTestRule CLASS_RULE =
+ HBaseClassTestRule.forClass(TestHttpServerDynamicallyCompileJsp.class);
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(TestHttpServerDynamicallyCompileJsp.class);
+ private static HttpServer server;
+ private static URL baseUrl;
+ // jetty 9.4.x needs this many threads to start, even in the small.
+ static final int MAX_THREADS = 16;
+ static final String JSP_NAME = "testDynamicallyCompile.jsp";
+
+
+ private void setupServer(boolean enableCompile) throws Exception {
+ Configuration conf = new Configuration();
+ conf.setInt(HttpServer.HTTP_MAX_THREADS, MAX_THREADS);
+ conf.setBoolean(HttpServer.HTTP_COMPILE_JSP_DYNAMICALLY_ENABLED, enableCompile);
+ server = createTestServer(conf);
+ server.start();
+ baseUrl = getServerURL(server);
+ LOG.info("HTTP server started: "+ baseUrl);
+ }
+
+ private static void shutdown() throws Exception {
+ if (server != null) {
+ LOG.info("shutting down");
+ server.stop();
+ }
+ }
+
+ @Test
+ public void testCompileJspDynamicallyOn() throws Exception {
+ setupServer(true);
+ String serverURL = "http://" +
+ NetUtils.getHostPortString(server.getConnectorAddress(0)) +
+ "/" + JSP_NAME;
+ LOG.info("url:" + serverURL);
+ Assert.assertEquals(readOutput(new URL(serverURL)).trim(), "hello world");
+ }
+
+ @Test
+ public void testCompileJspDynamicallyOff() throws Exception {
+ setupServer(false);
+ String serverURL = "http://" +
+ NetUtils.getHostPortString(server.getConnectorAddress(0)) +
+ "/" + JSP_NAME;
+ LOG.info("url:" + serverURL);
+ try {
+ readOutput(new URL(serverURL));
+ fail("should return response code 500");
+ } catch (Exception e) {
+ LOG.info("error output:", e);
+ }
+ }
+
+ @AfterClass
+ public static void cleanup() throws Exception {
+ shutdown();
+ }
+
+}
diff --git a/hbase-http/src/test/java/org/apache/hadoop/hbase/http/log/TestLogLevel.java b/hbase-http/src/test/java/org/apache/hadoop/hbase/http/log/TestLogLevel.java
index 9da4819..f9a7344 100644
--- a/hbase-http/src/test/java/org/apache/hadoop/hbase/http/log/TestLogLevel.java
+++ b/hbase-http/src/test/java/org/apache/hadoop/hbase/http/log/TestLogLevel.java
@@ -63,7 +63,7 @@ public class TestLogLevel {
HttpServer server = null;
try {
- server = new HttpServer.Builder().setName("..")
+ server = new HttpServer.Builder().setName(".")
.addEndpoint(new URI("http://localhost:0")).setFindPort(true)
.build();
diff --git a/hbase-http/src/test/resources/webapps/footer.jsp b/hbase-http/src/test/resources/webapps/footer.jsp
new file mode 100644
index 0000000..1e078d6
--- /dev/null
+++ b/hbase-http/src/test/resources/webapps/footer.jsp
@@ -0,0 +1,24 @@
+<%--
+/**
+* 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.
+*/
+--%>
+
+
+
+