Merge branch 'master' of github.com:jamesagnew/hapi-fhir
This commit is contained in:
commit
a27acb9b79
|
@ -180,13 +180,28 @@
|
||||||
<id>SITE</id>
|
<id>SITE</id>
|
||||||
<reporting>
|
<reporting>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
<reportSets>
|
||||||
|
<reportSet>
|
||||||
|
<reports>
|
||||||
|
<report>checkstyle</report>
|
||||||
|
</reports>
|
||||||
|
</reportSet>
|
||||||
|
</reportSets>
|
||||||
|
<configuration>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<!--<plugin>
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
<artifactId>findbugs-maven-plugin</artifactId>
|
<artifactId>findbugs-maven-plugin</artifactId>
|
||||||
<version>3.0.0</version>
|
<version>3.0.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>-->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jxr-plugin</artifactId>
|
<artifactId>maven-jxr-plugin</artifactId>
|
||||||
|
|
|
@ -107,12 +107,25 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
|
||||||
}
|
}
|
||||||
final Method accessor = BeanUtils.findAccessor(declaringClass, targetReturnType, elementName);
|
final Method accessor = BeanUtils.findAccessor(declaringClass, targetReturnType, elementName);
|
||||||
if (accessor == null) {
|
if (accessor == null) {
|
||||||
throw new ConfigurationException("Could not find bean accessor/getter for property " + elementName + " on class " + declaringClass.getCanonicalName());
|
StringBuilder b = new StringBuilder();
|
||||||
|
b.append("Could not find bean accessor/getter for property ");
|
||||||
|
b.append(elementName);
|
||||||
|
b.append(" on class ");
|
||||||
|
b.append(declaringClass.getCanonicalName());
|
||||||
|
throw new ConfigurationException(b.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
final Method mutator = findMutator(declaringClass, targetReturnType, elementName);
|
final Method mutator = findMutator(declaringClass, targetReturnType, elementName);
|
||||||
if (mutator == null) {
|
if (mutator == null) {
|
||||||
throw new ConfigurationException("Could not find bean mutator/setter for property " + elementName + " on class " + declaringClass.getCanonicalName() + " (expected return type " + targetReturnType.getCanonicalName() + ")");
|
StringBuilder b = new StringBuilder();
|
||||||
|
b.append("Could not find bean mutator/setter for property ");
|
||||||
|
b.append(elementName);
|
||||||
|
b.append(" on class ");
|
||||||
|
b.append(declaringClass.getCanonicalName());
|
||||||
|
b.append(" (expected return type ");
|
||||||
|
b.append(targetReturnType.getCanonicalName());
|
||||||
|
b.append(")");
|
||||||
|
throw new ConfigurationException(b.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (List.class.isAssignableFrom(targetReturnType)) {
|
if (List.class.isAssignableFrom(targetReturnType)) {
|
||||||
|
@ -134,6 +147,7 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
|
||||||
return myAccessor;
|
return myAccessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getElementName() {
|
public String getElementName() {
|
||||||
return myElementName;
|
return myElementName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,11 @@ public class FhirContext {
|
||||||
throw new IllegalStateException(getLocalizer().getMessage(FhirContext.class, "noStructures"));
|
throw new IllegalStateException(getLocalizer().getMessage(FhirContext.class, "noStructures"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (theVersion == null) {
|
||||||
|
ourLog.info("Creating new FhirContext with auto-detected version [{}]. It is recommended to explicitly select a version for future compatibility by invoking FhirContext.forDstuX()", myVersion.getVersion().name());
|
||||||
|
} else {
|
||||||
ourLog.info("Creating new FHIR context for FHIR version [{}]", myVersion.getVersion().name());
|
ourLog.info("Creating new FHIR context for FHIR version [{}]", myVersion.getVersion().name());
|
||||||
|
}
|
||||||
|
|
||||||
scanResourceTypes(toElementList(theResourceTypes));
|
scanResourceTypes(toElementList(theResourceTypes));
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,18 +32,22 @@ public class Constants {
|
||||||
public static final Charset CHARSET_UTF8;
|
public static final Charset CHARSET_UTF8;
|
||||||
public static final String CHARSETNAME_UTF_8 = "UTF-8";
|
public static final String CHARSETNAME_UTF_8 = "UTF-8";
|
||||||
public static final String CT_ATOM_XML = "application/atom+xml";
|
public static final String CT_ATOM_XML = "application/atom+xml";
|
||||||
|
|
||||||
public static final String CT_FHIR_JSON = "application/json+fhir";
|
public static final String CT_FHIR_JSON = "application/json+fhir";
|
||||||
|
|
||||||
|
public static final String CTSUFFIX_CHARSET_UTF8 = "; charset=" + CHARSETNAME_UTF_8;
|
||||||
public static final String CT_FHIR_XML = "application/xml+fhir";
|
public static final String CT_FHIR_XML = "application/xml+fhir";
|
||||||
public static final String CT_HTML = "text/html";
|
public static final String CT_HTML = "text/html";
|
||||||
public static final String CT_HTML_WITH_UTF8 = "text/html; charset=UTF-8";
|
public static final String CT_HTML_WITH_UTF8 = "text/html" + CTSUFFIX_CHARSET_UTF8;
|
||||||
public static final String CT_JSON = "application/json";
|
public static final String CT_JSON = "application/json";
|
||||||
public static final String CT_OCTET_STREAM = "application/octet-stream";
|
public static final String CT_OCTET_STREAM = "application/octet-stream";
|
||||||
public static final String CT_TEXT = "text/plain";
|
public static final String CT_TEXT = "text/plain";
|
||||||
public static final String CT_TEXT_WITH_UTF8 = CT_TEXT + "; charset=UTF-8";
|
public static final String CT_TEXT_WITH_UTF8 = CT_TEXT + CTSUFFIX_CHARSET_UTF8;
|
||||||
public static final String CT_XML = "application/xml";
|
public static final String CT_XML = "application/xml";
|
||||||
public static final String ENCODING_GZIP = "gzip";
|
public static final String ENCODING_GZIP = "gzip";
|
||||||
public static final String EXTOP_VALIDATE = "$validate";
|
public static final String EXTOP_VALIDATE = "$validate";
|
||||||
|
public static final String EXTOP_VALIDATE_MODE = "mode";
|
||||||
|
public static final String EXTOP_VALIDATE_PROFILE = "profile";
|
||||||
|
public static final String EXTOP_VALIDATE_RESOURCE = "resource";
|
||||||
public static final String FORMAT_JSON = "json";
|
public static final String FORMAT_JSON = "json";
|
||||||
public static final Set<String> FORMAT_VAL_JSON;
|
public static final Set<String> FORMAT_VAL_JSON;
|
||||||
public static final Map<String, EncodingEnum> FORMAT_VAL_TO_ENCODING;
|
public static final Map<String, EncodingEnum> FORMAT_VAL_TO_ENCODING;
|
||||||
|
@ -128,9 +132,6 @@ public class Constants {
|
||||||
public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501;
|
public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501;
|
||||||
public static final String URL_TOKEN_HISTORY = "_history";
|
public static final String URL_TOKEN_HISTORY = "_history";
|
||||||
public static final String URL_TOKEN_METADATA = "metadata";
|
public static final String URL_TOKEN_METADATA = "metadata";
|
||||||
public static final String EXTOP_VALIDATE_MODE = "mode";
|
|
||||||
public static final String EXTOP_VALIDATE_PROFILE = "profile";
|
|
||||||
public static final String EXTOP_VALIDATE_RESOURCE = "resource";
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();
|
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();
|
||||||
|
|
|
@ -600,7 +600,6 @@ public class RestfulServer extends HttpServlet {
|
||||||
operation = Constants.PARAM_HISTORY;
|
operation = Constants.PARAM_HISTORY;
|
||||||
}
|
}
|
||||||
} else if (partIsOperation(nextString)) {
|
} else if (partIsOperation(nextString)) {
|
||||||
// FIXME: this would be untrue for _meta/_delete
|
|
||||||
if (operation != null) {
|
if (operation != null) {
|
||||||
throw new InvalidRequestException("URL Path contains two operations: " + requestPath);
|
throw new InvalidRequestException("URL Path contains two operations: " + requestPath);
|
||||||
}
|
}
|
||||||
|
@ -635,8 +634,6 @@ public class RestfulServer extends HttpServlet {
|
||||||
requestDetails.setSecondaryOperation(secondaryOperation);
|
requestDetails.setSecondaryOperation(secondaryOperation);
|
||||||
requestDetails.setCompartmentName(compartment);
|
requestDetails.setCompartmentName(compartment);
|
||||||
|
|
||||||
// TODO: look for more tokens for version, compartments, etc...
|
|
||||||
|
|
||||||
String acceptEncoding = theRequest.getHeader(Constants.HEADER_ACCEPT_ENCODING);
|
String acceptEncoding = theRequest.getHeader(Constants.HEADER_ACCEPT_ENCODING);
|
||||||
boolean respondGzip = false;
|
boolean respondGzip = false;
|
||||||
if (acceptEncoding != null) {
|
if (acceptEncoding != null) {
|
||||||
|
@ -819,7 +816,6 @@ public class RestfulServer extends HttpServlet {
|
||||||
* (which extends {@link ServletException}), as this is a flag to the servlet container that the servlet
|
* (which extends {@link ServletException}), as this is a flag to the servlet container that the servlet
|
||||||
* is not usable.
|
* is not usable.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
|
||||||
protected void initialize() throws ServletException {
|
protected void initialize() throws ServletException {
|
||||||
// nothing by default
|
// nothing by default
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.zip.GZIPOutputStream;
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
import javax.servlet.ServletOutputStream;
|
import javax.servlet.ServletOutputStream;
|
||||||
|
@ -59,6 +61,8 @@ import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
|
||||||
public class RestfulServerUtils {
|
public class RestfulServerUtils {
|
||||||
|
static final Pattern ACCEPT_HEADER_PATTERN = Pattern.compile("\\s*([a-zA-Z0-9+.*/-]+)\\s*(;\\s*([a-zA-Z]+)\\s*=\\s*([a-zA-Z0-9.]+)\\s*)?(,?)");
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServerUtils.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServerUtils.class);
|
||||||
|
|
||||||
public static void addProfileToBundleEntry(FhirContext theContext, IBaseResource theResource, String theServerBase) {
|
public static void addProfileToBundleEntry(FhirContext theContext, IBaseResource theResource, String theServerBase) {
|
||||||
|
@ -188,28 +192,52 @@ public class RestfulServerUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Accept header is kind of ridiculous, e.g.
|
||||||
|
*/
|
||||||
|
// text/xml, application/xml, application/xhtml+xml, text/html;q=0.9, text/plain;q=0.8, image/png, */*;q=0.5
|
||||||
|
|
||||||
Enumeration<String> acceptValues = theReq.getHeaders(Constants.HEADER_ACCEPT);
|
Enumeration<String> acceptValues = theReq.getHeaders(Constants.HEADER_ACCEPT);
|
||||||
if (acceptValues != null) {
|
if (acceptValues != null) {
|
||||||
|
float bestQ = -1f;
|
||||||
|
EncodingEnum retVal = null;
|
||||||
while (acceptValues.hasMoreElements()) {
|
while (acceptValues.hasMoreElements()) {
|
||||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
String nextAcceptHeaderValue = acceptValues.nextElement();
|
||||||
if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) {
|
Matcher m = ACCEPT_HEADER_PATTERN.matcher(nextAcceptHeaderValue);
|
||||||
for (String nextPart : nextAcceptHeaderValue.split(",")) {
|
float q = 1.0f;
|
||||||
int scIdx = nextPart.indexOf(';');
|
while (m.find()) {
|
||||||
if (scIdx == 0) {
|
String contentTypeGroup = m.group(1);
|
||||||
continue;
|
EncodingEnum encoding = Constants.FORMAT_VAL_TO_ENCODING.get(contentTypeGroup);
|
||||||
|
if (encoding != null) {
|
||||||
|
|
||||||
|
String name = m.group(3);
|
||||||
|
String value = m.group(4);
|
||||||
|
if (name != null && value != null) {
|
||||||
|
if ("q".equals(name)) {
|
||||||
|
try {
|
||||||
|
q = Float.parseFloat(value);
|
||||||
|
q = Math.max(q, 0.0f);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
ourLog.debug("Invalid Accept header q value: {}", value);
|
||||||
}
|
}
|
||||||
if (scIdx != -1) {
|
|
||||||
nextPart = nextPart.substring(0, scIdx);
|
|
||||||
}
|
}
|
||||||
nextPart = nextPart.trim();
|
}
|
||||||
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextPart);
|
}
|
||||||
if (retVal != null) {
|
|
||||||
|
if (q > bestQ) {
|
||||||
|
retVal = encoding;
|
||||||
|
bestQ = q;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!",".equals(m.group(5))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,3 +129,4 @@ local.properties
|
||||||
|
|
||||||
/target/
|
/target/
|
||||||
/target/
|
/target/
|
||||||
|
/target/
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package ca.uhn.fhir;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
|
|
||||||
|
public class Dstu1EnvTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCorrectDefault() {
|
||||||
|
FhirContext ctx = new FhirContext();
|
||||||
|
assertEquals("new FhirContext() is creating a context with the wrong FHIR versions. Something is probably wrong with the classpath.", FhirVersionEnum.DSTU1, ctx.getVersion().getVersion());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.Header;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
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.ServletHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||||
|
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.PortUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public class AcceptHeaderTest {
|
||||||
|
|
||||||
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static int ourPort;
|
||||||
|
private static Server ourServer;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadNoHeader() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(Constants.CT_FHIR_XML + Constants.CTSUFFIX_CHARSET_UTF8, status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadXmlHeaderHigherPriority() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||||
|
httpGet.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML + "; q=1.0, " + Constants.CT_FHIR_JSON + "; q=0.9");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(Constants.CT_FHIR_XML + Constants.CTSUFFIX_CHARSET_UTF8, status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadXmlHeaderHigherPriorityWithStar() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||||
|
httpGet.addHeader(Constants.HEADER_ACCEPT, "*/*; q=1.0, " + Constants.CT_FHIR_XML + "; q=1.0, " + Constants.CT_FHIR_JSON + "; q=0.9");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(Constants.CT_FHIR_XML + Constants.CTSUFFIX_CHARSET_UTF8, status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadXmlHeaderHigherPriorityBackwards() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||||
|
httpGet.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON + "; q=0.9, " + Constants.CT_FHIR_XML + "; q=1.0");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(Constants.CT_FHIR_XML + Constants.CTSUFFIX_CHARSET_UTF8, status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||||
|
|
||||||
|
// Now with spaces
|
||||||
|
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||||
|
httpGet.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON + "; q = 000000.9 , " + Constants.CT_FHIR_XML + " ; q = 1.0 ,");
|
||||||
|
status = ourClient.execute(httpGet);
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(Constants.CT_FHIR_XML + Constants.CTSUFFIX_CHARSET_UTF8, status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = PortUtil.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
|
RestfulServer servlet = new RestfulServer();
|
||||||
|
servlet.setResourceProviders(new PatientProvider());
|
||||||
|
servlet.setDefaultResponseEncoding(EncodingEnum.XML);
|
||||||
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
ourServer.setHandler(proxyHandler);
|
||||||
|
ourServer.start();
|
||||||
|
|
||||||
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
|
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||||
|
builder.setConnectionManager(connectionManager);
|
||||||
|
ourClient = builder.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public static class PatientProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@Read(version = true)
|
||||||
|
public Patient read(@IdParam IdDt theId) {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier(theId.getIdPart(), theId.getVersionIdPart());
|
||||||
|
patient.setId("Patient/1/_history/1");
|
||||||
|
return patient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IResource> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class RestfulServerUtilsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAcceptPattern() {
|
||||||
|
Matcher m = RestfulServerUtils.ACCEPT_HEADER_PATTERN.matcher("application/json+fhir");
|
||||||
|
assertTrue(m.find());
|
||||||
|
assertEquals("application/json+fhir", m.group(0));
|
||||||
|
assertEquals("application/json+fhir", m.group(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -88,7 +88,7 @@ public class ServerFeaturesTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAcceptHeader() throws Exception {
|
public void testAcceptHeaderXml() throws Exception {
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||||
httpGet.addHeader("Accept", Constants.CT_FHIR_XML);
|
httpGet.addHeader("Accept", Constants.CT_FHIR_XML);
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
@ -97,18 +97,28 @@ public class ServerFeaturesTest {
|
||||||
|
|
||||||
assertThat(responseContent, StringContains.containsString("<identifier><use"));
|
assertThat(responseContent, StringContains.containsString("<identifier><use"));
|
||||||
|
|
||||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAcceptHeaderAtom() throws Exception {
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||||
httpGet.addHeader("Accept", Constants.CT_ATOM_XML);
|
httpGet.addHeader("Accept", Constants.CT_ATOM_XML);
|
||||||
status = ourClient.execute(httpGet);
|
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||||
responseContent = IOUtils.toString(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
assertThat(responseContent, StringContains.containsString("<identifier><use"));
|
assertThat(responseContent, StringContains.containsString("<identifier><use"));
|
||||||
|
|
||||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAcceptHeaderJson() throws Exception {
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||||
httpGet.addHeader("Accept", Constants.CT_FHIR_JSON);
|
httpGet.addHeader("Accept", Constants.CT_FHIR_JSON);
|
||||||
status = ourClient.execute(httpGet);
|
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||||
responseContent = IOUtils.toString(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
assertThat(responseContent, StringContains.containsString("\"identifier\":"));
|
assertThat(responseContent, StringContains.containsString("\"identifier\":"));
|
||||||
|
@ -118,7 +128,6 @@ public class ServerFeaturesTest {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAcceptHeaderWithMultiple() throws Exception {
|
public void testAcceptHeaderWithMultiple() throws Exception {
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package ca.uhn.fhir;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
|
|
||||||
|
public class Dstu2EnvTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCorrectDefault() {
|
||||||
|
FhirContext ctx = new FhirContext();
|
||||||
|
assertEquals("new FhirContext() is creating a context with the wrong FHIR versions. Something is probably wrong with the classpath.", FhirVersionEnum.DSTU2, ctx.getVersion().getVersion());
|
||||||
|
}
|
||||||
|
}
|
20
pom.xml
20
pom.xml
|
@ -230,8 +230,8 @@
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>1.6</source>
|
<source>1.6</source>
|
||||||
<target>1.6</target>
|
<target>1.6</target>
|
||||||
<!-- <compilerId>javac-with-errorprone</compilerId>
|
<compilerId>javac-with-errorprone</compilerId>
|
||||||
<forceJavacCompilerUse>true</forceJavacCompilerUse> -->
|
<forceJavacCompilerUse>true</forceJavacCompilerUse>
|
||||||
</configuration>
|
</configuration>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -258,7 +258,6 @@
|
||||||
<configuration>
|
<configuration>
|
||||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||||
<runOrder>random</runOrder>
|
<runOrder>random</runOrder>
|
||||||
<!--<argLine>-Dfile.encoding=ISO-8859-1</argLine> -->
|
|
||||||
<argLine>-Dfile.encoding=UTF-8</argLine>
|
<argLine>-Dfile.encoding=UTF-8</argLine>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
@ -426,6 +425,21 @@
|
||||||
</lifecycleMappingMetadata>
|
</lifecycleMappingMetadata>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
<version>2.15</version>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.puppycrawl.tools</groupId>
|
||||||
|
<artifactId>checkstyle</artifactId>
|
||||||
|
<version>6.7</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<configuration>
|
||||||
|
<configLocation>src/checkstyle/checkstyle.xml</configLocation>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</pluginManagement>
|
</pluginManagement>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|
|
@ -98,6 +98,10 @@
|
||||||
in DSTU2 mode. Thanks to Eric from the FHIR Skype Implementers chat for
|
in DSTU2 mode. Thanks to Eric from the FHIR Skype Implementers chat for
|
||||||
reporting.
|
reporting.
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Server now supports complete Accept header content negotiation, including
|
||||||
|
q values specifying order of preference. Previously the q value was ignored.
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.0" date="2015-May-8">
|
<release version="1.0" date="2015-May-8">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
|
@ -0,0 +1,204 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE module PUBLIC
|
||||||
|
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||||
|
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Checkstyle configuration that checks the Google coding conventions from:
|
||||||
|
|
||||||
|
- Google Java Style
|
||||||
|
https://google-styleguide.googlecode.com/svn-history/r130/trunk/javaguide.html
|
||||||
|
|
||||||
|
Checkstyle is very configurable. Be sure to read the documentation at
|
||||||
|
http://checkstyle.sf.net (or in your downloaded distribution).
|
||||||
|
|
||||||
|
Most Checks are configurable, be sure to consult the documentation.
|
||||||
|
|
||||||
|
To completely disable a check, just comment it out or delete it from the file.
|
||||||
|
|
||||||
|
Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<module name = "Checker">
|
||||||
|
<property name="charset" value="UTF-8"/>
|
||||||
|
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
|
||||||
|
<!--<property name="fileExtensions" value="java, properties, xml"/>-->
|
||||||
|
|
||||||
|
<module name="TreeWalker">
|
||||||
|
<property name="tabWidth" value="3"/>
|
||||||
|
<module name="OuterTypeFilename"/>
|
||||||
|
<module name="IllegalTokenText">
|
||||||
|
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||||
|
<property name="format" value="\\u00(08|09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
|
||||||
|
<property name="message" value="Avoid using corresponding octal or Unicode escape."/>
|
||||||
|
</module>
|
||||||
|
<module name="AvoidEscapedUnicodeCharacters">
|
||||||
|
<property name="allowEscapesForControlCharacters" value="true"/>
|
||||||
|
<property name="allowByTailComment" value="true"/>
|
||||||
|
<property name="allowNonPrintableEscapes" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="LineLength">
|
||||||
|
<property name="max" value="200"/>
|
||||||
|
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
|
||||||
|
</module>
|
||||||
|
<module name="AvoidStarImport"/>
|
||||||
|
<module name="OneTopLevelClass"/>
|
||||||
|
<module name="NoLineWrap"/>
|
||||||
|
<module name="EmptyBlock">
|
||||||
|
<property name="option" value="TEXT"/>
|
||||||
|
<property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
|
||||||
|
</module>
|
||||||
|
<module name="NeedBraces"/>
|
||||||
|
<module name="LeftCurly">
|
||||||
|
<property name="maxLineLength" value="200"/>
|
||||||
|
</module>
|
||||||
|
<module name="RightCurly"/>
|
||||||
|
<module name="RightCurly">
|
||||||
|
<property name="option" value="alone"/>
|
||||||
|
<property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO, STATIC_INIT, INSTANCE_INIT"/>
|
||||||
|
</module>
|
||||||
|
<module name="WhitespaceAround">
|
||||||
|
<property name="allowEmptyConstructors" value="true"/>
|
||||||
|
<property name="allowEmptyMethods" value="true"/>
|
||||||
|
<property name="allowEmptyTypes" value="true"/>
|
||||||
|
<property name="allowEmptyLoops" value="true"/>
|
||||||
|
<message key="ws.notFollowed"
|
||||||
|
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
|
||||||
|
<message key="ws.notPreceded"
|
||||||
|
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
|
||||||
|
</module>
|
||||||
|
<module name="OneStatementPerLine"/>
|
||||||
|
<module name="MultipleVariableDeclarations"/>
|
||||||
|
<module name="ArrayTypeStyle"/>
|
||||||
|
<module name="MissingSwitchDefault"/>
|
||||||
|
<module name="FallThrough"/>
|
||||||
|
<module name="UpperEll"/>
|
||||||
|
<module name="ModifierOrder"/>
|
||||||
|
<module name="EmptyLineSeparator">
|
||||||
|
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<property name="tokens" value="DOT"/>
|
||||||
|
<property name="option" value="nl"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<property name="tokens" value="COMMA"/>
|
||||||
|
<property name="option" value="EOL"/>
|
||||||
|
</module>
|
||||||
|
<module name="PackageName">
|
||||||
|
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Package name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="TypeName">
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="MemberName">
|
||||||
|
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Member name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="ParameterName">
|
||||||
|
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="LocalVariableName">
|
||||||
|
<property name="tokens" value="VARIABLE_DEF"/>
|
||||||
|
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
|
||||||
|
<property name="allowOneCharVarInForLoop" value="true"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="ClassTypeParameterName">
|
||||||
|
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Class type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="MethodTypeParameterName">
|
||||||
|
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Method type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="NoFinalizer"/>
|
||||||
|
<module name="GenericWhitespace">
|
||||||
|
<message key="ws.followed"
|
||||||
|
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
|
||||||
|
<message key="ws.preceded"
|
||||||
|
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
|
||||||
|
<message key="ws.illegalFollow"
|
||||||
|
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
|
||||||
|
<message key="ws.notPreceded"
|
||||||
|
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
|
||||||
|
</module>
|
||||||
|
<module name="UncommentedMain"/>
|
||||||
|
<module name="Indentation">
|
||||||
|
<property name="basicOffset" value="3"/>
|
||||||
|
<property name="braceAdjustment" value="0"/>
|
||||||
|
<property name="caseIndent" value="3"/>
|
||||||
|
<property name="throwsIndent" value="6"/>
|
||||||
|
<property name="lineWrappingIndentation" value="6"/>
|
||||||
|
<property name="arrayInitIndent" value="3"/>
|
||||||
|
</module>
|
||||||
|
<module name="AbbreviationAsWordInName">
|
||||||
|
<property name="ignoreFinal" value="false"/>
|
||||||
|
<property name="allowedAbbreviationLength" value="1"/>
|
||||||
|
</module>
|
||||||
|
<module name="OverloadMethodsDeclarationOrder"/>
|
||||||
|
<module name="VariableDeclarationUsageDistance"/>
|
||||||
|
<!--
|
||||||
|
<module name="CustomImportOrder">
|
||||||
|
<property name="specialImportsRegExp" value="com.google"/>
|
||||||
|
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
||||||
|
<property name="customImportOrderRules" value="STATIC###SPECIAL_IMPORTS###THIRD_PARTY_PACKAGE###STANDARD_JAVA_PACKAGE"/>
|
||||||
|
</module>
|
||||||
|
-->
|
||||||
|
<module name="MethodParamPad"/>
|
||||||
|
<module name="OperatorWrap">
|
||||||
|
<property name="option" value="NL"/>
|
||||||
|
<property name="tokens" value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR "/>
|
||||||
|
</module>
|
||||||
|
<module name="AnnotationLocation">
|
||||||
|
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="AnnotationLocation">
|
||||||
|
<property name="tokens" value="VARIABLE_DEF"/>
|
||||||
|
<property name="allowSamelineMultipleAnnotations" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="NonEmptyAtclauseDescription"/>
|
||||||
|
<module name="JavadocTagContinuationIndentation"/>
|
||||||
|
<module name="SummaryJavadocCheck">
|
||||||
|
<property name="forbiddenSummaryFragments" value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
|
||||||
|
</module>
|
||||||
|
<module name="JavadocParagraph"/>
|
||||||
|
<module name="AtclauseOrder">
|
||||||
|
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||||
|
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="JavadocMethod">
|
||||||
|
<property name="scope" value="public"/>
|
||||||
|
<property name="allowMissingParamTags" value="true"/>
|
||||||
|
<property name="allowMissingThrowsTags" value="true"/>
|
||||||
|
<property name="allowMissingReturnTag" value="true"/>
|
||||||
|
<property name="minLineCount" value="2"/>
|
||||||
|
<property name="allowedAnnotations" value="Override, Test"/>
|
||||||
|
<property name="allowThrowsTagsForSubclasses" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="MethodName">
|
||||||
|
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Method name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="SingleLineJavadoc"/>
|
||||||
|
<!--<property name="ignoreInlineTags" value="false"/>-->
|
||||||
|
<!--<module name="EmptyCatchBlock">
|
||||||
|
<property name="exceptionVariableName" value="expected"/>
|
||||||
|
</module>-->
|
||||||
|
</module>
|
||||||
|
</module>
|
||||||
|
|
Loading…
Reference in New Issue