diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HashMapResourceProviderConceptMapDstu3.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HashMapResourceProviderConceptMapDstu3.java index 7ca6a49417f..9550a66ab17 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HashMapResourceProviderConceptMapDstu3.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HashMapResourceProviderConceptMapDstu3.java @@ -25,7 +25,7 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.rest.annotation.*; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.provider.AbstractHashMapResourceProvider; +import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider; import com.google.common.base.Charsets; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; @@ -42,7 +42,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; /** * This is a subclass to implement FHIR operations specific to DSTU3 ConceptMap - * resources. Its superclass, {@link AbstractHashMapResourceProvider}, is a simple + * resources. Its superclass, {@link HashMapResourceProvider}, is a simple * implementation of the resource provider interface that uses a HashMap to * store all resources in memory. *

@@ -53,7 +53,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; *

  • Conditional update for DSTU3 ConceptMap resources by ConceptMap.url
  • * */ -public class HashMapResourceProviderConceptMapDstu3 extends AbstractHashMapResourceProvider { +public class HashMapResourceProviderConceptMapDstu3 extends HashMapResourceProvider { @SuppressWarnings("unchecked") public HashMapResourceProviderConceptMapDstu3(FhirContext theFhirContext) { super(theFhirContext, ConceptMap.class); @@ -84,10 +84,10 @@ public class HashMapResourceProviderConceptMapDstu3 extends AbstractHashMapResou return retVal; } + @Override @Update - public MethodOutcome updateConceptMapConditional( + public MethodOutcome update( @ResourceParam ConceptMap theConceptMap, - @IdParam IdType theId, @ConditionalUrlParam String theConditional) { MethodOutcome methodOutcome = new MethodOutcome(); @@ -112,14 +112,14 @@ public class HashMapResourceProviderConceptMapDstu3 extends AbstractHashMapResou List conceptMaps = searchByUrl(url); if (!conceptMaps.isEmpty()) { - methodOutcome = update(conceptMaps.get(0)); + methodOutcome = super.update(conceptMaps.get(0), null); } else { methodOutcome = create(theConceptMap); } } } else { - methodOutcome = update(theConceptMap); + methodOutcome = super.update(theConceptMap, null); } return methodOutcome; diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HashMapResourceProviderConceptMapR4.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HashMapResourceProviderConceptMapR4.java index d958ae5a3c4..471af74a751 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HashMapResourceProviderConceptMapR4.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HashMapResourceProviderConceptMapR4.java @@ -25,7 +25,7 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.rest.annotation.*; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.provider.AbstractHashMapResourceProvider; +import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider; import com.google.common.base.Charsets; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; @@ -42,7 +42,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; /** * This is a subclass to implement FHIR operations specific to R4 ConceptMap - * resources. Its superclass, {@link AbstractHashMapResourceProvider}, is a simple + * resources. Its superclass, {@link HashMapResourceProvider}, is a simple * implementation of the resource provider interface that uses a HashMap to * store all resources in memory. *

    @@ -53,7 +53,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; *

  • Conditional update for R4 ConceptMap resources by ConceptMap.url
  • * */ -public class HashMapResourceProviderConceptMapR4 extends AbstractHashMapResourceProvider { +public class HashMapResourceProviderConceptMapR4 extends HashMapResourceProvider { @SuppressWarnings("unchecked") public HashMapResourceProviderConceptMapR4(FhirContext theFhirContext) { super(theFhirContext, ConceptMap.class); @@ -84,16 +84,15 @@ public class HashMapResourceProviderConceptMapR4 extends AbstractHashMapResource return retVal; } + @Override @Update - public MethodOutcome updateConceptMapConditional( + public MethodOutcome update( @ResourceParam ConceptMap theConceptMap, - @IdParam IdType theId, @ConditionalUrlParam String theConditional) { MethodOutcome methodOutcome = new MethodOutcome(); if (theConditional != null) { - String url = null; try { @@ -112,14 +111,14 @@ public class HashMapResourceProviderConceptMapR4 extends AbstractHashMapResource List conceptMaps = searchByUrl(url); if (!conceptMaps.isEmpty()) { - methodOutcome = update(conceptMaps.get(0)); + methodOutcome = super.update(conceptMaps.get(0), null); } else { methodOutcome = create(theConceptMap); } } } else { - methodOutcome = update(theConceptMap); + methodOutcome = super.update(theConceptMap, null); } return methodOutcome; 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 556ff29c3ed..572d0a8c111 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 @@ -34,7 +34,6 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.Rule; import org.mockito.Mockito; -import org.springframework.orm.hibernate5.HibernateTransactionManager; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; @@ -49,6 +48,7 @@ import java.sql.SQLException; import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; import static ca.uhn.fhir.util.TestUtil.randomizeLocale; import static org.junit.Assert.*; @@ -77,16 +77,6 @@ public abstract class BaseJpaTest { BaseHapiFhirResourceDao.setDisableIncrementOnUpdateForUnitTest(false); } - @Before - public void beforeCreateSrd() { - mySrd = mock(ServletRequestDetails.class, Mockito.RETURNS_DEEP_STUBS); - when(mySrd.getRequestOperationCallback()).thenReturn(myRequestOperationCallback); - myServerInterceptorList = new ArrayList<>(); - when(mySrd.getServer().getInterceptors()).thenReturn(myServerInterceptorList); - when(mySrd.getUserData()).thenReturn(new HashMap<>()); - when(mySrd.getHeaders(eq(JpaConstants.HEADER_META_SNAPSHOT_MODE))).thenReturn(new ArrayList<>()); - } - @After public void afterValidateNoTransaction() { PlatformTransactionManager txManager = getTxManager(); @@ -103,6 +93,7 @@ public abstract class BaseJpaTest { if (currentSession != null) { currentSession.doWork(new Work() { + @Override public void execute(Connection connection) throws SQLException { isReadOnly.set(connection.isReadOnly()); } @@ -113,6 +104,16 @@ public abstract class BaseJpaTest { } } + @Before + public void beforeCreateSrd() { + mySrd = mock(ServletRequestDetails.class, Mockito.RETURNS_DEEP_STUBS); + when(mySrd.getRequestOperationCallback()).thenReturn(myRequestOperationCallback); + myServerInterceptorList = new ArrayList<>(); + when(mySrd.getServer().getInterceptors()).thenReturn(myServerInterceptorList); + when(mySrd.getUserData()).thenReturn(new HashMap<>()); + when(mySrd.getHeaders(eq(JpaConstants.HEADER_META_SNAPSHOT_MODE))).thenReturn(new ArrayList<>()); + } + @Before public void beforeRandomizeLocale() { randomizeLocale(); @@ -299,6 +300,7 @@ public abstract class BaseJpaTest { return retVal.toArray(new String[retVal.size()]); } + @SuppressWarnings("RedundantThrows") @AfterClass public static void afterClassClearContext() throws Exception { TestUtil.clearAllStaticFieldsForUnitTest(); @@ -360,7 +362,19 @@ public abstract class BaseJpaTest { } } if (sw.getMillis() >= 15000) { - fail("Size " + theList.size() + " is != target " + theTarget + " - Got: " + theList.toString()); + String describeResults = theList + .stream() + .map(t -> { + if (t == null) { + return "null"; + } + if (t instanceof IBaseResource) { + return ((IBaseResource)t).getIdElement().getValue(); + } + return t.toString(); + }) + .collect(Collectors.joining(", ")); + fail("Size " + theList.size() + " is != target " + theTarget + " - Got: " + describeResults); } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java index c6aa63ae750..fccf834173b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java @@ -235,6 +235,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest { return retVal; } + @SuppressWarnings("RedundantThrows") @AfterClass public static void afterClassClearContext() throws Exception { TestUtil.clearAllStaticFieldsForUnitTest(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/r4/RestHookTestWithInterceptorRegisteredToDaoConfigR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/r4/RestHookTestWithInterceptorRegisteredToDaoConfigR4Test.java index 32e418e82a8..d8568ecdd5e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/r4/RestHookTestWithInterceptorRegisteredToDaoConfigR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/r4/RestHookTestWithInterceptorRegisteredToDaoConfigR4Test.java @@ -150,7 +150,7 @@ public class RestHookTestWithInterceptorRegisteredToDaoConfigR4Test extends Base waitForSize(0, ourCreatedObservations); waitForSize(4, ourUpdatedObservations); - Observation observation3 = myClient.read(Observation.class, observationTemp3.getId()); + Observation observation3 = myClient.read().resource(Observation.class).withId(observationTemp3.getId()).execute(); CodeableConcept codeableConcept = new CodeableConcept(); observation3.setCode(codeableConcept); Coding coding = codeableConcept.addCoding(); @@ -223,7 +223,7 @@ public class RestHookTestWithInterceptorRegisteredToDaoConfigR4Test extends Base waitForSize(0, ourCreatedObservations); waitForSize(4, ourUpdatedObservations); - Observation observation3 = myClient.read(Observation.class, observationTemp3.getId()); + Observation observation3 = myClient.read().resource(Observation.class).withId(observationTemp3.getId()).execute(); CodeableConcept codeableConcept = new CodeableConcept(); observation3.setCode(codeableConcept); Coding coding = codeableConcept.addCoding(); @@ -236,7 +236,7 @@ public class RestHookTestWithInterceptorRegisteredToDaoConfigR4Test extends Base waitForSize(0, ourCreatedObservations); waitForSize(4, ourUpdatedObservations); - Observation observation3a = myClient.read(Observation.class, observationTemp3.getId()); + Observation observation3a = myClient.read().resource(Observation.class).withId(observationTemp3.getId()).execute(); CodeableConcept codeableConcept1 = new CodeableConcept(); observation3a.setCode(codeableConcept1); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java index 3045580b603..1b7e81e81ff 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java @@ -13,7 +13,9 @@ import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -440,206 +442,237 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter { } theServletResponse.setContentType(Constants.CT_HTML_WITH_UTF8); - StringBuilder b = new StringBuilder(); - b.append("\n"); - b.append(" \n"); - b.append(" \n"); - b.append(" \n"); - b.append(" \n"); - b.append("\n"); - b.append(" "); + StringBuilder outputBuffer = new StringBuilder(); + outputBuffer.append("\n"); + outputBuffer.append(" \n"); + outputBuffer.append(" \n"); + outputBuffer.append(" \n"); + outputBuffer.append(" \n"); + outputBuffer.append("\n"); + outputBuffer.append(" "); - b.append("

    "); - b.append("This result is being rendered in HTML for easy viewing. "); - b.append("You may access this content as "); + outputBuffer.append("

    "); + outputBuffer.append("This result is being rendered in HTML for easy viewing. "); + outputBuffer.append("You may access this content as "); - b.append("Raw JSON or "); + outputBuffer.append("Raw JSON or "); - b.append("Raw XML, "); + outputBuffer.append("Raw XML, "); - b.append(" or view this content in "); + outputBuffer.append(" or view this content in "); - b.append("HTML JSON "); + outputBuffer.append("HTML JSON "); - b.append("or "); - b.append("HTML XML."); + outputBuffer.append("or "); + outputBuffer.append("HTML XML."); Date startTime = (Date) theServletRequest.getAttribute(RestfulServer.REQUEST_START_TIME); if (startTime != null) { long time = System.currentTimeMillis() - startTime.getTime(); - b.append(" Response generated in "); - b.append(time); - b.append("ms."); + outputBuffer.append(" Response generated in "); + outputBuffer.append(time); + outputBuffer.append("ms."); } - b.append("

    "); + outputBuffer.append("

    "); - b.append("\n"); + outputBuffer.append("\n"); // status (e.g. HTTP 200 OK) String statusName = Constants.HTTP_STATUS_NAMES.get(theServletResponse.getStatus()); statusName = defaultString(statusName); - b.append("
    "); - b.append("HTTP "); - b.append(theServletResponse.getStatus()); - b.append(" "); - b.append(statusName); - b.append("
    "); + outputBuffer.append("
    "); + outputBuffer.append("HTTP "); + outputBuffer.append(theServletResponse.getStatus()); + outputBuffer.append(" "); + outputBuffer.append(statusName); + outputBuffer.append("
    "); - b.append("\n"); - b.append("\n"); + outputBuffer.append("\n"); + outputBuffer.append("\n"); try { if (isShowRequestHeaders()) { - streamRequestHeaders(theServletRequest, b); + streamRequestHeaders(theServletRequest, outputBuffer); } if (isShowResponseHeaders()) { - streamResponseHeaders(theRequestDetails, theServletResponse, b); + streamResponseHeaders(theRequestDetails, theServletResponse, outputBuffer); } } catch (Throwable t) { // ignore (this will hit if we're running in a servlet 2.5 environment) } - b.append("

    Response Body

    "); + outputBuffer.append("

    Response Body

    "); - b.append("
    "); + outputBuffer.append("
    "); // Response Body - b.append("
    ");
    +			outputBuffer.append("
    ");
     			StringBuilder target = new StringBuilder();
     			int linesCount = format(encoded, target, encoding);
    -			b.append(target);
    -			b.append("
    "); + outputBuffer.append(target); + outputBuffer.append("
    "); // Line Numbers - b.append("
    ");
    +			outputBuffer.append("
    ");
     			for (int i = 1; i <= linesCount; i++) {
    -				b.append("
    "); + outputBuffer.append(""); + outputBuffer.append(""); + outputBuffer.append(i); + outputBuffer.append("
    "); } - b.append("
    "); + outputBuffer.append("
    "); - b.append("
    "); + outputBuffer.append("
    "); - b.append("\n"); + outputBuffer.append("\n"); InputStream jsStream = ResponseHighlighterInterceptor.class.getResourceAsStream("ResponseHighlighter.js"); String jsStr = jsStream != null ? IOUtils.toString(jsStream, "UTF-8") : "console.log('ResponseHighlighterInterceptor: javascript theResource not found')"; jsStr = jsStr.replace("FHIR_BASE", theRequestDetails.getServerBaseForRequest()); - b.append("\n"); + outputBuffer.append("\n"); - b.append(""); - b.append(""); - String out = b.toString(); + StopWatch writeSw = new StopWatch(); + theServletResponse.getWriter().append(outputBuffer); + theServletResponse.getWriter().flush(); + + theServletResponse.getWriter().append("
    "); + theServletResponse.getWriter().append("Wrote "); + writeLength(theServletResponse, encoded.length()); + theServletResponse.getWriter().append(" ("); + writeLength(theServletResponse, outputBuffer.length()); + theServletResponse.getWriter().append(" total including HTML)"); + + theServletResponse.getWriter().append(" in estimated "); + theServletResponse.getWriter().append(writeSw.toString()); + theServletResponse.getWriter().append("
    "); + + + theServletResponse.getWriter().append(""); + theServletResponse.getWriter().append(""); - theServletResponse.getWriter().append(out); theServletResponse.getWriter().close(); } catch (IOException e) { throw new InternalErrorException(e); } } + private void writeLength(HttpServletResponse theServletResponse, int theLength) throws IOException { + double kb = ((double)theLength) / FileUtils.ONE_KB; + if (kb <= 1000) { + theServletResponse.getWriter().append(String.format("%.1f", kb)).append(" KB"); + } else { + double mb = kb / 1000; + theServletResponse.getWriter().append(String.format("%.1f", mb)).append(" MB"); + } + } + private void streamResponseHeaders(RequestDetails theRequestDetails, HttpServletResponse theServletResponse, StringBuilder b) { if (theServletResponse.getHeaderNames().isEmpty() == false) { b.append("

    Response Headers

    "); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/AbstractHashMapResourceProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/AbstractHashMapResourceProvider.java deleted file mode 100644 index 0756072e34b..00000000000 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/AbstractHashMapResourceProvider.java +++ /dev/null @@ -1,198 +0,0 @@ -package ca.uhn.fhir.rest.server.provider; - -/*- - * #%L - * HAPI FHIR - Server Framework - * %% - * Copyright (C) 2014 - 2018 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IIdType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.*; - -/** - * This class is a simple implementation of the resource provider - * interface that uses a HashMap to store all resources in memory. - * It is essentially a copy of {@link ca.uhn.fhir.rest.server.provider.HashMapResourceProvider} - * with the {@link Update} and {@link ResourceParam} annotations removed from method - * {@link ca.uhn.fhir.rest.server.provider.HashMapResourceProvider#update(IBaseResource)}. - * Non-generic subclasses of this abstract class may implement their own annotated methods (e.g. a conditional - * update method specifically for ConceptMap resources). - *

    - * This class currently supports the following FHIR operations: - *

    - *
      - *
    • Create
    • - *
    • Update existing resource
    • - *
    • Update non-existing resource (e.g. create with client-supplied ID)
    • - *
    • Delete
    • - *
    • Search by resource type with no parameters
    • - *
    - * - * @param The resource type to support - */ -public class AbstractHashMapResourceProvider implements IResourceProvider { - private static final Logger ourLog = LoggerFactory.getLogger(AbstractHashMapResourceProvider.class); - private final Class myResourceType; - private final FhirContext myFhirContext; - private final String myResourceName; - protected Map> myIdToVersionToResourceMap = new HashMap<>(); - private long myNextId; - - /** - * Constructor - * - * @param theFhirContext The FHIR context - * @param theResourceType The resource type to support - */ - @SuppressWarnings("WeakerAccess") - public AbstractHashMapResourceProvider(FhirContext theFhirContext, Class theResourceType) { - myFhirContext = theFhirContext; - myResourceType = theResourceType; - myResourceName = myFhirContext.getResourceDefinition(theResourceType).getName(); - clear(); - } - - /** - * Clear all data held in this resource provider - */ - public void clear() { - myNextId = 1; - myIdToVersionToResourceMap.clear(); - } - - @Create - public MethodOutcome create(@ResourceParam T theResource) { - long idPart = myNextId++; - String idPartAsString = Long.toString(idPart); - Long versionIdPart = 1L; - - IIdType id = store(theResource, idPartAsString, versionIdPart); - - return new MethodOutcome() - .setCreated(true) - .setId(id); - } - - @Delete - public MethodOutcome delete(@IdParam IIdType theId) { - TreeMap versions = myIdToVersionToResourceMap.get(theId.getIdPart()); - if (versions == null || versions.isEmpty()) { - throw new ResourceNotFoundException(theId); - } - - long nextVersion = versions.lastEntry().getKey() + 1L; - IIdType id = store(null, theId.getIdPart(), nextVersion); - - return new MethodOutcome() - .setId(id); - } - - @Override - public Class getResourceType() { - return myResourceType; - } - - private synchronized TreeMap getVersionToResource(String theIdPart) { - if (!myIdToVersionToResourceMap.containsKey(theIdPart)) { - myIdToVersionToResourceMap.put(theIdPart, new TreeMap()); - } - return myIdToVersionToResourceMap.get(theIdPart); - } - - @Read(version = true) - public IBaseResource read(@IdParam IIdType theId) { - TreeMap versions = myIdToVersionToResourceMap.get(theId.getIdPart()); - if (versions == null || versions.isEmpty()) { - throw new ResourceNotFoundException(theId); - } - - if (theId.hasVersionIdPart()) { - Long versionId = theId.getVersionIdPartAsLong(); - if (!versions.containsKey(versionId)) { - throw new ResourceNotFoundException(theId); - } else { - T resource = versions.get(versionId); - if (resource == null) { - throw new ResourceGoneException(theId); - } - return resource; - } - - } else { - return versions.lastEntry().getValue(); - } - } - - @Search - public List search() { - List retVal = new ArrayList<>(); - - for (TreeMap next : myIdToVersionToResourceMap.values()) { - if (next.isEmpty() == false) { - retVal.add(next.lastEntry().getValue()); - } - } - - return retVal; - } - - private IIdType store(@ResourceParam T theResource, String theIdPart, Long theVersionIdPart) { - IIdType id = myFhirContext.getVersion().newIdType(); - id.setParts(null, myResourceName, theIdPart, Long.toString(theVersionIdPart)); - if (theResource != null) { - theResource.setId(id); - } - - TreeMap versionToResource = getVersionToResource(theIdPart); - versionToResource.put(theVersionIdPart, theResource); - - ourLog.info("Storing resource with ID: {}", id.getValue()); - - return id; - } - - public MethodOutcome update(T theResource) { - String idPartAsString = theResource.getIdElement().getIdPart(); - TreeMap versionToResource = getVersionToResource(idPartAsString); - - Long versionIdPart; - Boolean created; - if (versionToResource.isEmpty()) { - versionIdPart = 1L; - created = true; - } else { - versionIdPart = versionToResource.lastKey() + 1L; - created = false; - } - - IIdType id = store(theResource, idPartAsString, versionIdPart); - - return new MethodOutcome() - .setCreated(created) - .setId(id); - } -} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/HashMapResourceProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/HashMapResourceProvider.java index e876efa85fc..59a14d9f2b7 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/HashMapResourceProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/HashMapResourceProvider.java @@ -34,6 +34,7 @@ import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.util.ValidateUtil; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -44,6 +45,8 @@ import org.slf4j.LoggerFactory; import java.util.*; import java.util.concurrent.atomic.AtomicLong; +import static org.apache.commons.lang3.StringUtils.isBlank; + /** * This class is a simple implementation of the resource provider * interface that uses a HashMap to store all resources in memory. @@ -338,8 +341,15 @@ public class HashMapResourceProvider implements IResour return id; } + /** + * @param theConditional This is provided only so that subclasses can implement if they want + */ @Update - public MethodOutcome update(@ResourceParam T theResource) { + public MethodOutcome update( + @ResourceParam T theResource, + @ConditionalUrlParam String theConditional) { + + ValidateUtil.isTrueOrThrowInvalidRequest(isBlank(theConditional), "This server doesn't support conditional update"); String idPartAsString = theResource.getIdElement().getIdPart(); TreeMap versionToResource = getVersionToResource(idPartAsString); diff --git a/osgi/hapi-fhir-karaf-features/pom.xml b/osgi/hapi-fhir-karaf-features/pom.xml index 8a921d1a981..1ca1eeb8774 100644 --- a/osgi/hapi-fhir-karaf-features/pom.xml +++ b/osgi/hapi-fhir-karaf-features/pom.xml @@ -17,8 +17,8 @@ features.xml - 1.10.1 - 6.0.0 + 1.8.6 + 3.2.2 diff --git a/osgi/hapi-fhir-karaf-integration-tests/pom.xml b/osgi/hapi-fhir-karaf-integration-tests/pom.xml index 20a8d47e6cb..d8d98bd71ae 100644 --- a/osgi/hapi-fhir-karaf-integration-tests/pom.xml +++ b/osgi/hapi-fhir-karaf-integration-tests/pom.xml @@ -33,7 +33,7 @@ - 4.12.0 + 4.9.1 diff --git a/src/changes/changes.xml b/src/changes/changes.xml index d01eb6ab8c2..e4362f3952c 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -262,6 +262,10 @@ / R4 (and future) versions of FHIR. This should greatly improve maintainability and consistency for transaction processing. + + ResponseHighlighterInterceptor now displays the total size of the output and + an estimate of the transfer time at the bottom of the response. +