From 14f12e7dabd2436f5c8be676f4097050f6a1e988 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Mon, 15 Mar 2021 08:52:46 -0400 Subject: [PATCH] Missed commit for #2471 --- ...questRetryVersionConflictsInterceptor.java | 10 ++++++ .../jpa/partition/SystemRequestDetails.java | 30 ++++++++++++++-- .../FhirResourceDaoR4ConcurrentWriteTest.java | 36 +++++++++++++++++-- 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/UserRequestRetryVersionConflictsInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/UserRequestRetryVersionConflictsInterceptor.java index e764e2f5794..c5e84ea27f8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/UserRequestRetryVersionConflictsInterceptor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/UserRequestRetryVersionConflictsInterceptor.java @@ -24,7 +24,9 @@ import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.api.model.ResourceVersionConflictResolutionStrategy; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails; +import org.apache.commons.lang3.Validate; import java.util.List; import java.util.StringTokenizer; @@ -83,4 +85,12 @@ public class UserRequestRetryVersionConflictsInterceptor { } + /** + * Convenience method to add a retry header to a system request + */ + public static void addRetryHeader(SystemRequestDetails theRequestDetails, int theMaxRetries) { + Validate.inclusiveBetween(1, Integer.MAX_VALUE, theMaxRetries, "Max retries must be > 0"); + String value = RETRY + "; " + MAX_RETRIES + "=" + theMaxRetries; + theRequestDetails.addHeader(HEADER_NAME, value); + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/SystemRequestDetails.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/SystemRequestDetails.java index 2cd6f44da8e..c2b3361eb0c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/SystemRequestDetails.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/SystemRequestDetails.java @@ -33,12 +33,19 @@ import ca.uhn.fhir.rest.server.ElementsSupportEnum; import ca.uhn.fhir.rest.server.IPagingProvider; import ca.uhn.fhir.rest.server.IRestfulServerDefaults; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.nio.charset.Charset; import java.util.List; +import java.util.Optional; /** * A default RequestDetails implementation that can be used for system calls to @@ -51,6 +58,8 @@ public class SystemRequestDetails extends RequestDetails { super(new MyInterceptorBroadcaster()); } + private ListMultimap myHeaders; + public SystemRequestDetails(IInterceptorBroadcaster theInterceptorBroadcaster) { super(theInterceptorBroadcaster); } @@ -72,14 +81,31 @@ public class SystemRequestDetails extends RequestDetails { @Override public String getHeader(String name) { - return null; + List headers = getHeaders(name); + if (headers.isEmpty()) { + return null; + } else { + return headers.get(0); + } } @Override public List getHeaders(String name) { - return null; + ListMultimap headers = myHeaders; + if (headers == null) { + headers = ImmutableListMultimap.of(); + } + return headers.get(name); } + public void addHeader(String theName, String theValue) { + if (myHeaders == null) { + myHeaders = ArrayListMultimap.create(); + } + myHeaders.put(theName, theValue); + } + + @Override public Object getAttribute(String theAttributeName) { return null; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConcurrentWriteTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConcurrentWriteTest.java index f9b4a3132ab..1403b0c3462 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConcurrentWriteTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConcurrentWriteTest.java @@ -12,14 +12,12 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.HapiExtensions; -import com.github.dockerjava.api.model.ResourceVersion; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.IdType; -import org.hl7.fhir.r4.model.IntegerType; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.SearchParameter; @@ -140,6 +138,39 @@ public class FhirResourceDaoR4ConcurrentWriteTest extends BaseJpaR4Test { } + @Test + public void testCreateWithClientAssignedId_SystemRequestContainingRetryDirective() throws InterruptedException, ExecutionException { + myInterceptorRegistry.registerInterceptor(myRetryInterceptor); + + SystemRequestDetails requestDetails = new SystemRequestDetails(); + UserRequestRetryVersionConflictsInterceptor.addRetryHeader(requestDetails, 10); + + List> futures = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + Patient p = new Patient(); + p.setId("ABC"); + p.setActive(true); + p.addIdentifier().setValue("VAL" + i); + Runnable task = () -> { + myPatientDao.update(p, requestDetails); + }; + Future future = myExecutor.submit(task); + futures.add(future); + } + + // Should not fail + for (Future next : futures) { + next.get(); + ourLog.info("Future produced success"); + } + + // Make sure we saved the object + Patient patient = myPatientDao.read(new IdType("Patient/ABC")); + ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient)); + assertEquals(true, patient.getActive()); + + } + @Test public void testCreateWithUniqueConstraint() { SearchParameter sp = new SearchParameter(); @@ -424,7 +455,6 @@ public class FhirResourceDaoR4ConcurrentWriteTest extends BaseJpaR4Test { } - @Test public void testTransactionWithCreate() { myInterceptorRegistry.registerInterceptor(myRetryInterceptor);