Make cache timeout configurable
This commit is contained in:
parent
48e2960c71
commit
9a050461a8
|
@ -36,5 +36,7 @@ import java.lang.annotation.Target;
|
|||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value=ElementType.METHOD)
|
||||
public @interface Metadata {
|
||||
// nothing for now
|
||||
|
||||
long cacheMillis() default 60 * 1000L;
|
||||
|
||||
}
|
||||
|
|
|
@ -2669,15 +2669,11 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
@Test
|
||||
public void testMetadata() throws Exception {
|
||||
HttpGet get = new HttpGet(ourServerBase + "/metadata");
|
||||
CloseableHttpResponse response = ourHttpClient.execute(get);
|
||||
try {
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(get)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(resp);
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
assertThat(resp, stringContainsInOrder("THIS IS THE DESC"));
|
||||
} finally {
|
||||
response.getEntity().getContent().close();
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,22 +20,14 @@ package ca.uhn.fhir.rest.server.method;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseConformance;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.rest.annotation.Metadata;
|
||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
|
@ -44,12 +36,17 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|||
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseConformance;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding {
|
||||
private static final long CACHE_MILLIS = 60 * 1000;
|
||||
|
||||
/*
|
||||
* Note: This caching mechanism should probably be configurable and maybe
|
||||
* even applicable to other bindings. It's particularly important for this
|
||||
|
@ -57,7 +54,7 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
|
|||
*/
|
||||
private final AtomicReference<IBaseResource> myCachedResponse = new AtomicReference<>();
|
||||
private final AtomicLong myCachedResponseExpires = new AtomicLong(0L);
|
||||
|
||||
private long myCacheMillis = 60 * 1000;
|
||||
|
||||
ConformanceMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
super(theMethod.getReturnType(), theMethod, theContext, theProvider);
|
||||
|
@ -68,6 +65,35 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
|
|||
throw new ConfigurationException("Conformance resource provider method '" + theMethod.getName() + "' should return a Conformance resource class, returns: " + theMethod.getReturnType());
|
||||
}
|
||||
|
||||
Metadata metadata = theMethod.getAnnotation(Metadata.class);
|
||||
if (metadata != null) {
|
||||
setCacheMillis(metadata.cacheMillis());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of milliseconds to cache the generated CapabilityStatement for. Default is one minute, and can be
|
||||
* set to 0 to never cache.
|
||||
*
|
||||
* @see #setCacheMillis(long)
|
||||
* @see Metadata#cacheMillis()
|
||||
* @since 4.1.0
|
||||
*/
|
||||
private long getCacheMillis() {
|
||||
return myCacheMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of milliseconds to cache the generated CapabilityStatement for. Default is one minute, and can be
|
||||
* set to 0 to never cache.
|
||||
*
|
||||
* @see #getCacheMillis()
|
||||
* @see Metadata#cacheMillis()
|
||||
* @since 4.1.0
|
||||
*/
|
||||
private void setCacheMillis(long theCacheMillis) {
|
||||
myCacheMillis = theCacheMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -116,8 +142,10 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
|
|||
|
||||
if (conf == null) {
|
||||
conf = (IBaseResource) invokeServerMethod(theServer, theRequest, theMethodParams);
|
||||
myCachedResponse.set(conf);
|
||||
myCachedResponseExpires.set(System.currentTimeMillis() + CACHE_MILLIS);
|
||||
if (myCacheMillis > 0) {
|
||||
myCachedResponse.set(conf);
|
||||
myCachedResponseExpires.set(System.currentTimeMillis() + getCacheMillis());
|
||||
}
|
||||
}
|
||||
|
||||
return new SimpleBundleProvider(conf);
|
||||
|
|
|
@ -1,29 +1,18 @@
|
|||
package ca.uhn.fhir.rest.server.method;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.annotation.Metadata;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.hl7.fhir.instance.model.api.IBaseConformance;
|
||||
import org.hl7.fhir.instance.model.api.IBaseMetaType;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
|
@ -31,40 +20,130 @@ public class ConformanceMethodBindingTest {
|
|||
|
||||
private FhirContext fhirContext;
|
||||
private ConformanceMethodBinding conformanceMethodBinding;
|
||||
private TestResourceProvider dummy;
|
||||
|
||||
@Before
|
||||
public void setUp() throws NoSuchMethodException {
|
||||
public void setUp() {
|
||||
fhirContext = mock(FhirContext.class);
|
||||
dummy = spy(new TestResourceProvider());
|
||||
Method method = dummy.getClass().getDeclaredMethod("getServerConformance", HttpServletRequest.class, RequestDetails.class);
|
||||
conformanceMethodBinding = new ConformanceMethodBinding(method, fhirContext, dummy);
|
||||
}
|
||||
|
||||
private <T> T init(T theCapabilityStatementProvider) throws NoSuchMethodException {
|
||||
T provider = spy(theCapabilityStatementProvider);
|
||||
Method method = provider.getClass().getDeclaredMethod("getServerConformance", HttpServletRequest.class, RequestDetails.class);
|
||||
conformanceMethodBinding = new ConformanceMethodBinding(method, fhirContext, provider);
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeServerCached() {
|
||||
public void invokeServerCached() throws NoSuchMethodException {
|
||||
TestResourceProvider provider = init(new TestResourceProvider());
|
||||
|
||||
conformanceMethodBinding.invokeServer(mock(IRestfulServer.class, RETURNS_DEEP_STUBS), mock(RequestDetails.class, RETURNS_DEEP_STUBS), new Object[]{mock(HttpServletRequest.class), mock(RequestDetails.class)});
|
||||
verify(dummy, times(1)).getServerConformance(any(), any());
|
||||
verify(provider, times(1)).getServerConformance(any(), any());
|
||||
conformanceMethodBinding.invokeServer(mock(IRestfulServer.class, RETURNS_DEEP_STUBS), mock(RequestDetails.class, RETURNS_DEEP_STUBS), new Object[]{mock(HttpServletRequest.class), mock(RequestDetails.class)});
|
||||
verify(dummy, times(1)).getServerConformance(any(), any());
|
||||
verify(provider, times(1)).getServerConformance(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeServerNotCached_ClientControlled() {
|
||||
public void invokeServerCacheExpires() throws NoSuchMethodException {
|
||||
TestResourceProviderSmallCache provider = init(new TestResourceProviderSmallCache());
|
||||
|
||||
conformanceMethodBinding.invokeServer(mock(IRestfulServer.class, RETURNS_DEEP_STUBS), mock(RequestDetails.class, RETURNS_DEEP_STUBS), new Object[]{mock(HttpServletRequest.class), mock(RequestDetails.class)});
|
||||
verify(provider, times(1)).getServerConformance(any(), any());
|
||||
|
||||
sleepAtLeast(20);
|
||||
|
||||
conformanceMethodBinding.invokeServer(mock(IRestfulServer.class, RETURNS_DEEP_STUBS), mock(RequestDetails.class, RETURNS_DEEP_STUBS), new Object[]{mock(HttpServletRequest.class), mock(RequestDetails.class)});
|
||||
verify(provider, times(2)).getServerConformance(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeServerCacheDisabled() throws NoSuchMethodException {
|
||||
TestResourceProviderNoCache provider = init(new TestResourceProviderNoCache());
|
||||
|
||||
conformanceMethodBinding.invokeServer(mock(IRestfulServer.class, RETURNS_DEEP_STUBS), mock(RequestDetails.class, RETURNS_DEEP_STUBS), new Object[]{mock(HttpServletRequest.class), mock(RequestDetails.class)});
|
||||
verify(provider, times(1)).getServerConformance(any(), any());
|
||||
|
||||
conformanceMethodBinding.invokeServer(mock(IRestfulServer.class, RETURNS_DEEP_STUBS), mock(RequestDetails.class, RETURNS_DEEP_STUBS), new Object[]{mock(HttpServletRequest.class), mock(RequestDetails.class)});
|
||||
verify(provider, times(2)).getServerConformance(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeServerCacheDisabledInSuperclass() throws NoSuchMethodException {
|
||||
TestResourceProviderNoCache2 provider = init(new TestResourceProviderNoCache2());
|
||||
|
||||
conformanceMethodBinding.invokeServer(mock(IRestfulServer.class, RETURNS_DEEP_STUBS), mock(RequestDetails.class, RETURNS_DEEP_STUBS), new Object[]{mock(HttpServletRequest.class), mock(RequestDetails.class)});
|
||||
verify(provider, times(1)).getServerConformance(any(), any());
|
||||
|
||||
// We currently don't scan the annotation on the superclass...Perhaps we should
|
||||
conformanceMethodBinding.invokeServer(mock(IRestfulServer.class, RETURNS_DEEP_STUBS), mock(RequestDetails.class, RETURNS_DEEP_STUBS), new Object[]{mock(HttpServletRequest.class), mock(RequestDetails.class)});
|
||||
verify(provider, times(1)).getServerConformance(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeServerNotCached_ClientControlled() throws NoSuchMethodException {
|
||||
TestResourceProvider provider = init(new TestResourceProvider());
|
||||
|
||||
RequestDetails requestDetails = mock(RequestDetails.class, RETURNS_DEEP_STUBS);
|
||||
when(requestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL)).thenReturn(Lists.newArrayList(Constants.CACHE_CONTROL_NO_CACHE));
|
||||
conformanceMethodBinding.invokeServer(mock(IRestfulServer.class, RETURNS_DEEP_STUBS), requestDetails, new Object[]{mock(HttpServletRequest.class), mock(RequestDetails.class)});
|
||||
verify(dummy, times(1)).getServerConformance(any(), any());
|
||||
verify(provider, times(1)).getServerConformance(any(), any());
|
||||
conformanceMethodBinding.invokeServer(mock(IRestfulServer.class, RETURNS_DEEP_STUBS), requestDetails, new Object[]{mock(HttpServletRequest.class), mock(RequestDetails.class)});
|
||||
verify(dummy, times(2)).getServerConformance(any(), any());
|
||||
verify(provider, times(2)).getServerConformance(any(), any());
|
||||
}
|
||||
|
||||
class TestResourceProvider {
|
||||
@SuppressWarnings("unused")
|
||||
static class TestResourceProvider {
|
||||
|
||||
@Metadata
|
||||
public IBaseConformance getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
|
||||
|
||||
return mock(IBaseConformance.class, RETURNS_DEEP_STUBS);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class TestResourceProviderSmallCache {
|
||||
|
||||
@Metadata(cacheMillis = 10)
|
||||
public IBaseConformance getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
|
||||
|
||||
return mock(IBaseConformance.class, RETURNS_DEEP_STUBS);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class TestResourceProviderNoCache {
|
||||
|
||||
@Metadata(cacheMillis = 0)
|
||||
public IBaseConformance getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
|
||||
|
||||
return mock(IBaseConformance.class, RETURNS_DEEP_STUBS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class TestResourceProviderNoCache2 extends TestResourceProviderNoCache {
|
||||
|
||||
// No @Metadata
|
||||
@Override
|
||||
public IBaseConformance getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
|
||||
return mock(IBaseConformance.class, RETURNS_DEEP_STUBS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void sleepAtLeast(long theMillis) {
|
||||
long start = System.currentTimeMillis();
|
||||
while (System.currentTimeMillis() <= start + theMillis) {
|
||||
try {
|
||||
long timeSinceStarted = System.currentTimeMillis() - start;
|
||||
long timeToSleep = Math.max(0, theMillis - timeSinceStarted);
|
||||
Thread.sleep(timeToSleep);
|
||||
} catch (InterruptedException theE) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.annotation.Validate;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.util.VersionUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
|
@ -28,28 +26,23 @@ import org.junit.AfterClass;
|
|||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.annotation.Validate;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.util.VersionUtil;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class MetadataConformanceDstu3Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MetadataConformanceDstu3Test.class);
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
private static RestfulServer ourServlet;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MetadataConformanceDstu3Test.class);
|
||||
|
||||
|
||||
@Test
|
||||
public void testSummary() throws Exception {
|
||||
String output;
|
||||
|
@ -61,7 +54,7 @@ public class MetadataConformanceDstu3Test {
|
|||
output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
ourLog.info(output);
|
||||
assertThat(output, containsString("<CapabilityStatement"));
|
||||
assertThat(output, containsString("<CapabilityStatement"));
|
||||
assertThat(output, stringContainsInOrder("<meta>", "SUBSETTED", "</meta>"));
|
||||
assertThat(output, not(stringContainsInOrder("searchParam")));
|
||||
} finally {
|
||||
|
@ -75,7 +68,7 @@ public class MetadataConformanceDstu3Test {
|
|||
output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
ourLog.info(output);
|
||||
assertThat(output, containsString("<CapabilityStatement"));
|
||||
assertThat(output, containsString("<CapabilityStatement"));
|
||||
assertThat(output, not(stringContainsInOrder("<meta>", "SUBSETTED", "</meta>")));
|
||||
assertThat(output, stringContainsInOrder("searchParam"));
|
||||
} finally {
|
||||
|
@ -104,38 +97,55 @@ public class MetadataConformanceDstu3Test {
|
|||
public void testHttpMethods() throws Exception {
|
||||
String output;
|
||||
|
||||
HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata");
|
||||
CloseableHttpResponse status = ourClient.execute(httpPost);
|
||||
try {
|
||||
HttpRequestBase httpOperation = new HttpGet("http://localhost:" + ourPort + "/metadata");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpOperation)) {
|
||||
output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertThat(output, containsString("<CapabilityStatement"));
|
||||
assertThat(output, containsString("<CapabilityStatement"));
|
||||
assertEquals("HAPI FHIR " + VersionUtil.getVersion() + " REST Server (FHIR Server; FHIR " + FhirVersionEnum.DSTU3.getFhirVersionString() + "/DSTU3)", status.getFirstHeader("X-Powered-By").getValue());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
|
||||
try {
|
||||
httpPost = new HttpPost("http://localhost:" + ourPort + "/metadata");
|
||||
status = ourClient.execute(httpPost);
|
||||
httpOperation = new HttpOptions("http://localhost:" + ourPort);
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpOperation)) {
|
||||
output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertThat(output, containsString("<CapabilityStatement"));
|
||||
assertEquals("HAPI FHIR " + VersionUtil.getVersion() + " REST Server (FHIR Server; FHIR " + FhirVersionEnum.DSTU3.getFhirVersionString() + "/DSTU3)", status.getFirstHeader("X-Powered-By").getValue());
|
||||
}
|
||||
|
||||
httpOperation = new HttpPost("http://localhost:" + ourPort + "/metadata");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpOperation)) {
|
||||
output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
assertEquals(405, status.getStatusLine().getStatusCode());
|
||||
assertEquals("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><issue><severity value=\"error\"/><code value=\"processing\"/><diagnostics value=\"/metadata request must use HTTP GET\"/></issue></OperationOutcome>", output);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
|
||||
/*
|
||||
* There is no @read on the RP below, so this should fail. Otherwise it
|
||||
* would be interpreted as a read on ID "metadata"
|
||||
*/
|
||||
try {
|
||||
httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/metadata");
|
||||
status = ourClient.execute(httpPost);
|
||||
output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
httpOperation = new HttpGet("http://localhost:" + ourPort + "/Patient/metadata");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpOperation)) {
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<Patient> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<Patient> search(@OptionalParam(name = "foo") StringParam theFoo) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Validate()
|
||||
public MethodOutcome validate(@ResourceParam Patient theResource) {
|
||||
return new MethodOutcome();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,7 +170,7 @@ public class MetadataConformanceDstu3Test {
|
|||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
JettyUtil.startServer(ourServer);
|
||||
ourPort = JettyUtil.getPortForStartedServer(ourServer);
|
||||
ourPort = JettyUtil.getPortForStartedServer(ourServer);
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
|
@ -169,23 +179,4 @@ public class MetadataConformanceDstu3Test {
|
|||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<Patient> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<Patient> search(@OptionalParam(name="foo") StringParam theFoo) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Validate()
|
||||
public MethodOutcome validate(@ResourceParam Patient theResource) {
|
||||
return new MethodOutcome();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -363,6 +363,10 @@
|
|||
The server CapabilityStatement (/metadata) endpoint now respects the Cache-Control header. Thanks
|
||||
to Jens Villadsen for the pull request!
|
||||
</action>
|
||||
<action type="add">
|
||||
The @Metadata annotation now has an attribute that can be used to control
|
||||
the cache timeout
|
||||
</action>
|
||||
</release>
|
||||
<release version="4.0.3" date="2019-09-03" description="Igloo (Point Release)">
|
||||
<action type="fix">
|
||||
|
|
Loading…
Reference in New Issue