blob: 2fb099f7da525f8a10c5997a8a27a9205928fb23 [file] [log] [blame]
/**
* 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.oozie.test;
import org.apache.oozie.servlet.ErrorServlet;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletResponse;
import java.net.InetAddress;
import java.util.EnumSet;
import java.util.Map;
/**
* An embedded servlet container for testing purposes. <p> It provides reduced functionality, it supports only
* Servlets. <p> The servlet container is started in a free port or a specific port which depends on the testing
* purposes.
*/
public class EmbeddedServletContainer {
private Server server;
private String host = null;
private int port = -1;
private String contextPath;
private ServletContextHandler context;
/**
* Create a servlet container.
*
* @param contextPath context path for the servlet, it must not be prefixed or append with "/", for the default
* context use ""
*/
public EmbeddedServletContainer(String contextPath) {
this.contextPath = contextPath;
server = new Server(0);
context = new ServletContextHandler();
context.setContextPath("/" + contextPath);
context.setErrorHandler(getErrorHandler());
this.addServletEndpoint("/error/*", ErrorServlet.class);
server.setHandler(context);
}
public EmbeddedServletContainer(String contextPath, int port) {
this(contextPath);
this.port = port;
}
/**
* Add a servlet to the container.
*
* @param servletPath servlet path for the servlet, it should be prefixed with '/", it may contain a wild card at
* the end.
* @param servletClass servlet class
* @param initParams a mapping of init parameters for the servlet, or null
*/
public void addServletEndpoint(String servletPath, Class<? extends Servlet> servletClass, Map<String, String> initParams) {
ServletHolder holder = new ServletHolder(servletClass);
if (initParams != null) {
holder.setInitParameters(initParams);
}
context.addServlet(holder, servletPath);
}
/**
* Add a servlet to the container.
*
* @param servletPath servlet path for the servlet, it should be prefixed with '/", it may contain a wild card at
* the end.
* @param servletClass servlet class
*/
public void addServletEndpoint(String servletPath, Class<? extends Servlet> servletClass) {
addServletEndpoint(servletPath, servletClass, null);
}
/**
* Add a servlet instance to the container.
*
* @param servletPath servlet path for the servlet, it should be prefixed with '/", it may contain a wild card at
* the end.
* @param servlet servlet instance
*/
public void addServletEndpoint(String servletPath, Servlet servlet) {
ServletHolder holder = new ServletHolder(servlet);
context.addServlet(holder, servletPath);
}
/**
* Add a filter to the container.
*
* @param filterPath path for the filter, it should be prefixed with '/", it may contain a wild card at
* the end.
* @param filterClass servlet class
*/
public void addFilter(String filterPath, Class<? extends Filter> filterClass) {
context.addFilter(new FilterHolder(filterClass), filterPath, EnumSet.of(DispatcherType.REQUEST));
}
/**
* Start the servlet container. <p> The container starts on a free port or a specific port.
*
* @throws Exception thrown if the container could not start.
*/
public void start() throws Exception {
host = InetAddress.getLocalHost().getHostName();
port = startServerWithPort(port);
System.out.println("Running embedded servlet container at: http://" + host + ":" + port);
}
/**
* if port is the default value (-1), this will start on the free port, and this function will return this port
* if port is not -1, this will start on the specific port
* @param port
* @return
* @throws Exception
*/
private int startServerWithPort(int port) throws Exception {
host = InetAddress.getLocalHost().getHostName();
ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(new HttpConfiguration()));
connector.setHost(host);
if (port != -1) {
connector.setPort(port);
}
server.setConnectors(new Connector[] { connector });
server.start();
return connector.getLocalPort();
}
/**
* Return the hostname the servlet container is bound to.
*
* @return the hostname.
*/
public String getHost() {
return host;
}
/**
* Return the port number the servlet container is bound to.
*
* @return the port number.
*/
public int getPort() {
return port;
}
/**
* Return the full URL (including protocol, host, port, context path, servlet path) for the context path.
*
* @return URL to the context path.
*/
public String getContextURL() {
return "http://" + host + ":" + port + "/" + contextPath;
}
/**
* Return the full URL (including protocol, host, port, context path, servlet path) for a servlet path.
*
* @param servletPath the path which will be expanded to a full URL.
* @return URL to the servlet.
*/
public String getServletURL(String servletPath) {
String path = servletPath;
if (path.endsWith("*")) {
path = path.substring(0, path.length() - 1);
}
return getContextURL() + path;
}
/**
* Stop the servlet container.
*/
public void stop() {
try {
server.stop();
}
catch (Exception e) {
// ignore exception
}
try {
server.destroy();
}
catch (Exception e) {
// ignore exception
}
host = null;
port = -1;
}
/**
* Returns an error page handler
* @return
*/
private ErrorPageErrorHandler getErrorHandler() {
ErrorPageErrorHandler errorHandler = new ErrorPageErrorHandler();
errorHandler.addErrorPage(HttpServletResponse.SC_BAD_REQUEST, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_UNAUTHORIZED, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_FORBIDDEN, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_NOT_FOUND, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_CONFLICT, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_NOT_IMPLEMENTED, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "/error");
errorHandler.addErrorPage("java.lang.Throwable", "/error");
return errorHandler;
}
}