Use HttpServletRequest.getContextPath to get the context path

This commit is contained in:
James Agnew 2015-07-27 11:19:29 -04:00
parent e9061ef975
commit 75798cf9fe
7 changed files with 235 additions and 52 deletions

View File

@ -23,7 +23,7 @@
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.1.26</version>
<version>6.1.26</version> <!-- 6.1.26 -->
</dependency>
<dependency>
<groupId>org.slf4j</groupId>

View File

@ -35,21 +35,16 @@ public class IncomingRequestAddressStrategy implements IServerAddressStrategy {
@Override
public String determineServerBase(ServletContext theServletContext, HttpServletRequest theRequest) {
String requestFullPath = StringUtils.defaultString(theRequest.getRequestURI());
String servletPath;
if (myServletPath != null) {
servletPath = myServletPath;
} else {
servletPath = StringUtils.defaultString(theRequest.getServletPath());
}
StringBuffer requestUrl = theRequest.getRequestURL();
String servletContextPath = "";
if (theServletContext != null) {
servletContextPath = StringUtils.defaultString(theServletContext.getContextPath());
// } else {
// servletContextPath = servletPath;
}
String servletContextPath = StringUtils.defaultString(theRequest.getContextPath());
String requestPath = requestFullPath.substring(servletContextPath.length() + servletPath.length());
if (requestPath.length() > 0 && requestPath.charAt(0) == '/') {
@ -105,4 +100,33 @@ public class IncomingRequestAddressStrategy implements IServerAddressStrategy {
myServletPath = theServletPath;
}
/**
* Determines the servlet's context path.
*
* This is here to try and deal with the wide variation in servers and what they return.
*
* getServletContext().getContextPath() is supposed to return the path to the specific servlet we are deployed as but it's not available everywhere. On some servers getServletContext() can return
* null (old Jetty seems to suffer from this, see hapi-fhir-base-test-mindeps-server) and on other old servers (Servlet 2.4) getServletContext().getContextPath() doesn't even exist.
*
* theRequest.getContextPath() returns the context for the specific incoming request. It should be available everywhere, but it's likely to be less predicable if there are multiple servlet mappings
* pointing to the same servlet, so we don't favour it. This is possibly not the best strategy (maybe we should just always use theRequest.getContextPath()?) but so far people seem happy with this
* behavour across a wide variety of platforms.
*
* If you are having troubles on a given platform/configuration and want to suggest a change or even report incompatibility here, we'd love to hear about it.
*/
public static String determineServletContextPath(HttpServletRequest theRequest, RestfulServer server) {
String retVal;
if (server.getServletContext() != null) {
if (server.getServletContext().getMajorVersion() >= 3 || (server.getServletContext().getMajorVersion() > 2 && server.getServletContext().getMinorVersion() >= 5)) {
retVal = server.getServletContext().getContextPath();
} else {
retVal = theRequest.getContextPath();
}
} else {
retVal = theRequest.getContextPath();
}
retVal = StringUtils.defaultString(retVal);
return retVal;
}
}

View File

@ -525,15 +525,11 @@ public class RestfulServer extends HttpServlet {
String requestFullPath = StringUtils.defaultString(theRequest.getRequestURI());
String servletPath = StringUtils.defaultString(theRequest.getServletPath());
StringBuffer requestUrl = theRequest.getRequestURL();
String servletContextPath = "";
// if (getServletContext().getMajorVersion() >= 3) {
// // getServletContext is only supported in version 3+ of servlet-api
if (getServletContext() != null) {
servletContextPath = StringUtils.defaultString(getServletContext().getContextPath());
}
// }
String servletContextPath = IncomingRequestAddressStrategy.determineServletContextPath(theRequest, this);
/*
* Just for debugging..
*/
if (ourLog.isTraceEnabled()) {
ourLog.trace("Request FullPath: {}", requestFullPath);
ourLog.trace("Servlet Path: {}", servletPath);
@ -752,6 +748,7 @@ public class RestfulServer extends HttpServlet {
}
}
/**
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations,
* but subclasses may put initialization code in {@link #initialize()}, which is called immediately before beginning
@ -842,6 +839,7 @@ public class RestfulServer extends HttpServlet {
* (which extends {@link ServletException}), as this is a flag to the servlet container that the servlet
* is not usable.
*/
@SuppressWarnings("unused")
protected void initialize() throws ServletException {
// nothing by default
}

View File

@ -220,6 +220,8 @@ public class IncomingRequestAddressStrategyTest {
private static class MyServlet extends HttpServlet {
private static final long serialVersionUID = -8903322104434705422L;
@Override
protected void doGet(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {

View File

@ -0,0 +1,188 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.util.RandomServerPortProvider;
public class ServletContextParsingTest {
private static CloseableHttpClient ourClient;
private static IdDt ourLastId;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletContextParsingTest.class);
private Server myServer;
public void after() throws Exception {
if (myServer != null) {
myServer.stop();
}
}
@Before
public void before() {
ourLastId = null;
}
private void httpGet(String url) throws IOException, ClientProtocolException {
ourLastId = null;
HttpGet httpget = new HttpGet(url);
HttpResponse status = ourClient.execute(httpget);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
}
private void startServer(int port, String contextPath, String servletPath) throws Exception {
myServer = new Server(port);
org.eclipse.jetty.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.servlet.ServletContextHandler();
proxyHandler.setContextPath(contextPath);
ServletHolder handler = new ServletHolder();
handler.setServlet(new MyServlet());
proxyHandler.addServlet(handler, servletPath);
myServer.setHandler(proxyHandler);
myServer.start();
}
@Test
public void testUnderJettyWithContextPathServletRoot() throws Exception {
int port = RandomServerPortProvider.findFreePort();
String contextPath = "/ctx";
String servletPath = "/*";
startServer(port, contextPath, servletPath);
httpGet("http://localhost:" + port + "/ctx/Patient/123/_history/234?_pretty=true");
assertEquals("Patient/123/_history/234", ourLastId.getValue());
}
@Test
public void testUnderJettyWithContextPathServletRoot2() throws Exception {
int port = RandomServerPortProvider.findFreePort();
String contextPath = "/ctx";
String servletPath = "/foo/bar/*"; // not /* but still this should work
startServer(port, contextPath, servletPath);
httpGet("http://localhost:" + port + "/ctx/foo/bar/Patient/123/_history/222");
assertEquals("Patient/123/_history/222", ourLastId.getValue());
}
@Test
public void testUnderJettyWithContextPathServletPath() throws Exception {
int port = RandomServerPortProvider.findFreePort();
String contextPath = "/ctx";
String servletPath = "/servlet/*";
startServer(port, contextPath, servletPath);
httpGet("http://localhost:" + port + "/ctx/servlet/Patient/123/_history/222");
assertEquals("Patient/123/_history/222", ourLastId.getValue());
}
@Test
public void testUnderJettyWithMultiplePaths() throws Exception {
int port = RandomServerPortProvider.findFreePort();
myServer = new Server(port);
org.eclipse.jetty.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.servlet.ServletContextHandler();
proxyHandler.setContextPath("/ctx");
proxyHandler.addServlet(new ServletHolder(new MyServlet()), "/servlet/*");
proxyHandler.addServlet(new ServletHolder(new MyServlet()), "/foo/bar/*");
myServer.setHandler(proxyHandler);
myServer.start();
httpGet("http://localhost:" + port + "/ctx/servlet/Patient/123/_history/222");
assertEquals("Patient/123/_history/222", ourLastId.getValue());
httpGet("http://localhost:" + port + "/ctx/foo/bar/Patient/123/_history/222");
assertEquals("Patient/123/_history/222", ourLastId.getValue());
}
@Test
public void testUnderJettyWithContextRootServletRoot() throws Exception {
int port = RandomServerPortProvider.findFreePort();
String contextPath = "/";
String servletPath = "/*";
startServer(port, contextPath, servletPath);
httpGet("http://localhost:" + port + "/Patient/123/_history/222");
assertEquals("Patient/123/_history/222", ourLastId.getValue());
}
@BeforeClass
public static void beforeClass() {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
private static class MyServlet extends RestfulServer {
private static final long serialVersionUID = -8903322104434705422L;
@Override
protected void initialize() throws ServletException {
setResourceProviders(new MyPatientProvider());
}
}
public static class MyPatientProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Read(version=true)
public Patient read(@IdParam IdDt theId) {
ourLastId = theId;
Patient retVal = new Patient();
retVal.setId(theId);
return retVal;
}
}
}

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>restful-server-example</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.validation.validationbuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
</natures>
</projectDescription>

View File

@ -53,6 +53,12 @@
Resources and datatypes are now serializable. This is an
experimental feature which hasn't yet been extensively tested. Please test and give us your feedback!
</action>
<action type="add">
Switch REST server to using HttpServletRequest#getContextPath() to get
the servlet's context path. This means that the server should behave more
predictably, and should work in servlet 2.4 environments. Thanks to
Ken Zeisset for the suggestion!
</action>
</release>
<release version="1.1" date="2015-07-13">
<action type="add">