001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.oozie.test; 020 021import org.apache.oozie.servlet.ErrorServlet; 022import org.eclipse.jetty.server.Connector; 023import org.eclipse.jetty.server.HttpConfiguration; 024import org.eclipse.jetty.server.HttpConnectionFactory; 025import org.eclipse.jetty.server.Server; 026import org.eclipse.jetty.server.ServerConnector; 027import org.eclipse.jetty.servlet.ErrorPageErrorHandler; 028import org.eclipse.jetty.servlet.FilterHolder; 029import org.eclipse.jetty.servlet.ServletContextHandler; 030import org.eclipse.jetty.servlet.ServletHolder; 031 032import javax.servlet.DispatcherType; 033import javax.servlet.Filter; 034import javax.servlet.Servlet; 035import javax.servlet.http.HttpServletResponse; 036import java.net.InetAddress; 037import java.util.EnumSet; 038import java.util.Map; 039 040/** 041 * An embedded servlet container for testing purposes. <p> It provides reduced functionality, it supports only 042 * Servlets. <p> The servlet container is started in a free port. 043 */ 044public class EmbeddedServletContainer { 045 private Server server; 046 private String host = null; 047 private int port = -1; 048 private String contextPath; 049 private ServletContextHandler context; 050 051 /** 052 * Create a servlet container. 053 * 054 * @param contextPath context path for the servlet, it must not be prefixed or append with "/", for the default 055 * context use "" 056 */ 057 public EmbeddedServletContainer(String contextPath) { 058 this.contextPath = contextPath; 059 server = new Server(0); 060 context = new ServletContextHandler(); 061 context.setContextPath("/" + contextPath); 062 context.setErrorHandler(getErrorHandler()); 063 this.addServletEndpoint("/error/*", ErrorServlet.class); 064 server.setHandler(context); 065 } 066 067 /** 068 * Add a servlet to the container. 069 * 070 * @param servletPath servlet path for the servlet, it should be prefixed with '/", it may contain a wild card at 071 * the end. 072 * @param servletClass servlet class 073 * @param initParams a mapping of init parameters for the servlet, or null 074 */ 075 public void addServletEndpoint(String servletPath, Class<? extends Servlet> servletClass, Map<String, String> initParams) { 076 ServletHolder holder = new ServletHolder(servletClass); 077 if (initParams != null) { 078 holder.setInitParameters(initParams); 079 } 080 context.addServlet(holder, servletPath); 081 } 082 083 /** 084 * Add a servlet to the container. 085 * 086 * @param servletPath servlet path for the servlet, it should be prefixed with '/", it may contain a wild card at 087 * the end. 088 * @param servletClass servlet class 089 */ 090 public void addServletEndpoint(String servletPath, Class<? extends Servlet> servletClass) { 091 addServletEndpoint(servletPath, servletClass, null); 092 } 093 094 /** 095 * Add a servlet instance to the container. 096 * 097 * @param servletPath servlet path for the servlet, it should be prefixed with '/", it may contain a wild card at 098 * the end. 099 * @param servlet servlet instance 100 */ 101 public void addServletEndpoint(String servletPath, Servlet servlet) { 102 ServletHolder holder = new ServletHolder(servlet); 103 context.addServlet(holder, servletPath); 104 } 105 106 /** 107 * Add a filter to the container. 108 * 109 * @param filterPath path for the filter, it should be prefixed with '/", it may contain a wild card at 110 * the end. 111 * @param filterClass servlet class 112 */ 113 public void addFilter(String filterPath, Class<? extends Filter> filterClass) { 114 context.addFilter(new FilterHolder(filterClass), filterPath, EnumSet.of(DispatcherType.REQUEST)); 115 } 116 117 /** 118 * Start the servlet container. <p> The container starts on a free port. 119 * 120 * @throws Exception thrown if the container could not start. 121 */ 122 public void start() throws Exception { 123 host = InetAddress.getLocalHost().getHostName(); 124 ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(new HttpConfiguration())); 125 connector.setHost(host); 126 server.setConnectors(new Connector[] { connector }); 127 server.start(); 128 port = connector.getLocalPort(); 129 System.out.println("Running embedded servlet container at: http://" + host + ":" + port); 130 } 131 132 /** 133 * Return the hostname the servlet container is bound to. 134 * 135 * @return the hostname. 136 */ 137 public String getHost() { 138 return host; 139 } 140 141 /** 142 * Return the port number the servlet container is bound to. 143 * 144 * @return the port number. 145 */ 146 public int getPort() { 147 return port; 148 } 149 150 /** 151 * Return the full URL (including protocol, host, port, context path, servlet path) for the context path. 152 * 153 * @return URL to the context path. 154 */ 155 public String getContextURL() { 156 return "http://" + host + ":" + port + "/" + contextPath; 157 } 158 159 /** 160 * Return the full URL (including protocol, host, port, context path, servlet path) for a servlet path. 161 * 162 * @param servletPath the path which will be expanded to a full URL. 163 * @return URL to the servlet. 164 */ 165 public String getServletURL(String servletPath) { 166 String path = servletPath; 167 if (path.endsWith("*")) { 168 path = path.substring(0, path.length() - 1); 169 } 170 return getContextURL() + path; 171 } 172 173 /** 174 * Stop the servlet container. 175 */ 176 public void stop() { 177 try { 178 server.stop(); 179 } 180 catch (Exception e) { 181 // ignore exception 182 } 183 184 try { 185 server.destroy(); 186 } 187 catch (Exception e) { 188 // ignore exception 189 } 190 191 host = null; 192 port = -1; 193 } 194 195 /** 196 * Returns an error page handler 197 * @return 198 */ 199 private ErrorPageErrorHandler getErrorHandler() { 200 ErrorPageErrorHandler errorHandler = new ErrorPageErrorHandler(); 201 errorHandler.addErrorPage(HttpServletResponse.SC_BAD_REQUEST, "/error"); 202 errorHandler.addErrorPage(HttpServletResponse.SC_UNAUTHORIZED, "/error"); 203 errorHandler.addErrorPage(HttpServletResponse.SC_FORBIDDEN, "/error"); 204 errorHandler.addErrorPage(HttpServletResponse.SC_NOT_FOUND, "/error"); 205 errorHandler.addErrorPage(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "/error"); 206 errorHandler.addErrorPage(HttpServletResponse.SC_CONFLICT, "/error"); 207 errorHandler.addErrorPage(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "/error"); 208 errorHandler.addErrorPage(HttpServletResponse.SC_NOT_IMPLEMENTED, "/error"); 209 errorHandler.addErrorPage(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "/error"); 210 errorHandler.addErrorPage("java.lang.Throwable", "/error"); 211 return errorHandler; 212 } 213}