|
@@ -19,12 +19,13 @@ package org.apache.hadoop.http;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
import java.io.PrintWriter;
|
|
|
+import java.net.BindException;
|
|
|
import java.net.InetSocketAddress;
|
|
|
import java.net.URL;
|
|
|
import java.util.ArrayList;
|
|
|
+import java.util.HashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
-import java.util.HashMap;
|
|
|
|
|
|
import javax.servlet.ServletException;
|
|
|
import javax.servlet.http.HttpServlet;
|
|
@@ -36,12 +37,22 @@ import org.apache.commons.logging.LogFactory;
|
|
|
import org.apache.hadoop.conf.Configuration;
|
|
|
import org.apache.hadoop.log.LogLevel;
|
|
|
import org.apache.hadoop.util.ReflectionUtils;
|
|
|
-import org.mortbay.http.SocketListener;
|
|
|
-import org.mortbay.http.SslListener;
|
|
|
-import org.mortbay.jetty.servlet.Dispatcher;
|
|
|
+
|
|
|
+import org.mortbay.jetty.Connector;
|
|
|
+import org.mortbay.jetty.Handler;
|
|
|
+import org.mortbay.jetty.Server;
|
|
|
+import org.mortbay.jetty.handler.ContextHandlerCollection;
|
|
|
+import org.mortbay.jetty.nio.SelectChannelConnector;
|
|
|
+import org.mortbay.jetty.security.SslSocketConnector;
|
|
|
+import org.mortbay.jetty.servlet.Context;
|
|
|
+import org.mortbay.jetty.servlet.DefaultServlet;
|
|
|
import org.mortbay.jetty.servlet.FilterHolder;
|
|
|
-import org.mortbay.jetty.servlet.WebApplicationContext;
|
|
|
-import org.mortbay.jetty.servlet.WebApplicationHandler;
|
|
|
+import org.mortbay.jetty.servlet.FilterMapping;
|
|
|
+import org.mortbay.jetty.servlet.ServletHandler;
|
|
|
+import org.mortbay.jetty.servlet.ServletHolder;
|
|
|
+import org.mortbay.jetty.webapp.WebAppContext;
|
|
|
+import org.mortbay.thread.QueuedThreadPool;
|
|
|
+import org.mortbay.util.MultiException;
|
|
|
|
|
|
/**
|
|
|
* Create a Jetty embedded server to answer http requests. The primary goal
|
|
@@ -57,19 +68,18 @@ public class HttpServer implements FilterContainer {
|
|
|
static final String FILTER_INITIALIZER_PROPERTY
|
|
|
= "hadoop.http.filter.initializers";
|
|
|
|
|
|
- protected final org.mortbay.jetty.Server webServer;
|
|
|
- protected final WebApplicationContext webAppContext;
|
|
|
- protected final Map<WebApplicationContext, Boolean> defaultContexts =
|
|
|
- new HashMap<WebApplicationContext, Boolean>();
|
|
|
+ protected final Server webServer;
|
|
|
+ protected final Connector listener;
|
|
|
+ protected final WebAppContext webAppContext;
|
|
|
protected final boolean findPort;
|
|
|
- protected final SocketListener listener;
|
|
|
- private SslListener sslListener;
|
|
|
+ protected final Map<Context, Boolean> defaultContexts =
|
|
|
+ new HashMap<Context, Boolean>();
|
|
|
protected final List<String> filterNames = new ArrayList<String>();
|
|
|
|
|
|
/** Same as this(name, bindAddress, port, findPort, null); */
|
|
|
public HttpServer(String name, String bindAddress, int port, boolean findPort
|
|
|
) throws IOException {
|
|
|
- this(name, bindAddress, port, findPort, null);
|
|
|
+ this(name, bindAddress, port, findPort, new Configuration());
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -83,16 +93,26 @@ public class HttpServer implements FilterContainer {
|
|
|
*/
|
|
|
public HttpServer(String name, String bindAddress, int port,
|
|
|
boolean findPort, Configuration conf) throws IOException {
|
|
|
- webServer = new org.mortbay.jetty.Server();
|
|
|
+ webServer = new Server();
|
|
|
this.findPort = findPort;
|
|
|
- listener = new SocketListener();
|
|
|
- listener.setPort(port);
|
|
|
+
|
|
|
+ listener = createBaseListener(conf);
|
|
|
listener.setHost(bindAddress);
|
|
|
- webServer.addListener(listener);
|
|
|
+ listener.setPort(port);
|
|
|
+ webServer.addConnector(listener);
|
|
|
+
|
|
|
+ webServer.setThreadPool(new QueuedThreadPool());
|
|
|
|
|
|
final String appDir = getWebAppsPath();
|
|
|
- webAppContext = webServer.addWebApplication("/", appDir + "/" + name);
|
|
|
- addDefaultApps(appDir);
|
|
|
+ ContextHandlerCollection contexts = new ContextHandlerCollection();
|
|
|
+ webServer.setHandler(contexts);
|
|
|
+
|
|
|
+ webAppContext = new WebAppContext();
|
|
|
+ webAppContext.setContextPath("/");
|
|
|
+ webAppContext.setWar(appDir + "/" + name);
|
|
|
+ webServer.addHandler(webAppContext);
|
|
|
+
|
|
|
+ addDefaultApps(contexts, appDir);
|
|
|
|
|
|
final FilterInitializer[] initializers = getFilterInitializers(conf);
|
|
|
if (initializers != null) {
|
|
@@ -103,6 +123,21 @@ public class HttpServer implements FilterContainer {
|
|
|
addDefaultServlets();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Create a required listener for the Jetty instance listening on the port
|
|
|
+ * provided. This wrapper and all subclasses must create at least one
|
|
|
+ * listener.
|
|
|
+ */
|
|
|
+ protected Connector createBaseListener(Configuration conf)
|
|
|
+ throws IOException {
|
|
|
+ SelectChannelConnector ret = new SelectChannelConnector();
|
|
|
+ ret.setLowResourceMaxIdleTime(10000);
|
|
|
+ ret.setAcceptQueueSize(128);
|
|
|
+ ret.setResolveNames(false);
|
|
|
+ ret.setUseDirectBuffers(false);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
/** Get an array of FilterConfiguration specified in the conf */
|
|
|
private static FilterInitializer[] getFilterInitializers(Configuration conf) {
|
|
|
if (conf == null) {
|
|
@@ -127,15 +162,21 @@ public class HttpServer implements FilterContainer {
|
|
|
* @param appDir The application directory
|
|
|
* @throws IOException
|
|
|
*/
|
|
|
- protected void addDefaultApps(final String appDir) throws IOException {
|
|
|
+ protected void addDefaultApps(ContextHandlerCollection parent,
|
|
|
+ final String appDir) throws IOException {
|
|
|
// set up the context for "/logs/" if "hadoop.log.dir" property is defined.
|
|
|
String logDir = System.getProperty("hadoop.log.dir");
|
|
|
if (logDir != null) {
|
|
|
- addContext("/logs/*", logDir, true);
|
|
|
+ Context logContext = new Context(parent, "/logs");
|
|
|
+ logContext.setResourceBase(logDir);
|
|
|
+ logContext.addServlet(DefaultServlet.class, "/");
|
|
|
+ defaultContexts.put(logContext, true);
|
|
|
}
|
|
|
-
|
|
|
// set up the context for "/static/*"
|
|
|
- addContext("/static/*", appDir + "/static", true);
|
|
|
+ Context staticContext = new Context(parent, "/static");
|
|
|
+ staticContext.setResourceBase(appDir + "/static");
|
|
|
+ staticContext.addServlet(DefaultServlet.class, "/*");
|
|
|
+ defaultContexts.put(staticContext, true);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -147,6 +188,12 @@ public class HttpServer implements FilterContainer {
|
|
|
addServlet("logLevel", "/logLevel", LogLevel.Servlet.class);
|
|
|
}
|
|
|
|
|
|
+ public void addContext(Context ctxt, boolean isFiltered)
|
|
|
+ throws IOException {
|
|
|
+ webServer.addHandler(ctxt);
|
|
|
+ defaultContexts.put(ctxt, isFiltered);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Add a context
|
|
|
* @param pathSpec The path spec for the context
|
|
@@ -155,8 +202,13 @@ public class HttpServer implements FilterContainer {
|
|
|
* @throws IOException
|
|
|
*/
|
|
|
protected void addContext(String pathSpec, String dir, boolean isFiltered) throws IOException {
|
|
|
- WebApplicationContext webAppCtx = webServer.addWebApplication(pathSpec, dir);
|
|
|
- defaultContexts.put(webAppCtx, isFiltered);
|
|
|
+ if (0 == webServer.getHandlers().length) {
|
|
|
+ throw new RuntimeException("Couldn't find handler");
|
|
|
+ }
|
|
|
+ WebAppContext webAppCtx = new WebAppContext();
|
|
|
+ webAppCtx.setContextPath(pathSpec);
|
|
|
+ webAppCtx.setWar(dir);
|
|
|
+ addContext(webAppCtx, true);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -191,19 +243,11 @@ public class HttpServer implements FilterContainer {
|
|
|
@Deprecated
|
|
|
public void addInternalServlet(String name, String pathSpec,
|
|
|
Class<? extends HttpServlet> clazz) {
|
|
|
- try {
|
|
|
- if (name == null) {
|
|
|
- webAppContext.addServlet(pathSpec, clazz.getName());
|
|
|
- } else {
|
|
|
- webAppContext.addServlet(name, pathSpec, clazz.getName());
|
|
|
- }
|
|
|
- } catch (ClassNotFoundException cnfe) {
|
|
|
- throw new RuntimeException("Problem instantiating class", cnfe);
|
|
|
- } catch (InstantiationException ie) {
|
|
|
- throw new RuntimeException("Problem instantiating class", ie);
|
|
|
- } catch (IllegalAccessException iae) {
|
|
|
- throw new RuntimeException("Problem instantiating class", iae);
|
|
|
+ ServletHolder holder = new ServletHolder(clazz);
|
|
|
+ if (name != null) {
|
|
|
+ holder.setName(name);
|
|
|
}
|
|
|
+ webAppContext.addServlet(holder, pathSpec);
|
|
|
}
|
|
|
|
|
|
/** {@inheritDoc} */
|
|
@@ -212,15 +256,13 @@ public class HttpServer implements FilterContainer {
|
|
|
|
|
|
final String[] USER_FACING_URLS = { "*.html", "*.jsp" };
|
|
|
defineFilter(webAppContext, name, classname, parameters, USER_FACING_URLS);
|
|
|
-
|
|
|
final String[] ALL_URLS = { "/*" };
|
|
|
- for (Map.Entry<WebApplicationContext, Boolean> e : defaultContexts
|
|
|
- .entrySet()) {
|
|
|
+ for (Map.Entry<Context, Boolean> e : defaultContexts.entrySet()) {
|
|
|
if (e.getValue()) {
|
|
|
- WebApplicationContext ctx = e.getKey();
|
|
|
+ Context ctx = e.getKey();
|
|
|
defineFilter(ctx, name, classname, parameters, ALL_URLS);
|
|
|
LOG.info("Added filter " + name + " (class=" + classname
|
|
|
- + ") to context " + ctx.getName());
|
|
|
+ + ") to context " + ctx.getDisplayName());
|
|
|
}
|
|
|
}
|
|
|
filterNames.add(name);
|
|
@@ -231,7 +273,7 @@ public class HttpServer implements FilterContainer {
|
|
|
Map<String, String> parameters) {
|
|
|
final String[] ALL_URLS = { "/*" };
|
|
|
defineFilter(webAppContext, name, classname, parameters, ALL_URLS);
|
|
|
- for (WebApplicationContext ctx : defaultContexts.keySet()) {
|
|
|
+ for (Context ctx : defaultContexts.keySet()) {
|
|
|
defineFilter(ctx, name, classname, parameters, ALL_URLS);
|
|
|
}
|
|
|
LOG.info("Added global filter" + name + " (class=" + classname + ")");
|
|
@@ -240,20 +282,19 @@ public class HttpServer implements FilterContainer {
|
|
|
/**
|
|
|
* Define a filter for a context and set up default url mappings.
|
|
|
*/
|
|
|
- protected void defineFilter(WebApplicationContext ctx, String name,
|
|
|
- String classname, Map<String, String> parameters, String[] urls) {
|
|
|
-
|
|
|
- WebApplicationHandler handler = ctx.getWebApplicationHandler();
|
|
|
- FilterHolder holder = handler.defineFilter(name, classname);
|
|
|
- if (parameters != null) {
|
|
|
- for(Map.Entry<String, String> e : parameters.entrySet()) {
|
|
|
- holder.setInitParameter(e.getKey(), e.getValue());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for (String url : urls) {
|
|
|
- handler.addFilterPathMapping(url, name, Dispatcher.__ALL);
|
|
|
- }
|
|
|
+ protected void defineFilter(Context ctx, String name,
|
|
|
+ String classname, Map<String,String> parameters, String[] urls) {
|
|
|
+
|
|
|
+ FilterHolder holder = new FilterHolder();
|
|
|
+ holder.setName(name);
|
|
|
+ holder.setClassName(classname);
|
|
|
+ holder.setInitParameters(parameters);
|
|
|
+ FilterMapping fmap = new FilterMapping();
|
|
|
+ fmap.setPathSpecs(urls);
|
|
|
+ fmap.setDispatches(Handler.ALL);
|
|
|
+ fmap.setFilterName(name);
|
|
|
+ ServletHandler handler = ctx.getServletHandler();
|
|
|
+ handler.addFilter(holder, fmap);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -261,10 +302,15 @@ public class HttpServer implements FilterContainer {
|
|
|
* @param pathSpec The path spec
|
|
|
* @param webAppCtx The WebApplicationContext to add to
|
|
|
*/
|
|
|
- protected void addFilterPathMapping(String pathSpec, WebApplicationContext webAppCtx) {
|
|
|
- WebApplicationHandler handler = webAppCtx.getWebApplicationHandler();
|
|
|
+ protected void addFilterPathMapping(String pathSpec,
|
|
|
+ Context webAppCtx) {
|
|
|
+ ServletHandler handler = webAppCtx.getServletHandler();
|
|
|
for(String name : filterNames) {
|
|
|
- handler.addFilterPathMapping(pathSpec, name, Dispatcher.__ALL);
|
|
|
+ FilterMapping fmap = new FilterMapping();
|
|
|
+ fmap.setPathSpec(pathSpec);
|
|
|
+ fmap.setFilterName(name);
|
|
|
+ fmap.setDispatches(Handler.ALL);
|
|
|
+ handler.addFilterMapping(fmap);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -294,12 +340,16 @@ public class HttpServer implements FilterContainer {
|
|
|
* @return the port
|
|
|
*/
|
|
|
public int getPort() {
|
|
|
- return listener.getPort();
|
|
|
+ return webServer.getConnectors()[0].getLocalPort();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Set the min, max number of worker threads (simultaneous connections).
|
|
|
+ */
|
|
|
public void setThreads(int min, int max) {
|
|
|
- listener.setMinThreads(min);
|
|
|
- listener.setMaxThreads(max);
|
|
|
+ QueuedThreadPool pool = (QueuedThreadPool) webServer.getThreadPool() ;
|
|
|
+ pool.setMinThreads(min);
|
|
|
+ pool.setMaxThreads(max);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -313,16 +363,16 @@ public class HttpServer implements FilterContainer {
|
|
|
@Deprecated
|
|
|
public void addSslListener(InetSocketAddress addr, String keystore,
|
|
|
String storPass, String keyPass) throws IOException {
|
|
|
- if (sslListener != null || webServer.isStarted()) {
|
|
|
+ if (webServer.isStarted()) {
|
|
|
throw new IOException("Failed to add ssl listener");
|
|
|
}
|
|
|
- sslListener = new SslListener();
|
|
|
+ SslSocketConnector sslListener = new SslSocketConnector();
|
|
|
sslListener.setHost(addr.getHostName());
|
|
|
sslListener.setPort(addr.getPort());
|
|
|
sslListener.setKeystore(keystore);
|
|
|
sslListener.setPassword(storPass);
|
|
|
sslListener.setKeyPassword(keyPass);
|
|
|
- webServer.addListener(sslListener);
|
|
|
+ webServer.addConnector(sslListener);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -333,7 +383,7 @@ public class HttpServer implements FilterContainer {
|
|
|
*/
|
|
|
public void addSslListener(InetSocketAddress addr, Configuration sslConf,
|
|
|
boolean needClientAuth) throws IOException {
|
|
|
- if (sslListener != null || webServer.isStarted()) {
|
|
|
+ if (webServer.isStarted()) {
|
|
|
throw new IOException("Failed to add ssl listener");
|
|
|
}
|
|
|
if (needClientAuth) {
|
|
@@ -345,7 +395,7 @@ public class HttpServer implements FilterContainer {
|
|
|
System.setProperty("javax.net.ssl.trustStoreType", sslConf.get(
|
|
|
"ssl.server.truststore.type", "jks"));
|
|
|
}
|
|
|
- sslListener = new SslListener();
|
|
|
+ SslSocketConnector sslListener = new SslSocketConnector();
|
|
|
sslListener.setHost(addr.getHostName());
|
|
|
sslListener.setPort(addr.getPort());
|
|
|
sslListener.setKeystore(sslConf.get("ssl.server.keystore.location"));
|
|
@@ -353,7 +403,7 @@ public class HttpServer implements FilterContainer {
|
|
|
sslListener.setKeyPassword(sslConf.get("ssl.server.keystore.keypassword", ""));
|
|
|
sslListener.setKeystoreType(sslConf.get("ssl.server.keystore.type", "jks"));
|
|
|
sslListener.setNeedClientAuth(needClientAuth);
|
|
|
- webServer.addListener(sslListener);
|
|
|
+ webServer.addConnector(sslListener);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -365,39 +415,37 @@ public class HttpServer implements FilterContainer {
|
|
|
try {
|
|
|
webServer.start();
|
|
|
break;
|
|
|
- } catch (org.mortbay.util.MultiException ex) {
|
|
|
+ } catch (MultiException ex) {
|
|
|
// if the multi exception contains ONLY a bind exception,
|
|
|
// then try the next port number.
|
|
|
- boolean needNewPort = false;
|
|
|
- if(ex.size() == 1) {
|
|
|
- Exception sub = ex.getException(0);
|
|
|
- if (sub instanceof java.net.BindException) {
|
|
|
- if(!findPort)
|
|
|
- throw sub; // java.net.BindException
|
|
|
- needNewPort = true;
|
|
|
+ if (ex.size() == 1 && ex.getThrowable(0) instanceof BindException) {
|
|
|
+ if (!findPort) {
|
|
|
+ throw (BindException) ex.getThrowable(0);
|
|
|
}
|
|
|
- }
|
|
|
- if (!needNewPort)
|
|
|
+ } else {
|
|
|
throw ex;
|
|
|
- listener.setPort(listener.getPort() + 1);
|
|
|
+ }
|
|
|
}
|
|
|
+ listener.setPort(listener.getLocalPort() + 1);
|
|
|
}
|
|
|
} catch (IOException ie) {
|
|
|
throw ie;
|
|
|
} catch (Exception e) {
|
|
|
- IOException ie = new IOException("Problem starting http server");
|
|
|
- ie.initCause(e);
|
|
|
- throw ie;
|
|
|
+ throw new IOException("Problem starting http server", e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* stop the server
|
|
|
*/
|
|
|
- public void stop() throws InterruptedException {
|
|
|
+ public void stop() throws Exception {
|
|
|
webServer.stop();
|
|
|
}
|
|
|
|
|
|
+ public void join() throws InterruptedException {
|
|
|
+ webServer.join();
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* A very simple servlet to serve up a text representation of the current
|
|
|
* stack traces. It both returns the stacks to the caller and logs them.
|