diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index d4085fca4ecc..1bdd3b18d478 100644 --- a/hapi-fhir-client-okhttp/pom.xml +++ b/hapi-fhir-client-okhttp/pom.xml @@ -79,11 +79,6 @@ jetty-servlet test - - org.jboss.resteasy - resteasy-jaxrs - test - org.jboss.resteasy resteasy-client diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_7_0/changes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_7_0/changes.yaml index 8f9f59d7b94c..a8c23850b6fc 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_7_0/changes.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_7_0/changes.yaml @@ -25,6 +25,7 @@
  • Jetty Server (CLI): 9.4.43.v20210629 -> 9.4.44.v20210927
  • Spring Boot (Boot): 2.5.0 -> 2.6.2
  • Swagger UI (OpenAPI): 3.46.0 -> 4.1.3
  • +
  • Resteasy (JAX-RS): 4.0.0.Beta3 -> 5.0.2.Final
  • " diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 4e86e70a4612..460d69725174 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -72,11 +72,6 @@ ${project.version}
    - - javax.ws.rs - javax.ws.rs-api - provided - javax.ejb ejb-api @@ -87,6 +82,11 @@ javax.servlet-api provided + + org.jboss.spec.javax.ws.rs + jboss-jaxrs-api_2.1_spec + provided + javax.annotation @@ -122,11 +122,6 @@ - - org.jboss.resteasy - resteasy-jaxrs - test - org.jboss.resteasy resteasy-client @@ -162,7 +157,7 @@ test - + diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpClient.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpClient.java index 6a1445b46cfe..beff27524a59 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpClient.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpClient.java @@ -20,27 +20,31 @@ * #L% */ -import java.util.List; -import java.util.Map; - +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.client.api.Header; +import ca.uhn.fhir.rest.client.api.HttpClientUtil; +import ca.uhn.fhir.rest.client.api.IHttpClient; +import ca.uhn.fhir.rest.client.api.IHttpRequest; +import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation; +import ca.uhn.fhir.rest.client.method.MethodUtil; import javax.ws.rs.client.Client; import javax.ws.rs.client.Entity; import javax.ws.rs.client.Invocation.Builder; -import javax.ws.rs.core.*; - +import javax.ws.rs.core.Form; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; import org.hl7.fhir.instance.model.api.IBaseBinary; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.client.api.*; -import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation; -import ca.uhn.fhir.rest.client.method.MethodUtil; -import ca.uhn.fhir.rest.server.RestfulServerUtils; +import java.util.List; +import java.util.Map; /** * A Http Request based on JaxRs. This is an adapter around the class * {@link javax.ws.rs.client.Client Client} - * + * * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare */ public class JaxRsHttpClient implements IHttpClient { @@ -52,8 +56,8 @@ public class JaxRsHttpClient implements IHttpClient { private String myIfNoneExistString; private RequestTypeEnum myRequestType; - public JaxRsHttpClient(Client theClient, StringBuilder theUrl, Map> theIfNoneExistParams, String theIfNoneExistString, - RequestTypeEnum theRequestType, List
    theHeaders) { + public JaxRsHttpClient(Client theClient, StringBuilder theUrl, Map> theIfNoneExistParams, String theIfNoneExistString, + RequestTypeEnum theRequestType, List
    theHeaders) { this.myClient = theClient; this.myUrl = theUrl; this.myIfNoneExistParams = theIfNoneExistParams; @@ -82,7 +86,7 @@ public IHttpRequest createParamRequest(FhirContext theContext, Map entity = Entity.form(map); JaxRsHttpRequest retVal = createHttpRequest(entity); - addHeadersToRequest(retVal, theEncoding, theContext); + addHeadersToRequest(retVal, theEncoding, theContext); return retVal; } @@ -110,13 +114,13 @@ public void addHeadersToRequest(JaxRsHttpRequest theHttpRequest, EncodingEnum th theHttpRequest.addHeader("User-Agent", HttpClientUtil.createUserAgentString(theContext, "jax-rs")); theHttpRequest.addHeader("Accept-Charset", "utf-8"); - + Builder request = theHttpRequest.getRequest(); request.acceptEncoding("gzip"); MethodUtil.addAcceptHeaderToRequest(theEncoding, theHttpRequest, theContext); } - + private JaxRsHttpRequest createHttpRequest(Entity entity) { Builder request = myClient.target(myUrl.toString()).request(); JaxRsHttpRequest result = new JaxRsHttpRequest(request, myRequestType, entity); diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java index 1ee23a88ef37..3c138e9bab19 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java @@ -26,6 +26,8 @@ import javax.interceptor.Interceptors; import javax.ws.rs.*; + +import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java index 7a0385f3c3c0..c88df0fa98c8 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java @@ -20,54 +20,77 @@ * #L% */ -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.*; -import java.util.Map.Entry; -import java.util.concurrent.ConcurrentHashMap; - -import javax.annotation.PostConstruct; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder; +import ca.uhn.fhir.rest.annotation.IdParam; +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.SummaryEnum; +import ca.uhn.fhir.rest.api.server.IRestfulResponse; +import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; +import ca.uhn.fhir.rest.server.IResourceProvider; +import ca.uhn.fhir.rest.server.ResourceBinding; +import ca.uhn.fhir.rest.server.RestfulServerConfiguration; +import ca.uhn.fhir.rest.server.method.BaseMethodBinding; import ca.uhn.fhir.rest.server.provider.ServerCapabilityStatementProvider; +import ca.uhn.fhir.util.ReflectionUtil; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu2.hapi.rest.server.ServerConformanceProvider; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CapabilityStatement; import org.slf4j.LoggerFactory; -import ca.uhn.fhir.context.*; -import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.api.server.IRestfulResponse; -import ca.uhn.fhir.rest.server.*; -import ca.uhn.fhir.rest.server.method.BaseMethodBinding; -import ca.uhn.fhir.util.ReflectionUtil; -import java.util.stream.Collectors; +import javax.annotation.PostConstruct; +import javax.ws.rs.GET; +import javax.ws.rs.OPTIONS; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * This is the conformance provider for the jax rs servers. It requires all providers to be registered during startup because the conformance profile is generated during the postconstruct phase. - * + * * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare */ -@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) +@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProvider implements IResourceProvider { - /** the logger */ + /** + * the logger + */ private static final org.slf4j.Logger ourLog = LoggerFactory.getLogger(AbstractJaxRsConformanceProvider.class); - /** the server bindings */ + /** + * the server bindings + */ private ResourceBinding myServerBinding = new ResourceBinding(); - /** the resource bindings */ + /** + * the resource bindings + */ private ConcurrentHashMap myResourceNameToBinding = new ConcurrentHashMap(); - /** the server configuration */ + /** + * the server configuration + */ private RestfulServerConfiguration serverConfiguration = new RestfulServerConfiguration(); - /** the conformance. It is created once during startup */ + /** + * the conformance. It is created once during startup + */ private org.hl7.fhir.r4.model.CapabilityStatement myR4CapabilityStatement; private org.hl7.fhir.dstu3.model.CapabilityStatement myDstu3CapabilityStatement; private org.hl7.fhir.dstu2016may.model.Conformance myDstu2_1Conformance; @@ -77,13 +100,10 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv /** * Constructor allowing the description, servername and server to be set - * - * @param implementationDescription - * the implementation description. If null, "" is used - * @param serverName - * the server name. If null, "" is used - * @param serverVersion - * the server version. If null, "" is used + * + * @param implementationDescription the implementation description. If null, "" is used + * @param serverName the server name. If null, "" is used + * @param serverVersion the server version. If null, "" is used */ protected AbstractJaxRsConformanceProvider(String implementationDescription, String serverName, String serverVersion) { serverConfiguration.setFhirContext(getFhirContext()); @@ -94,15 +114,11 @@ protected AbstractJaxRsConformanceProvider(String implementationDescription, Str /** * Constructor allowing the description, servername and server to be set - * - * @param ctx - * the {@link FhirContext} instance. - * @param implementationDescription - * the implementation description. If null, "" is used - * @param serverName - * the server name. If null, "" is used - * @param serverVersion - * the server version. If null, "" is used + * + * @param ctx the {@link FhirContext} instance. + * @param implementationDescription the implementation description. If null, "" is used + * @param serverName the server name. If null, "" is used + * @param serverVersion the server version. If null, "" is used */ protected AbstractJaxRsConformanceProvider(FhirContext ctx, String implementationDescription, String serverName, String serverVersion) { super(ctx); @@ -167,15 +183,15 @@ protected synchronized void setUpPostConstruct() { /** * This method must return all the resource providers which need to be included in the conformance - * + * * @return a map of the resource providers and their corresponding classes. This class needs to be given explicitly because retrieving the interface using {@link Object#getClass()} may not give the - * correct interface in a jee environment. + * correct interface in a jee environment. */ protected abstract ConcurrentHashMap, IResourceProvider> getProviders(); /** * This method will retrieve the conformance using the http OPTIONS method - * + * * @return the response containing the conformance */ @OPTIONS @@ -186,7 +202,7 @@ public Response conformanceUsingOptions() throws IOException { /** * This method will retrieve the conformance using the http GET method - * + * * @return the response containing the conformance */ @GET @@ -197,7 +213,7 @@ public Response conformance() throws IOException { Builder request = getRequest(RequestTypeEnum.OPTIONS, RestOperationTypeEnum.METADATA); IRestfulResponse response = request.build().getResponse(); response.addHeader(Constants.HEADER_CORS_ALLOW_ORIGIN, "*"); - + IBaseResource conformance; FhirVersionEnum fhirContextVersion = super.getFhirContext().getVersion().getVersion(); switch (fhirContextVersion) { @@ -219,7 +235,7 @@ public Response conformance() throws IOException { default: throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion); } - + if (conformance != null) { Set summaryMode = Collections.emptySet(); return (Response) response.streamResponseAsResource(conformance, false, summaryMode, Constants.STATUS_HTTP_200_OK, null, true, false); @@ -229,11 +245,9 @@ public Response conformance() throws IOException { /** * This method will add a provider to the conformance. This method is almost an exact copy of {@link ca.uhn.fhir.rest.server.RestfulServer#findResourceMethods(Object)} - * - * @param theProvider - * an instance of the provider interface - * @param theProviderInterface - * the class describing the providers interface + * + * @param theProvider an instance of the provider interface + * @param theProviderInterface the class describing the providers interface * @return the numbers of basemethodbindings added * @see ca.uhn.fhir.rest.server.RestfulServer#findResourceMethods(Object) */ diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java index 2ec8a4679a5d..5023802fb22a 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java @@ -23,7 +23,10 @@ import java.io.IOException; import javax.interceptor.Interceptors; -import javax.ws.rs.*; + +import javax.ws.rs.GET; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java index f1afff31a577..e5992c307b60 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java @@ -23,9 +23,13 @@ import java.util.*; import java.util.Map.Entry; -import javax.ws.rs.core.*; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; import ca.uhn.fhir.interceptor.api.IInterceptorService; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; import org.apache.commons.lang3.StringUtils; import ca.uhn.fhir.context.FhirContext; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2_1Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2_1Test.java index da458ab8c2d9..3259d3223414 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2_1Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2_1Test.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProviderDstu2_1; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.IResourceProvider; +import javax.ws.rs.core.MultivaluedMap; import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -16,7 +17,8 @@ import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -27,7 +29,7 @@ public class AbstractJaxRsConformanceProviderDstu2_1Test { AbstractJaxRsConformanceProvider provider; private ConcurrentHashMap, IResourceProvider> providers; private ResteasyHttpHeaders headers; - private MultivaluedHashMap queryParameters; + private MultivaluedMap queryParameters; @BeforeEach public void setUp() throws Exception { @@ -85,7 +87,7 @@ public void testConformanceInXml() throws Exception { } private AbstractJaxRsConformanceProvider createConformanceProvider(final ConcurrentHashMap, IResourceProvider> providers) - throws Exception { + throws Exception { AbstractJaxRsConformanceProvider result = new AbstractJaxRsConformanceProvider(FhirContext.forDstu2_1(), null, null, null) { @Override protected ConcurrentHashMap, IResourceProvider> getProviders() { diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java index 5879df0fb890..620a8b3dc1c5 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java @@ -14,7 +14,6 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; -import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.test.utilities.JettyUtil; @@ -36,6 +35,7 @@ import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -68,12 +68,9 @@ public class AbstractJaxRsResourceProviderDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(AbstractJaxRsResourceProviderDstu3Test.class); + private static final String PATIENT_NAME = "Van Houte"; private static IGenericClient client; - - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static final String PATIENT_NAME = "Van Houte"; - private static int ourPort; private static String serverBase; private static Server jettyServer; @@ -85,12 +82,6 @@ public class AbstractJaxRsResourceProviderDstu3Test { private void compareResultId(int id, IBaseResource resource) { assertEquals(id, Integer.parseInt(resource.getIdElement().getIdPart())); } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(jettyServer); - TestUtil.randomizeLocaleAndTimezone(); - } private void compareResultUrl(String url, IBaseResource resource) { assertEquals(url, resource.getIdElement().getValueAsString().substring(serverBase.length() - 1)); @@ -98,7 +89,7 @@ private void compareResultUrl(String url, IBaseResource resource) { private Patient createPatient(long id) { Patient theResource = new Patient(); - theResource.setId(new IdType(id)); + theResource.setId(new IdType(id)); return theResource; } @@ -110,7 +101,9 @@ private List createPatients(int firstId, int lastId) { return result; } - /** Find By Id */ + /** + * Find By Id + */ @Test public void findUsingGenericClientById() { when(mock.find(any(IdType.class))).thenReturn(createPatient(1)); @@ -129,7 +122,7 @@ private Bundle getPatientBundle(int size) { for (long i = 0; i < size; i++) { Patient patient = createPatient(i); BundleEntryComponent entry = new BundleEntryComponent(); - entry.setResource(patient); + entry.setResource(patient); result.addEntry(entry); } return result; @@ -144,7 +137,9 @@ public void setUp() { reset(mock); } - /** Conditional Creates */ + /** + * Conditional Creates + */ @Test public void testConditionalCreate() throws Exception { Patient toCreate = createPatient(1); @@ -156,14 +151,16 @@ public void testConditionalCreate() throws Exception { client.setEncoding(EncodingEnum.JSON); MethodOutcome response = client.create().resource(toCreate).conditional() - .where(Patient.IDENTIFIER.exactly().identifier("2")).prefer(PreferReturnEnum.REPRESENTATION).execute(); + .where(Patient.IDENTIFIER.exactly().identifier("2")).prefer(PreferReturnEnum.REPRESENTATION).execute(); assertEquals("myIdentifier", patientCaptor.getValue().getIdentifier().get(0).getValue()); IBaseResource resource = response.getResource(); compareResultId(1, resource); } - /** Conformance - Server */ + /** + * Conformance - Server + */ @Test public void testConformance() { final CapabilityStatement conf = client.fetchConformance().ofType(CapabilityStatement.class).execute(); @@ -180,7 +177,7 @@ public void testCreatePatient() throws Exception { when(mock.create(patientCaptor.capture(), isNull())).thenReturn(outcome); client.setEncoding(EncodingEnum.JSON); final MethodOutcome response = client.create().resource(toCreate).prefer(PreferReturnEnum.REPRESENTATION) - .execute(); + .execute(); IBaseResource resource = response.getResource(); compareResultId(1, resource); assertEquals("myIdentifier", patientCaptor.getValue().getIdentifier().get(0).getValue()); @@ -192,15 +189,17 @@ public void testDeletePatient() { final IBaseOperationOutcome results = client.delete().resourceById("Patient", "1").execute().getOperationOutcome(); assertEquals("1", idCaptor.getValue().getIdPart()); } - - @Test - public void testConditionalDelete() { - when(mock.delete(idCaptor.capture(), conditionalCaptor.capture())).thenReturn(new MethodOutcome()); - client.delete().resourceConditionalByType("Patient").where(Patient.IDENTIFIER.exactly().identifier("2")).execute(); - assertEquals("Patient?identifier=2&_format=json", conditionalCaptor.getValue()); - } - - /** Extended Operations */ + + @Test + public void testConditionalDelete() { + when(mock.delete(idCaptor.capture(), conditionalCaptor.capture())).thenReturn(new MethodOutcome()); + client.delete().resourceConditionalByType("Patient").where(Patient.IDENTIFIER.exactly().identifier("2")).execute(); + assertEquals("Patient?identifier=2&_format=json", conditionalCaptor.getValue()); + } + + /** + * Extended Operations + */ @Test public void testExtendedOperations() { // prepare mock @@ -214,23 +213,9 @@ public void testExtendedOperations() { inParams.addParameter().setName("dummy").setValue(new StringType("myAwesomeDummyValue")); //invoke Parameters outParams = client.operation().onInstance(new IdType("Patient", "1")).named("$someCustomOperation") - .withParameters(inParams).execute(); + .withParameters(inParams).execute(); //verify - assertEquals("outputValue", ((StringType)outParams.getParameter().get(0).getValue()).getValueAsString()); - } - - class StringTypeMatcher implements ArgumentMatcher { - private StringType myStringType; - - public StringTypeMatcher(StringType stringType) { - myStringType = stringType; - } - - @Override - public boolean matches(StringType argument) { - return myStringType.getValue().equals(argument.getValue()); - } - + assertEquals("outputValue", ((StringType) outParams.getParameter().get(0).getValue()).getValueAsString()); } @Test @@ -238,7 +223,7 @@ public void testExtendedOperationsUsingGet() { // prepare mock Parameters resultParameters = new Parameters(); resultParameters.addParameter().setName("return").setResource(createPatient(1)).setValue(new StringType("outputValue")); - when(mock.someCustomOperation(any(IdType.class), argThat(new StringTypeMatcher(new StringType("myAwesomeDummyValue"))))).thenReturn(resultParameters); + when(mock.someCustomOperation(any(IdType.class), argThat(new StringTypeMatcher(new StringType("myAwesomeDummyValue"))))).thenReturn(resultParameters); // Create the input parameters to pass to the server Parameters inParams = new Parameters(); inParams.addParameter().setName("start").setValue(new DateType("2001-01-01")); @@ -255,7 +240,7 @@ public void testExtendedOperationsUsingGet() { .execute(); // verify - assertEquals("outputValue", ((StringType)outParams.getParameter().get(0).getValue()).getValueAsString()); + assertEquals("outputValue", ((StringType) outParams.getParameter().get(0).getValue()).getValueAsString()); } @Test @@ -267,37 +252,43 @@ public void testRead() { assertEquals("1", idCaptor.getValue().getIdPart()); } - /** Search - Compartments */ + /** + * Search - Compartments + */ @Test public void testSearchCompartments() { when(mock.searchCompartment(any(IdType.class))).thenReturn(Arrays.asList((IBaseResource) createPatient(1))); org.hl7.fhir.dstu3.model.Bundle response = client.search().forResource(Patient.class).withIdAndCompartment("1", "Condition") - .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class).execute(); + .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class).execute(); Resource resource = response.getEntry().get(0).getResource(); compareResultId(1, resource); compareResultUrl("/Patient/1", resource); } - /** */ + /** + * + */ @Test public void testSearchPost() { when(mock.search(ArgumentMatchers.isNull(), ArgumentMatchers.isNull())) - .thenReturn(createPatients(1, 13)); + .thenReturn(createPatients(1, 13)); org.hl7.fhir.dstu3.model.Bundle result = client.search().forResource("Patient").usingStyle(SearchStyleEnum.POST) - .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class).execute(); + .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class).execute(); Resource resource = result.getEntry().get(0).getResource(); compareResultId(1, resource); compareResultUrl("/Patient/1", resource); } - /** Search/Query - Type */ + /** + * Search/Query - Type + */ @Test public void testSearchUsingGenericClientBySearch() { // Perform a search when(mock.search(any(StringParam.class), isNull())) - .thenReturn(Arrays.asList(createPatient(1))); + .thenReturn(Arrays.asList(createPatient(1))); final Bundle results = client.search().forResource(Patient.class) - .where(Patient.NAME.matchesExactly().value(PATIENT_NAME)).returnBundle(Bundle.class).execute(); + .where(Patient.NAME.matchesExactly().value(PATIENT_NAME)).returnBundle(Bundle.class).execute(); verify(mock).search(any(StringParam.class), isNull()); IBaseResource resource = results.getEntry().get(0).getResource(); @@ -305,29 +296,33 @@ public void testSearchUsingGenericClientBySearch() { compareResultUrl("/Patient/1", resource); } - /** Search - Multi-valued Parameters (ANY/OR) */ + /** + * Search - Multi-valued Parameters (ANY/OR) + */ @Test public void testSearchUsingGenericClientBySearchWithMultiValues() { when(mock.search(any(StringParam.class), ArgumentMatchers.notNull())) - .thenReturn(Arrays.asList(createPatient(1))); + .thenReturn(Arrays.asList(createPatient(1))); final Bundle results = client.search().forResource(Patient.class) - .where(Patient.ADDRESS.matches().values("Toronto")).and(Patient.ADDRESS.matches().values("Ontario")) - .and(Patient.ADDRESS.matches().values("Canada")) - .where(Patient.NAME.matches().value("SHORTNAME")).returnBundle(Bundle.class).execute(); + .where(Patient.ADDRESS.matches().values("Toronto")).and(Patient.ADDRESS.matches().values("Ontario")) + .and(Patient.ADDRESS.matches().values("Canada")) + .where(Patient.NAME.matches().value("SHORTNAME")).returnBundle(Bundle.class).execute(); IBaseResource resource = results.getEntry().get(0).getResource(); compareResultId(1, resource); compareResultUrl("/Patient/1", resource); } - /** Search - Paging */ + /** + * Search - Paging + */ @Test public void testSearchWithPaging() { // Perform a search when(mock.search(ArgumentMatchers.isNull(), ArgumentMatchers.isNull())) - .thenReturn(createPatients(1, 13)); + .thenReturn(createPatients(1, 13)); final org.hl7.fhir.dstu3.model.Bundle results = client.search().forResource(Patient.class).count(8).returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) - .execute(); + .execute(); assertEquals(results.getEntry().size(), 8); IBaseResource resource = results.getEntry().get(0).getResource(); @@ -348,15 +343,19 @@ public void testSearchWithPaging() { assertNull(nextPage.getLink(org.hl7.fhir.dstu3.model.Bundle.LINK_NEXT)); } - /** Search - Subsetting (_summary and _elements) */ + /** + * Search - Subsetting (_summary and _elements) + */ @Test @Disabled public void testSummary() { Object response = client.search().forResource(Patient.class) - .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class).execute(); + .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class).execute(); } - /** Transaction - Server */ + /** + * Transaction - Server + */ // @Disabled // @Test // public void testTransaction() { @@ -371,7 +370,6 @@ public void testSummary() { // entry.setTransactionMethod(theTransactionOperation); // ca.uhn.fhir.model.api.Bundle response = client.transaction().withBundle(bundle).execute(); // } - @Test public void testUpdateById() throws Exception { when(mock.update(idCaptor.capture(), patientCaptor.capture(), conditionalCaptor.capture())).thenReturn(new MethodOutcome()); @@ -379,17 +377,17 @@ public void testUpdateById() throws Exception { assertEquals("1", idCaptor.getValue().getIdPart()); compareResultId(1, patientCaptor.getValue()); } - - @Test - public void testConditionalUpdate() throws Exception { - when(mock.update(idCaptor.capture(), patientCaptor.capture(), conditionalCaptor.capture())).thenReturn(new MethodOutcome()); - client.update().resource(createPatient(1)).conditional().where(Patient.IDENTIFIER.exactly().identifier("2")).execute(); - - assertEquals(null, patientCaptor.getValue().getIdElement().getIdPart()); - assertEquals(null, patientCaptor.getValue().getIdElement().getVersionIdPart()); - assertEquals("Patient?identifier=2&_format=json", conditionalCaptor.getValue()); - } - + + @Test + public void testConditionalUpdate() throws Exception { + when(mock.update(idCaptor.capture(), patientCaptor.capture(), conditionalCaptor.capture())).thenReturn(new MethodOutcome()); + client.update().resource(createPatient(1)).conditional().where(Patient.IDENTIFIER.exactly().identifier("2")).execute(); + + assertEquals(null, patientCaptor.getValue().getIdElement().getIdPart()); + assertEquals(null, patientCaptor.getValue().getIdElement().getVersionIdPart()); + assertEquals("Patient?identifier=2&_format=json", conditionalCaptor.getValue()); + } + @SuppressWarnings("unchecked") @Disabled @Test @@ -425,8 +423,8 @@ public void testXFindUnknownPatient() { assertTrue(e.getMessage().contains("999955541264")); } } - - @Test + + @Test public void testValidate() { // prepare mock final OperationOutcome oo = new OperationOutcome(); @@ -441,33 +439,53 @@ public void testValidate() { assertNotNull(mO.getOperationOutcome()); } + class StringTypeMatcher implements ArgumentMatcher { + private StringType myStringType; + + public StringTypeMatcher(StringType stringType) { + myStringType = stringType; + } + + @Override + public boolean matches(StringType argument) { + return myStringType.getValue().equals(argument.getValue()); + } + + } + + @AfterAll + public static void afterClassClearContext() throws Exception { + JettyUtil.closeServer(jettyServer); + TestUtil.randomizeLocaleAndTimezone(); + } + @BeforeAll public static void setUpClass() throws Exception { ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); jettyServer = new Server(0); jettyServer.setHandler(context); - ServletHolder jerseyServlet = context.addServlet(org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.class, "/*"); + ServletHolder jerseyServlet = context.addServlet(HttpServletDispatcher.class, "/*"); jerseyServlet.setInitOrder(0); //@formatter:off jerseyServlet.setInitParameter("resteasy.resources", - StringUtils.join(Arrays.asList( - TestJaxRsMockPatientRestProviderDstu3.class.getCanonicalName(), + StringUtils.join(Arrays.asList( + TestJaxRsMockPatientRestProviderDstu3.class.getCanonicalName(), // JaxRsExceptionInterceptor.class.getCanonicalName(), - TestJaxRsConformanceRestProviderDstu3.class.getCanonicalName(), - TestJaxRsMockPageProviderDstu3.class.getCanonicalName() - ), ",")); + TestJaxRsConformanceRestProviderDstu3.class.getCanonicalName(), + TestJaxRsMockPageProviderDstu3.class.getCanonicalName() + ), ",")); //@formatter:on - + JettyUtil.startServer(jettyServer); - ourPort = JettyUtil.getPortForStartedServer(jettyServer); + ourPort = JettyUtil.getPortForStartedServer(jettyServer); ourCtx.setRestfulClientFactory(new JaxRsRestfulClientFactory(ourCtx)); ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); - serverBase = "http://localhost:" + ourPort + "/"; - client = ourCtx.newRestfulGenericClient(serverBase); + serverBase = "http://localhost:" + ourPort + "/"; + client = ourCtx.newRestfulGenericClient(serverBase); client.setEncoding(EncodingEnum.JSON); client.registerInterceptor(new LoggingInterceptor(true)); } diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderTest.java index c6271d0bb60c..67b5fff1d673 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderTest.java @@ -439,7 +439,7 @@ public static void setUpClass() throws Exception { context.setContextPath("/"); jettyServer = new Server(0); jettyServer.setHandler(context); - ServletHolder jerseyServlet = context.addServlet(org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.class, "/*"); + ServletHolder jerseyServlet = context.addServlet(org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher.class, "/*"); jerseyServlet.setInitOrder(0); //@formatter:off diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProvider.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProvider.java index 09821ecda934..7d9231b09308 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProvider.java @@ -1,22 +1,21 @@ package ca.uhn.fhir.jaxrs.server.test; -import java.util.concurrent.ConcurrentHashMap; - -import javax.ejb.Stateless; +import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.server.IResourceProvider; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; -import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.server.IResourceProvider; +import javax.ejb.Stateless; +import java.util.concurrent.ConcurrentHashMap; /** * A conformance provider exposes the mock patient and this provider */ @Path("") @Stateless -@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML }) +@Produces({MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML}) public class TestJaxRsConformanceRestProvider extends AbstractJaxRsConformanceProvider { public TestJaxRsConformanceRestProvider() { diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java index 5ff2b78f5402..335272b506dd 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java @@ -3,6 +3,7 @@ import java.util.concurrent.ConcurrentHashMap; import javax.ejb.Stateless; + import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java index cf2d4299b789..b399265fd18b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java @@ -50,6 +50,7 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.test.BaseTest; +import ca.uhn.fhir.test.utilities.BatchJobHelper; import ca.uhn.fhir.test.utilities.LoggingExtension; import ca.uhn.fhir.test.utilities.ProxyUtil; import ca.uhn.fhir.test.utilities.UnregisterScheduledProcessor; @@ -81,6 +82,10 @@ import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.springframework.batch.core.repository.dao.JobExecutionDao; +import org.springframework.batch.core.repository.dao.JobInstanceDao; +import org.springframework.batch.core.repository.dao.MapJobExecutionDao; +import org.springframework.batch.core.repository.dao.MapJobInstanceDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.orm.jpa.JpaTransactionManager; @@ -195,6 +200,21 @@ public abstract class BaseJpaTest extends BaseTest { private IForcedIdDao myForcedIdDao; @Autowired(required = false) protected IFulltextSearchSvc myFulltestSearchSvc; + @Autowired(required = false) + protected BatchJobHelper myBatchJobHelper; + @Autowired(required = false) + private JobExecutionDao myMapJobExecutionDao; + @Autowired(required = false) + private JobInstanceDao myMapJobInstanceDao; + + @AfterEach + public void afterEnsureNoStaleBatchJobs() { + if (myMapJobInstanceDao != null) { + myBatchJobHelper.ensureNoRunningJobs(); + ProxyUtil.getSingletonTarget(myMapJobExecutionDao, MapJobExecutionDao.class).clear(); + ProxyUtil.getSingletonTarget(myMapJobInstanceDao, MapJobInstanceDao.class).clear(); + } + } @AfterEach public void afterPerformCleanup() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java index d5434c6c63b5..4b718b5184e4 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java @@ -87,7 +87,9 @@ import ca.uhn.fhir.rest.server.BasePagingProvider; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; +import ca.uhn.fhir.test.utilities.BatchJobHelper; import ca.uhn.fhir.test.utilities.ITestDataBuilder; +import ca.uhn.fhir.test.utilities.ProxyUtil; import ca.uhn.fhir.util.ClasspathUtil; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.validation.FhirValidator; @@ -171,6 +173,10 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.event.Level; +import org.springframework.batch.core.repository.dao.JobExecutionDao; +import org.springframework.batch.core.repository.dao.JobInstanceDao; +import org.springframework.batch.core.repository.dao.MapJobExecutionDao; +import org.springframework.batch.core.repository.dao.MapJobInstanceDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; @@ -492,11 +498,6 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil @Autowired protected DaoRegistry myDaoRegistry; @Autowired - private IValidationSupport myJpaValidationSupportChainR4; - private PerformanceTracingLoggingInterceptor myPerformanceTracingLoggingInterceptor; - @Autowired - private IBulkDataExportSvc myBulkDataExportSvc; - @Autowired protected IdHelperService myIdHelperService; @Autowired protected IBatchJobSubmitter myBatchJobSubmitter; @@ -504,6 +505,11 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil protected ValidationSettings myValidationSettings; @Autowired protected IMdmLinkDao myMdmLinkDao; + @Autowired + private IValidationSupport myJpaValidationSupportChainR4; + private PerformanceTracingLoggingInterceptor myPerformanceTracingLoggingInterceptor; + @Autowired + private IBulkDataExportSvc myBulkDataExportSvc; @AfterEach() public void afterCleanupDao() { @@ -564,7 +570,7 @@ public void beforeFlushFT() { @AfterEach public void afterPurgeDatabase() { - runInTransaction(()->{ + runInTransaction(() -> { myMdmLinkDao.deleteAll(); }); purgeDatabase(myDaoConfig, mySystemDao, myResourceReindexingSvc, mySearchCoordinatorSvc, mySearchParamRegistry, myBulkDataExportSvc); @@ -623,23 +629,6 @@ protected void validate(IBaseResource theResource) { } } - public class ValidationPolicyAdvisor implements IValidationPolicyAdvisor { - @Override - public ReferenceValidationPolicy policyForReference(IResourceValidator validator, Object appContext, String path, String url) { - return ReferenceValidationPolicy.CHECK_VALID; - } - - @Override - public CodedContentValidationPolicy policyForCodedContent(IResourceValidator iResourceValidator, Object o, String s, ElementDefinition elementDefinition, org.hl7.fhir.r5.model.StructureDefinition structureDefinition, BindingKind bindingKind, org.hl7.fhir.r5.model.ValueSet valueSet, List list) { - return CodedContentValidationPolicy.CODE; - } - - @Override - public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, Object appContext, String containerType, String containerId, Element.SpecialElement containingResourceType, String path, String url) { - return ContainedReferenceValidationPolicy.CHECK_VALID; - } - } - @SuppressWarnings("unchecked") protected void upload(String theClasspath) throws IOException { String resource = loadResource(theClasspath); @@ -649,7 +638,6 @@ protected void upload(String theClasspath) throws IOException { dao.update(resourceParsed); } - protected void assertHierarchyContains(String... theStrings) { List hierarchy = runInTransaction(() -> { List hierarchyHolder = new ArrayList<>(); @@ -667,6 +655,72 @@ protected void assertHierarchyContains(String... theStrings) { } } + protected ValueSet.ConceptReferenceDesignationComponent assertConceptContainsDesignation(ValueSet.ValueSetExpansionContainsComponent theConcept, String theLanguage, String theUseSystem, String theUseCode, String theUseDisplay, String theDesignationValue) { + Stream stream = theConcept.getDesignation().stream(); + if (theLanguage != null) { + stream = stream.filter(designation -> theLanguage.equalsIgnoreCase(designation.getLanguage())); + } + if (theUseSystem != null) { + stream = stream.filter(designation -> theUseSystem.equalsIgnoreCase(designation.getUse().getSystem())); + } + if (theUseCode != null) { + stream = stream.filter(designation -> theUseCode.equalsIgnoreCase(designation.getUse().getCode())); + } + if (theUseDisplay != null) { + stream = stream.filter(designation -> theUseDisplay.equalsIgnoreCase(designation.getUse().getDisplay())); + } + if (theDesignationValue != null) { + stream = stream.filter(designation -> theDesignationValue.equalsIgnoreCase(designation.getValue())); + } + + Optional first = stream.findFirst(); + if (!first.isPresent()) { + String failureMessage = String.format("Concept %s did not contain designation [%s|%s|%s|%s|%s] ", theConcept, theLanguage, theUseSystem, theUseCode, theUseDisplay, theDesignationValue); + fail(failureMessage); + return null; + } else { + return first.get(); + } + } + + protected ValueSet.ValueSetExpansionContainsComponent assertExpandedValueSetContainsConcept(ValueSet theValueSet, String theSystem, String theCode, String theDisplay, Integer theDesignationCount) { + List contains = theValueSet.getExpansion().getContains(); + + Stream stream = contains.stream(); + if (theSystem != null) { + stream = stream.filter(concept -> theSystem.equalsIgnoreCase(concept.getSystem())); + } + if (theCode != null) { + stream = stream.filter(concept -> theCode.equalsIgnoreCase(concept.getCode())); + } + if (theDisplay != null) { + stream = stream.filter(concept -> theDisplay.equalsIgnoreCase(concept.getDisplay())); + } + if (theDesignationCount != null) { + stream = stream.filter(concept -> concept.getDesignation().size() == theDesignationCount); + } + + Optional first = stream.findFirst(); + if (!first.isPresent()) { + String expandedValueSetString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theValueSet); + String failureMessage = String.format("Expanded ValueSet %s did not contain concept [%s|%s|%s] with [%d] designations. Outcome:\n%s", theValueSet.getId(), theSystem, theCode, theDisplay, theDesignationCount, expandedValueSetString); + fail(failureMessage); + return null; + } else { + return first.get(); + } + } + + public List getExpandedConceptsByValueSetUrl(String theValuesetUrl) { + return runInTransaction(() -> { + List valueSets = myTermValueSetDao.findTermValueSetByUrl(Pageable.unpaged(), theValuesetUrl); + assertEquals(1, valueSets.size()); + TermValueSet valueSet = valueSets.get(0); + List concepts = valueSet.getConcepts(); + return concepts.stream().map(concept -> concept.getCode()).collect(Collectors.toList()); + }); + } + private static void flattenExpansionHierarchy(List theFlattenedHierarchy, List theCodes, String thePrefix) { theCodes.sort((o1, o2) -> { int s1 = o1.getSequence() != null ? o1.getSequence() : o1.getCode().hashCode(); @@ -796,70 +850,20 @@ public static String toSearchUuidFromLinkNext(Bundle theBundle) { return uuid; } - - protected ValueSet.ConceptReferenceDesignationComponent assertConceptContainsDesignation(ValueSet.ValueSetExpansionContainsComponent theConcept, String theLanguage, String theUseSystem, String theUseCode, String theUseDisplay, String theDesignationValue) { - Stream stream = theConcept.getDesignation().stream(); - if (theLanguage != null) { - stream = stream.filter(designation -> theLanguage.equalsIgnoreCase(designation.getLanguage())); - } - if (theUseSystem != null) { - stream = stream.filter(designation -> theUseSystem.equalsIgnoreCase(designation.getUse().getSystem())); - } - if (theUseCode != null) { - stream = stream.filter(designation -> theUseCode.equalsIgnoreCase(designation.getUse().getCode())); - } - if (theUseDisplay != null) { - stream = stream.filter(designation -> theUseDisplay.equalsIgnoreCase(designation.getUse().getDisplay())); - } - if (theDesignationValue != null) { - stream = stream.filter(designation -> theDesignationValue.equalsIgnoreCase(designation.getValue())); - } - - Optional first = stream.findFirst(); - if (!first.isPresent()) { - String failureMessage = String.format("Concept %s did not contain designation [%s|%s|%s|%s|%s] ", theConcept, theLanguage, theUseSystem, theUseCode, theUseDisplay, theDesignationValue); - fail(failureMessage); - return null; - } else { - return first.get(); + public class ValidationPolicyAdvisor implements IValidationPolicyAdvisor { + @Override + public ReferenceValidationPolicy policyForReference(IResourceValidator validator, Object appContext, String path, String url) { + return ReferenceValidationPolicy.CHECK_VALID; } - } - - protected ValueSet.ValueSetExpansionContainsComponent assertExpandedValueSetContainsConcept(ValueSet theValueSet, String theSystem, String theCode, String theDisplay, Integer theDesignationCount) { - List contains = theValueSet.getExpansion().getContains(); - Stream stream = contains.stream(); - if (theSystem != null) { - stream = stream.filter(concept -> theSystem.equalsIgnoreCase(concept.getSystem())); - } - if (theCode != null) { - stream = stream.filter(concept -> theCode.equalsIgnoreCase(concept.getCode())); - } - if (theDisplay != null) { - stream = stream.filter(concept -> theDisplay.equalsIgnoreCase(concept.getDisplay())); - } - if (theDesignationCount != null) { - stream = stream.filter(concept -> concept.getDesignation().size() == theDesignationCount); + @Override + public CodedContentValidationPolicy policyForCodedContent(IResourceValidator iResourceValidator, Object o, String s, ElementDefinition elementDefinition, org.hl7.fhir.r5.model.StructureDefinition structureDefinition, BindingKind bindingKind, org.hl7.fhir.r5.model.ValueSet valueSet, List list) { + return CodedContentValidationPolicy.CODE; } - Optional first = stream.findFirst(); - if (!first.isPresent()) { - String expandedValueSetString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theValueSet); - String failureMessage = String.format("Expanded ValueSet %s did not contain concept [%s|%s|%s] with [%d] designations. Outcome:\n%s", theValueSet.getId(), theSystem, theCode, theDisplay, theDesignationCount, expandedValueSetString); - fail(failureMessage); - return null; - } else { - return first.get(); + @Override + public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, Object appContext, String containerType, String containerId, Element.SpecialElement containingResourceType, String path, String url) { + return ContainedReferenceValidationPolicy.CHECK_VALID; } } - - public List getExpandedConceptsByValueSetUrl(String theValuesetUrl) { - return runInTransaction(() -> { - List valueSets = myTermValueSetDao.findTermValueSetByUrl(Pageable.unpaged(), theValuesetUrl); - assertEquals(1, valueSets.size()); - TermValueSet valueSet = valueSets.get(0); - List concepts = valueSet.getConcepts(); - return concepts.stream().map(concept -> concept.getCode()).collect(Collectors.toList()); - }); - } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java index ad1a93e57edc..a3f369761166 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java @@ -158,9 +158,6 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4Test.class); - @Autowired - private BatchJobHelper myBatchJobHelper; - @AfterEach public final void after() { myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences()); diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/BatchJobHelper.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/BatchJobHelper.java index 0a8e64efe57d..b14c8257a583 100644 --- a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/BatchJobHelper.java +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/BatchJobHelper.java @@ -20,6 +20,9 @@ * #L% */ +import ca.uhn.fhir.util.StopWatch; +import org.awaitility.core.ConditionTimeoutException; +import org.hamcrest.Matchers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.BatchStatus; @@ -37,7 +40,11 @@ import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.oneOf; import static org.junit.jupiter.api.Assertions.fail; public class BatchJobHelper { @@ -55,15 +62,18 @@ public List awaitAllBulkJobCompletions(String... theJobNames) { /** * Await and report for job completions + * * @param theFailIfNotJobsFound indicate if must fail in case no matching jobs are found - * @param theJobNames The job names to match + * @param theJobNames The job names to match * @return the matched JobExecution(s) */ public List awaitAllBulkJobCompletions(boolean theFailIfNotJobsFound, String... theJobNames) { assert theJobNames.length > 0; if (theFailIfNotJobsFound) { - await().until(() -> !getJobInstances(theJobNames).isEmpty()); + await() + .alias("Wait for jobs to exist named: " + Arrays.asList(theJobNames)) + .until(() -> getJobInstances(theJobNames), not(empty())); } List matchingJobInstances = getJobInstances(theJobNames); @@ -97,19 +107,43 @@ public JobExecution awaitJobExecution(Long theJobExecutionId) { } protected void awaitJobCompletions(Collection theJobs) { - theJobs.forEach(jobExecution -> awaitJobCompletion(jobExecution)); + // This intermittently fails for unknown reasons, so I've added a bunch + // of extra junk here to improve what we output when it fails + for (JobExecution jobExecution : theJobs) { + try { + awaitJobCompletion(jobExecution); + } catch (ConditionTimeoutException e) { + StringBuilder msg = new StringBuilder(); + msg.append("Failed waiting for job to complete.\n"); + msg.append("Error: ").append(e).append("\n"); + msg.append("Statuses:"); + for (JobExecution next : theJobs) { + JobExecution execution = myJobExplorer.getJobExecution(next.getId()); + msg.append("\n * Execution ") + .append(execution.getId()) + .append(" has status ") + .append(execution.getStatus()); + } + fail(msg.toString()); + } + } } public void awaitJobCompletion(JobExecution theJobExecution) { - await().atMost(120, TimeUnit.SECONDS).until(() -> { - JobExecution jobExecution = myJobExplorer.getJobExecution(theJobExecution.getId()); - ourLog.info("JobExecution {} currently has status: {}- Failures if any: {}", theJobExecution.getId(), jobExecution.getStatus(), jobExecution.getFailureExceptions()); - // JM: Adding ABANDONED status because given the description, it s similar to FAILURE, and we need to avoid tests failing because - // of wait timeouts caused by unmatched statuses. Also adding STOPPED because tests were found where this wait timed out - // with jobs keeping that status during the whole wait - return jobExecution.getStatus() == BatchStatus.COMPLETED || jobExecution.getStatus() == BatchStatus.FAILED - || jobExecution.getStatus() == BatchStatus.ABANDONED || jobExecution.getStatus() == BatchStatus.STOPPED; - }); + await() + .atMost(120, TimeUnit.SECONDS).until(() -> { + JobExecution jobExecution = myJobExplorer.getJobExecution(theJobExecution.getId()); + ourLog.info("JobExecution {} currently has status: {}- Failures if any: {}", theJobExecution.getId(), jobExecution.getStatus(), jobExecution.getFailureExceptions()); + return jobExecution.getStatus(); + }, Matchers.oneOf( + // JM: Adding ABANDONED status because given the description, it s similar to FAILURE, and we need to avoid tests failing because + // of wait timeouts caused by unmatched statuses. Also adding STOPPED because tests were found where this wait timed out + // with jobs keeping that status during the whole wait + BatchStatus.COMPLETED, + BatchStatus.FAILED, + BatchStatus.ABANDONED, + BatchStatus.STOPPED + )); } public int getReadCount(Long theJobExecutionId) { @@ -129,4 +163,24 @@ private StepExecution getStepExecution(Long theJobExecutionId) { return stepExecutions.iterator().next(); } + public void ensureNoRunningJobs() { + for (String nextJobName : myJobExplorer.getJobNames()) { + List instances = myJobExplorer.getJobInstances(nextJobName, 0, 10000); + for (JobInstance nextInstance : instances) { + List executions = myJobExplorer.getJobExecutions(nextInstance); + for (JobExecution nextExecution : executions) { + ourLog.info("Have job execution {} in status: {}", nextExecution.getId(), nextExecution.getStatus()); + try { + await().until(() -> myJobExplorer.getJobExecution(nextExecution.getId()).getStatus(), oneOf(BatchStatus.STOPPED, BatchStatus.ABANDONED, BatchStatus.FAILED, BatchStatus.COMPLETED)); + } catch (ConditionTimeoutException e) { + JobExecution execution = myJobExplorer.getJobExecution(nextExecution.getId()); + fail("Execution " + execution + "\n" + + "Instance: " + nextInstance + "\n" + + "Job: " + nextJobName); + } + + } + } + } + } } diff --git a/pom.xml b/pom.xml index 818df5f410b2..0dcacac6ced6 100644 --- a/pom.xml +++ b/pom.xml @@ -825,7 +825,7 @@ 3.8.1 4.1.2 1.4 - 4.0.0.Beta3 + 5.0.2.Final 5.6.5 9.5.4 2.9.0 @@ -1156,11 +1156,6 @@ validation-api 2.0.1.Final - - javax.ws.rs - javax.ws.rs-api - 2.0.1 -