Allow server to specify its own base

This commit is contained in:
James 2014-07-04 17:19:33 -04:00
parent 02357bce4d
commit 0d13dc886b
6 changed files with 222 additions and 62 deletions

View File

@ -0,0 +1,33 @@
package ca.uhn.fhir.rest.server;
import javax.servlet.http.HttpServletRequest;
/**
* Server address strategy which simply returns a hardcoded URL
*/
public class HardcodedServerAddressStrategy implements IServerAddressStrategy {
private String myValue;
public HardcodedServerAddressStrategy() {
//nothing
}
public HardcodedServerAddressStrategy(String theValue) {
myValue=theValue;
}
@Override
public String determineServerBase(HttpServletRequest theRequest) {
return myValue;
}
public String getValue() {
return myValue;
}
public void setValue(String theValue) {
myValue = theValue;
}
}

View File

@ -0,0 +1,16 @@
package ca.uhn.fhir.rest.server;
import javax.servlet.http.HttpServletRequest;
/**
* Provides the server base for a given incoming request. This can be used to account for
* multi-homed servers or other unusual network configurations.
*/
public interface IServerAddressStrategy {
/**
* Determine the server base for a given request
*/
public String determineServerBase(HttpServletRequest theRequest);
}

View File

@ -0,0 +1,46 @@
package ca.uhn.fhir.rest.server;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
/**
* Determines the server's base using the incoming request
*/
public class IncomingRequestAddressStrategy implements IServerAddressStrategy {
@Override
public String determineServerBase(HttpServletRequest theRequest) {
String requestFullPath = StringUtils.defaultString(theRequest.getRequestURI());
String servletPath = StringUtils.defaultString(theRequest.getServletPath());
StringBuffer requestUrl = theRequest.getRequestURL();
String servletContextPath = "";
if (theRequest.getServletContext() != null) {
servletContextPath = StringUtils.defaultString(theRequest.getServletContext().getContextPath());
// } else {
// servletContextPath = servletPath;
}
String requestPath = requestFullPath.substring(servletContextPath.length() + servletPath.length());
if (requestPath.length() > 0 && requestPath.charAt(0) == '/') {
requestPath = requestPath.substring(1);
}
int contextIndex;
if (servletPath.length() == 0) {
if (requestPath.length() == 0) {
contextIndex = requestUrl.length();
} else {
contextIndex = requestUrl.indexOf(requestPath);
}
} else {
contextIndex = requestUrl.indexOf(servletPath);
}
String fhirServerBase;
int length = contextIndex + servletPath.length();
fhirServerBase = requestUrl.substring(0, length);
return fhirServerBase;
}
}

View File

@ -91,13 +91,14 @@ public class RestfulServer extends HttpServlet {
private Map<String, ResourceBinding> myResourceNameToProvider = new HashMap<String, ResourceBinding>();
private Collection<IResourceProvider> myResourceProviders;
private ISecurityManager mySecurityManager;
private IServerAddressStrategy myServerAddressStrategy= new IncomingRequestAddressStrategy();
private BaseMethodBinding<?> myServerConformanceMethod;
private Object myServerConformanceProvider;
private String myServerName = "HAPI FHIR Server";
/** This is configurable but by default we just use HAPI version */
private String myServerVersion = VersionUtil.getVersion();
private boolean myStarted;
private boolean myStarted;
private boolean myUseBrowserFriendlyContentTypes;
/**
@ -289,6 +290,14 @@ public class RestfulServer extends HttpServlet {
return mySecurityManager;
}
/**
* Get the server address strategy, which is used to determine what base URL to
* provide clients to refer to this server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
*/
public IServerAddressStrategy getServerAddressStrategy() {
return myServerAddressStrategy;
}
/**
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
* (metadata) statement.
@ -393,20 +402,8 @@ public class RestfulServer extends HttpServlet {
requestPath = requestPath.substring(1);
}
int contextIndex;
if (servletPath.length() == 0) {
if (requestPath.length() == 0) {
contextIndex = requestUrl.length();
} else {
contextIndex = requestUrl.indexOf(requestPath);
}
} else {
contextIndex = requestUrl.indexOf(servletPath);
}
String fhirServerBase;
int length = contextIndex + servletPath.length();
fhirServerBase = requestUrl.substring(0, length);
fhirServerBase = myServerAddressStrategy.determineServerBase(theRequest);
if (fhirServerBase.endsWith("/")) {
fhirServerBase = fhirServerBase.substring(0, fhirServerBase.length() - 1);
@ -635,7 +632,7 @@ public class RestfulServer extends HttpServlet {
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the
* server being used.
*/
protected void initialize() {
protected void initialize() throws ServletException {
// nothing by default
}
@ -652,6 +649,13 @@ public class RestfulServer extends HttpServlet {
myImplementationDescription = theImplementationDescription;
}
/**
* Sets the paging provider to use, or <code>null</code> to use no paging (which is the default)
*/
public void setPagingProvider(IPagingProvider thePagingProvider) {
myPagingProvider = thePagingProvider;
}
// /**
// * Sets the {@link INarrativeGenerator Narrative Generator} to use when
// serializing responses from this server, or <code>null</code> (which is
@ -669,13 +673,6 @@ public class RestfulServer extends HttpServlet {
// myNarrativeGenerator = theNarrativeGenerator;
// }
/**
* Sets the paging provider to use, or <code>null</code> to use no paging (which is the default)
*/
public void setPagingProvider(IPagingProvider thePagingProvider) {
myPagingProvider = thePagingProvider;
}
/**
* Sets the non-resource specific providers which implement method calls on this server.
*
@ -724,6 +721,15 @@ public class RestfulServer extends HttpServlet {
mySecurityManager = theSecurityManager;
}
/**
* Provide a server address strategy, which is used to determine what base URL to
* provide clients to refer to this server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
*/
public void setServerAddressStrategy(IServerAddressStrategy theServerAddressStrategy) {
Validate.notNull(theServerAddressStrategy, "Server address strategy can not be null");
myServerAddressStrategy = theServerAddressStrategy;
}
/**
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
* (metadata) statement.
@ -923,6 +929,17 @@ public class RestfulServer extends HttpServlet {
return parser.setPrettyPrint(thePrettyPrint).setSuppressNarratives(theNarrativeMode == NarrativeModeEnum.SUPPRESS);
}
private static Writer getWriter(HttpServletResponse theHttpResponse, boolean theRespondGzip) throws UnsupportedEncodingException, IOException {
Writer writer;
if (theRespondGzip) {
theHttpResponse.addHeader(Constants.HEADER_CONTENT_ENCODING, Constants.ENCODING_GZIP);
writer = new OutputStreamWriter(new GZIPOutputStream(theHttpResponse.getOutputStream()), "UTF-8");
}else {
writer = theHttpResponse.getWriter();
}
return writer;
}
public static boolean prettyPrintResponse(Request theRequest) {
Map<String, String[]> requestParams = theRequest.getParameters();
String[] pretty = requestParams.remove(Constants.PARAM_PRETTY);
@ -1034,17 +1051,6 @@ public class RestfulServer extends HttpServlet {
}
}
private static Writer getWriter(HttpServletResponse theHttpResponse, boolean theRespondGzip) throws UnsupportedEncodingException, IOException {
Writer writer;
if (theRespondGzip) {
theHttpResponse.addHeader(Constants.HEADER_CONTENT_ENCODING, Constants.ENCODING_GZIP);
writer = new OutputStreamWriter(new GZIPOutputStream(theHttpResponse.getOutputStream()), "UTF-8");
}else {
writer = theHttpResponse.getWriter();
}
return writer;
}
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, boolean theRespondGzip) throws IOException {
int stausCode = 200;
streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip);

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.Collections;
import java.util.HashMap;
@ -21,6 +22,7 @@ import org.eclipse.jetty.servlet.ServletHolder;
import org.hamcrest.core.IsNot;
import org.hamcrest.core.StringContains;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@ -32,6 +34,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
@ -43,6 +46,7 @@ public class ServerFeaturesTest {
private static CloseableHttpClient ourClient;
private static int ourPort;
private static Server ourServer;
private static RestfulServer servlet;
@Test
public void testPrettyPrint() throws Exception {
@ -52,7 +56,8 @@ public class ServerFeaturesTest {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
@ -62,7 +67,8 @@ public class ServerFeaturesTest {
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_pretty=false");
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
@ -72,7 +78,8 @@ public class ServerFeaturesTest {
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_pretty=true");
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertThat(responseContent, IsNot.not(StringContains.containsString("<identifier><use")));
@ -83,21 +90,24 @@ public class ServerFeaturesTest {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", Constants.CT_FHIR_XML);
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", Constants.CT_ATOM_XML);
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", Constants.CT_FHIR_JSON);
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("\"identifier\":"));
@ -108,21 +118,24 @@ public class ServerFeaturesTest {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", "text/plain, " + Constants.CT_FHIR_XML);
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", "text/plain, " + Constants.CT_ATOM_XML);
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", "text/plain, " + Constants.CT_FHIR_JSON);
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("\"identifier\":"));
@ -134,46 +147,64 @@ public class ServerFeaturesTest {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", Constants.CT_XML);
CloseableHttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", Constants.CT_JSON);
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("\"identifier\":"));
}
@Test
public void testHardcodedAddressStrategy() throws Exception {
servlet.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://foo/bar"));
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=1");
CloseableHttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<id>http://foo/bar/Patient/1"));
}
@Test
public void testAcceptHeaderWithPrettyPrint() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", Constants.CT_FHIR_XML+ "; pretty=true");
httpGet.addHeader("Accept", Constants.CT_FHIR_XML + "; pretty=true");
CloseableHttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier>\n "));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", Constants.CT_FHIR_JSON+ "; pretty=true");
httpGet.addHeader("Accept", Constants.CT_FHIR_JSON + "; pretty=true");
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("\",\n"));
}
@Test
public void testInternalErrorIfNoId() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/?_query=findPatientsWithNoIdSpecified");
httpGet.addHeader("Accept", Constants.CT_FHIR_XML+ "; pretty=true");
httpGet.addHeader("Accept", Constants.CT_FHIR_XML + "; pretty=true");
CloseableHttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(500, status.getStatusLine().getStatusCode());
assertThat(responseContent, StringContains.containsString("ID"));
@ -185,6 +216,11 @@ public class ServerFeaturesTest {
ourServer.stop();
}
@Before
public void before() {
servlet.setServerAddressStrategy(new IncomingRequestAddressStrategy());
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
@ -193,7 +229,7 @@ public class ServerFeaturesTest {
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
servlet = new RestfulServer();
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
@ -246,7 +282,24 @@ public class ServerFeaturesTest {
return getIdToPatient().get(theId.getValue());
}
@Search(queryName="findPatientsWithNoIdSpecified")
/**
* Retrieve the resource by its identifier
*
* @param theId
* The resource identity
* @return The resource
*/
@Search()
public List<Patient> getResourceById(@RequiredParam(name = "_id") String theId) {
Patient patient = getIdToPatient().get(theId);
if (patient != null) {
return Collections.singletonList(patient);
} else {
return Collections.emptyList();
}
}
@Search(queryName = "findPatientsWithNoIdSpecified")
public List<Patient> findPatientsWithNoIdSpecified() {
Patient p = new Patient();
p.addIdentifier().setSystem("foo");

View File

@ -1,18 +1,18 @@
package ca.uhn.fhirtest;
import java.sql.DriverManager;
import java.util.Collection;
import javax.servlet.ServletException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.provider.JpaConformanceProvider;
import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
@ -25,7 +25,7 @@ public class TestRestfulServer extends RestfulServer {
private ApplicationContext myAppCtx;
@Override
protected void initialize() {
protected void initialize() throws ServletException {
super.initialize();
// try {
@ -63,6 +63,12 @@ public class TestRestfulServer extends RestfulServer {
setUseBrowserFriendlyContentTypes(true);
String baseUrl = System.getProperty("fhir.baseurl");
if (StringUtils.isBlank(baseUrl)) {
throw new ServletException("Missing system property: fhir.baseurl");
}
setServerAddressStrategy(new HardcodedServerAddressStrategy(baseUrl));
setPagingProvider(new FifoMemoryPagingProvider(10));
}