diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java
index 869de46d65d..5feb2832010 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java
@@ -619,12 +619,14 @@ public class IdDt extends UriDt implements /*IPrimitiveDatatype
- * See Updates to Tags, Profiles, and Security Labels for a description of the logic that the default behaviour folows. + * See Updates to Tags, Profiles, and Security Labels for a description of the logic that the default behaviour folows. *
* - * @param theEntity The entity being updated (Do not modify the entity! Undefined behaviour will occur!) - * @param theTag The tag + * @param theTag The tag * @return Returnstrue
if the tag should be removed
*/
- protected boolean shouldDroppedTagBeRemovedOnUpdate(ResourceTable theEntity, ResourceTag theTag) {
- if (theTag.getTag().getTagType() == TagTypeEnum.PROFILE) {
+ protected boolean shouldDroppedTagBeRemovedOnUpdate(RequestDetails theRequest, ResourceTag theTag) {
+
+ SetPatient
* @param thePartsChoices E.g. [[gender=male], [name=SMITH, name=JOHN]]
*/
- public static Set extractCompositeStringUniquesValueChains(String theResourceType, List> thePartsChoices) {
+ public static Set extractCompositeStringUniquesValueChains(String
+ theResourceType, List> thePartsChoices) {
for (List next : thePartsChoices) {
- for (Iterator iter = next.iterator(); iter.hasNext(); ) {
- if (isBlank(iter.next())) {
- iter.remove();
- }
- }
+ next.removeIf(StringUtils::isBlank);
if (next.isEmpty()) {
return Collections.emptySet();
}
@@ -2288,19 +2324,16 @@ public abstract class BaseHapiFhirDao implements IDao {
return Collections.emptySet();
}
- Collections.sort(thePartsChoices, new Comparator>() {
- @Override
- public int compare(List o1, List o2) {
- String str1 = null;
- String str2 = null;
- if (o1.size() > 0) {
- str1 = o1.get(0);
- }
- if (o2.size() > 0) {
- str2 = o2.get(0);
- }
- return StringUtils.compare(str1, str2);
+ thePartsChoices.sort((o1, o2) -> {
+ String str1 = null;
+ String str2 = null;
+ if (o1.size() > 0) {
+ str1 = o1.get(0);
}
+ if (o2.size() > 0) {
+ str2 = o2.get(0);
+ }
+ return compare(str1, str2);
});
List values = new ArrayList<>();
@@ -2309,7 +2342,8 @@ public abstract class BaseHapiFhirDao implements IDao {
return queryStringsToPopulate;
}
- private static void extractCompositeStringUniquesValueChains(String theResourceType, List> thePartsChoices, List theValues, Set theQueryStringsToPopulate) {
+ private static void extractCompositeStringUniquesValueChains(String
+ theResourceType, List> thePartsChoices, List theValues, Set theQueryStringsToPopulate) {
if (thePartsChoices.size() > 0) {
List nextList = thePartsChoices.get(0);
Collections.sort(nextList);
@@ -2446,14 +2480,15 @@ public abstract class BaseHapiFhirDao implements IDao {
}
private static List toBaseCodingList(List theSecurityLabels) {
- ArrayList retVal = new ArrayList(theSecurityLabels.size());
+ ArrayList retVal = new ArrayList<>(theSecurityLabels.size());
for (IBaseCoding next : theSecurityLabels) {
retVal.add((BaseCodingDt) next);
}
return retVal;
}
- protected static Long translateForcedIdToPid(String theResourceName, String theResourceId, IForcedIdDao theForcedIdDao) {
+ protected static Long translateForcedIdToPid(String theResourceName, String theResourceId, IForcedIdDao
+ theForcedIdDao) {
return translateForcedIdToPids(new IdDt(theResourceName, theResourceId), theForcedIdDao).get(0);
}
@@ -2482,7 +2517,8 @@ public abstract class BaseHapiFhirDao implements IDao {
}
}
- public static SearchParameterMap translateMatchUrl(IDao theCallingDao, FhirContext theContext, String theMatchUrl, RuntimeResourceDefinition resourceDef) {
+ public static SearchParameterMap translateMatchUrl(IDao theCallingDao, FhirContext theContext, String
+ theMatchUrl, RuntimeResourceDefinition resourceDef) {
SearchParameterMap paramMap = new SearchParameterMap();
List parameters = translateMatchUrl(theMatchUrl);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
index 1553d97c20f..0d839c8dc85 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
@@ -46,6 +46,7 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor;
import ca.uhn.fhir.rest.server.method.SearchMethodBinding;
+import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.*;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.*;
@@ -175,7 +176,7 @@ public abstract class BaseHapiFhirResourceDao extends B
}
@Override
- public DaoMethodOutcome delete(IIdType theId, List theDeleteConflicts, RequestDetails theRequestDetails) {
+ public DaoMethodOutcome delete(IIdType theId, List theDeleteConflicts, RequestDetails theReques) {
if (theId == null || !theId.hasIdPart()) {
throw new InvalidRequestException("Can not perform delete, no ID provided");
}
@@ -208,12 +209,12 @@ public abstract class BaseHapiFhirResourceDao extends B
T resourceToDelete = toResource(myResourceType, entity, false);
// Notify IServerOperationInterceptors about pre-action call
- if (theRequestDetails != null) {
- theRequestDetails.getRequestOperationCallback().resourcePreDelete(resourceToDelete);
+ if (theReques != null) {
+ theReques.getRequestOperationCallback().resourcePreDelete(resourceToDelete);
}
for (IServerInterceptor next : getConfig().getInterceptors()) {
if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourcePreDelete(theRequestDetails, resourceToDelete);
+ ((IServerOperationInterceptor) next).resourcePreDelete(theReques, resourceToDelete);
}
}
@@ -222,23 +223,23 @@ public abstract class BaseHapiFhirResourceDao extends B
preDelete(resourceToDelete, entity);
// Notify interceptors
- if (theRequestDetails != null) {
- ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), theId.getResourceType(), theId);
+ if (theReques != null) {
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theReques, getContext(), theId.getResourceType(), theId);
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
}
Date updateTime = new Date();
- ResourceTable savedEntity = updateEntity(null, entity, updateTime, updateTime);
+ ResourceTable savedEntity = updateEntity(theReques, null, entity, updateTime, updateTime);
resourceToDelete.setId(entity.getIdDt());
// Notify JPA interceptors
- if (theRequestDetails != null) {
- ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), theId.getResourceType(), theId);
- theRequestDetails.getRequestOperationCallback().resourceDeleted(resourceToDelete);
+ if (theReques != null) {
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theReques, getContext(), theId.getResourceType(), theId);
+ theReques.getRequestOperationCallback().resourceDeleted(resourceToDelete);
}
for (IServerInterceptor next : getConfig().getInterceptors()) {
if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourceDeleted(theRequestDetails, resourceToDelete);
+ ((IServerOperationInterceptor) next).resourceDeleted(theReques, resourceToDelete);
}
}
@@ -272,7 +273,7 @@ public abstract class BaseHapiFhirResourceDao extends B
* transaction processors
*/
@Override
- public DeleteMethodOutcome deleteByUrl(String theUrl, List deleteConflicts, RequestDetails theRequestDetails) {
+ public DeleteMethodOutcome deleteByUrl(String theUrl, List deleteConflicts, RequestDetails theRequest) {
StopWatch w = new StopWatch();
Set resource = processMatchUrl(theUrl, myResourceType);
@@ -290,12 +291,12 @@ public abstract class BaseHapiFhirResourceDao extends B
T resourceToDelete = toResource(myResourceType, entity, false);
// Notify IServerOperationInterceptors about pre-action call
- if (theRequestDetails != null) {
- theRequestDetails.getRequestOperationCallback().resourcePreDelete(resourceToDelete);
+ if (theRequest != null) {
+ theRequest.getRequestOperationCallback().resourcePreDelete(resourceToDelete);
}
for (IServerInterceptor next : getConfig().getInterceptors()) {
if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourcePreDelete(theRequestDetails, resourceToDelete);
+ ((IServerOperationInterceptor) next).resourcePreDelete(theRequest, resourceToDelete);
}
}
@@ -303,24 +304,24 @@ public abstract class BaseHapiFhirResourceDao extends B
// Notify interceptors
IdDt idToDelete = entity.getIdDt();
- if (theRequestDetails != null) {
- ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, idToDelete.getResourceType(), idToDelete);
+ if (theRequest != null) {
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, idToDelete.getResourceType(), idToDelete);
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
}
// Perform delete
Date updateTime = new Date();
- updateEntity(null, entity, updateTime, updateTime);
+ updateEntity(theRequest, null, entity, updateTime, updateTime);
resourceToDelete.setId(entity.getIdDt());
// Notify JPA interceptors
- if (theRequestDetails != null) {
- theRequestDetails.getRequestOperationCallback().resourceDeleted(resourceToDelete);
- ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, idToDelete.getResourceType(), idToDelete);
+ if (theRequest != null) {
+ theRequest.getRequestOperationCallback().resourceDeleted(resourceToDelete);
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, idToDelete.getResourceType(), idToDelete);
}
for (IServerInterceptor next : getConfig().getInterceptors()) {
if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourceDeleted(theRequestDetails, resourceToDelete);
+ ((IServerOperationInterceptor) next).resourceDeleted(theRequest, resourceToDelete);
}
}
}
@@ -366,7 +367,7 @@ public abstract class BaseHapiFhirResourceDao extends B
}
}
- private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing, Date theUpdateTime, RequestDetails theRequestDetails) {
+ private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing, Date theUpdateTime, RequestDetails theRequest) {
StopWatch w = new StopWatch();
preProcessResourceForStorage(theResource);
@@ -405,23 +406,23 @@ public abstract class BaseHapiFhirResourceDao extends B
}
// Notify interceptors
- if (theRequestDetails != null) {
- ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), theResource);
+ if (theRequest != null) {
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, getContext(), theResource);
notifyInterceptors(RestOperationTypeEnum.CREATE, requestDetails);
}
// Notify JPA interceptors
- if (theRequestDetails != null) {
- theRequestDetails.getRequestOperationCallback().resourcePreCreate(theResource);
+ if (theRequest != null) {
+ theRequest.getRequestOperationCallback().resourcePreCreate(theResource);
}
for (IServerInterceptor next : getConfig().getInterceptors()) {
if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourcePreCreate(theRequestDetails, theResource);
+ ((IServerOperationInterceptor) next).resourcePreCreate(theRequest, theResource);
}
}
// Perform actual DB update
- ResourceTable updatedEntity = updateEntity(theResource, entity, null, thePerformIndexing, thePerformIndexing, theUpdateTime, false, thePerformIndexing);
+ ResourceTable updatedEntity = updateEntity(theRequest, theResource, entity, null, thePerformIndexing, thePerformIndexing, theUpdateTime, false, thePerformIndexing);
theResource.setId(entity.getIdDt());
@@ -436,12 +437,12 @@ public abstract class BaseHapiFhirResourceDao extends B
// Notify JPA interceptors
if (!updatedEntity.isUnchangedInCurrentOperation()) {
- if (theRequestDetails != null) {
- theRequestDetails.getRequestOperationCallback().resourceCreated(theResource);
+ if (theRequest != null) {
+ theRequest.getRequestOperationCallback().resourceCreated(theResource);
}
for (IServerInterceptor next : getConfig().getInterceptors()) {
if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourceCreated(theRequestDetails, theResource);
+ ((IServerOperationInterceptor) next).resourceCreated(theRequest, theResource);
}
}
}
@@ -931,7 +932,7 @@ public abstract class BaseHapiFhirResourceDao extends B
public void reindex(T theResource, ResourceTable theEntity) {
ourLog.debug("Indexing resource {} - PID {}", theResource.getIdElement().getValue(), theEntity.getId());
CURRENTLY_REINDEXING.put(theResource, Boolean.TRUE);
- updateEntity(theResource, theEntity, null, true, false, theEntity.getUpdatedDate(), true, false);
+ updateEntity(null, theResource, theEntity, null, true, false, theEntity.getUpdatedDate(), true, false);
CURRENTLY_REINDEXING.put(theResource, null);
}
@@ -1232,7 +1233,7 @@ public abstract class BaseHapiFhirResourceDao extends B
/*
* If we aren't indexing, that means we're doing this inside a transaction.
- * The transaction will do the actual storate to the database a bit later on,
+ * The transaction will do the actual storage to the database a bit later on,
* after placeholder IDs have been replaced, by calling {@link #updateInternal}
* directly. So we just bail now.
*/
@@ -1245,7 +1246,7 @@ public abstract class BaseHapiFhirResourceDao extends B
/*
* Otherwise, we're not in a transaction
*/
- ResourceTable savedEntity = updateInternal(theResource, thePerformIndexing, theForceUpdateVersion, theRequestDetails, entity, resourceId, oldResource);
+ ResourceTable savedEntity = updateInternal(theRequestDetails, theResource, thePerformIndexing, theForceUpdateVersion, theRequestDetails, entity, resourceId, oldResource);
DaoMethodOutcome outcome = toMethodOutcome(savedEntity, theResource).setCreated(false);
if (!thePerformIndexing) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java
index 0fc0f611737..6b1e936532d 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java
@@ -8,6 +8,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.util.ExpungeOptions;
import ca.uhn.fhir.jpa.util.ExpungeOutcome;
import ca.uhn.fhir.jpa.util.ReindexFailureException;
+import ca.uhn.fhir.jpa.util.SingleItemLoadingCache;
import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
@@ -15,6 +16,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
@@ -23,16 +25,10 @@ import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import javax.persistence.Query;
-import javax.persistence.Tuple;
import javax.persistence.TypedQuery;
-import javax.persistence.criteria.CriteriaBuilder;
-import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.Root;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import static org.apache.commons.lang3.StringUtils.isBlank;
@@ -74,6 +70,10 @@ public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao> myResourceCountsCache;
+
private int doPerformReindexingPass(final Integer theCount) {
/*
@@ -165,23 +165,23 @@ public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao getResourceCounts() {
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- CriteriaQuery cq = builder.createTupleQuery();
- Root> from = cq.from(ResourceTable.class);
- cq.multiselect(from.get("myResourceType").as(String.class), builder.count(from.get("myResourceType")).as(Long.class));
- cq.groupBy(from.get("myResourceType"));
-
- TypedQuery q = myEntityManager.createQuery(cq);
-
Map retVal = new HashMap<>();
- for (Tuple next : q.getResultList()) {
- String resourceName = next.get(0, String.class);
- Long count = next.get(1, Long.class);
- retVal.put(resourceName, count);
+
+ List> counts = myResourceTableDao.getResourceCounts();
+ for (Map, ?> next : counts) {
+ retVal.put(next.get("type").toString(), Long.parseLong(next.get("count").toString()));
}
+
return retVal;
}
+ @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
+ @Nullable
+ @Override
+ public Map getResourceCountsFromCache() {
+ return myResourceCountsCache.get();
+ }
+
@Override
public IBundleProvider history(Date theSince, Date theUntil, RequestDetails theRequestDetails) {
if (theRequestDetails != null) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSubscriptionDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSubscriptionDstu2.java
index d0c5d73ac40..72b44e1b251 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSubscriptionDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSubscriptionDstu2.java
@@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.model.dstu2.resource.Subscription;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
import ca.uhn.fhir.parser.DataFormatException;
+import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@@ -39,8 +40,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2 implements IFhirResourceDaoSubscription {
- private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoSubscriptionDstu2.class);
-
@Autowired
private ISubscriptionTableDao mySubscriptionTableDao;
@@ -74,9 +73,9 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2 {
InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource);
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
if (updatedEntities.contains(nextOutcome.getEntity())) {
- updateInternal(nextResource, true, false, theRequestDetails, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource());
+ updateInternal(theRequestDetails, nextResource, true, false, theRequestDetails, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource());
} else if (!nonUpdatedEntities.contains(nextOutcome.getEntity())) {
- updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, updateTime, false, true);
+ updateEntity(theRequestDetails, nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, updateTime, false, true);
}
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java
index 401ae09689c..2a1e88a81af 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java
@@ -25,7 +25,10 @@ import ca.uhn.fhir.jpa.util.ExpungeOutcome;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import javax.annotation.Nullable;
import java.util.Date;
import java.util.Map;
@@ -41,6 +44,14 @@ public interface IFhirSystemDao extends IDao {
Map getResourceCounts();
+ /**
+ *Returns a cached count of resources using a cache that regularly
+ * refreshes in the background. This method will never
+ */
+ @Nullable
+ Map getResourceCountsFromCache();
+
+
IBundleProvider history(Date theDate, Date theUntil, RequestDetails theRequestDetails);
/**
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java
index c396a24fb57..cd9f23f01b4 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java
@@ -8,6 +8,9 @@ import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
+import java.util.List;
+import java.util.Map;
+
/*
* #%L
* HAPI FHIR JPA Server
@@ -42,6 +45,9 @@ public interface IResourceTableDao extends JpaRepository {
@Query("SELECT t.myId FROM ResourceTable t WHERE t.myIndexStatus IS NULL")
Slice findUnindexed(Pageable thePageRequest);
+ @Query("SELECT t.myResourceType as type, COUNT(*) as count FROM ResourceTable t GROUP BY t.myResourceType")
+ List> getResourceCounts();
+
@Modifying
@Query("UPDATE ResourceTable r SET r.myIndexStatus = null WHERE r.myResourceType = :restype")
int markResourcesOfTypeAsRequiringReindexing(@Param("restype") String theResourceType);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java
index 8bc6722ee72..29cfecb2940 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java
@@ -199,9 +199,9 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3 {
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
if (updatedEntities.contains(nextOutcome.getEntity())) {
- updateInternal(nextResource, true, false, theRequestDetails, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource());
+ updateInternal(theRequestDetails, nextResource, true, false, theRequestDetails, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource());
} else if (!nonUpdatedEntities.contains(nextOutcome.getEntity())) {
- updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, theUpdateTime, false, true);
+ updateEntity(theRequestDetails, nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, theUpdateTime, false, true);
}
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java
index 030683b562e..0f3bd4cdec8 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java
@@ -47,6 +47,7 @@ import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.IdType;
import org.springframework.beans.factory.annotation.Autowired;
+import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -172,9 +173,9 @@ public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4 i
}
@Override
- protected ResourceTable updateEntity(IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
+ protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
- ResourceTable retVal = super.updateEntity(theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
+ ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
CodeSystem cs = (CodeSystem) theResource;
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSubscriptionR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSubscriptionR4.java
index 8fb7349189e..98ecc91d3e9 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSubscriptionR4.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSubscriptionR4.java
@@ -28,6 +28,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.api.EncodingEnum;
+import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.apache.commons.lang3.ObjectUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -36,7 +37,6 @@ import org.hl7.fhir.r4.model.Subscription;
import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType;
import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.transaction.PlatformTransactionManager;
import javax.annotation.Nullable;
import java.util.Date;
@@ -75,9 +75,9 @@ public class FhirResourceDaoSubscriptionR4 extends FhirResourceDaoR4 {
}
@SuppressWarnings("unchecked")
- private Map doTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date theUpdateTime, Set theAllIds,
+ private Map doTransactionWriteOperations(RequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date theUpdateTime, Set theAllIds,
Map theIdSubstitutions, Map theIdToPersistedOutcome, Bundle theResponse, IdentityHashMap theOriginalRequestOrder, List theEntries) {
Set deletedResources = new HashSet<>();
List deleteConflicts = new ArrayList<>();
@@ -545,9 +544,9 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao {
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
if (updatedEntities.contains(nextOutcome.getEntity())) {
- updateInternal(nextResource, true, false, theRequestDetails, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource());
+ updateInternal(theRequestDetails, nextResource, true, false, theRequestDetails, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource());
} else if (!nonUpdatedEntities.contains(nextOutcome.getEntity())) {
- updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, theUpdateTime, false, true);
+ updateEntity(theRequestDetails, nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, theUpdateTime, false, true);
}
}
@@ -713,7 +712,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao {
}
private static void handleTransactionCreateOrUpdateOutcome(Map idSubstitutions, Map idToPersistedOutcome, IdType nextResourceId, DaoMethodOutcome outcome,
- BundleEntryComponent newEntry, String theResourceType, IBaseResource theRes, ServletRequestDetails theRequestDetails) {
+ BundleEntryComponent newEntry, String theResourceType, IBaseResource theRes, RequestDetails theRequestDetails) {
IdType newId = (IdType) outcome.getId().toUnqualifiedVersionless();
IdType resourceId = isPlaceholder(nextResourceId) ? nextResourceId : nextResourceId.toUnqualifiedVersionless();
if (newId.equals(resourceId) == false) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaConformanceProviderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaConformanceProviderDstu2.java
index 011225222cb..92f53cd1308 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaConformanceProviderDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaConformanceProviderDstu2.java
@@ -28,6 +28,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
+import ca.uhn.fhir.jpa.util.SingleItemLoadingCache;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Conformance;
@@ -44,6 +45,8 @@ import ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider;
import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.ExtensionConstants;
+import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
+
public class JpaConformanceProviderDstu2 extends ServerConformanceProvider {
private volatile Conformance myCachedValue;
@@ -52,16 +55,7 @@ public class JpaConformanceProviderDstu2 extends ServerConformanceProvider {
private boolean myIncludeResourceCounts;
private RestfulServer myRestfulServer;
private IFhirSystemDao mySystemDao;
-
- /**
- * Constructor
- */
- @CoverageIgnore
- public JpaConformanceProviderDstu2(){
- super();
- super.setCache(false);
- setIncludeResourceCounts(true);
- }
+ private SingleItemLoadingCache> myResourceCountsCache;
/**
* Constructor
@@ -79,10 +73,11 @@ public class JpaConformanceProviderDstu2 extends ServerConformanceProvider {
public Conformance getServerConformance(HttpServletRequest theRequest) {
Conformance retVal = myCachedValue;
- Map counts = Collections.emptyMap();
+ Map counts = null;
if (myIncludeResourceCounts) {
- counts = mySystemDao.getResourceCounts();
+ counts = mySystemDao.getResourceCountsFromCache();
}
+ counts = defaultIfNull(counts, Collections.emptyMap());
FhirContext ctx = myRestfulServer.getFhirContext();
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaResourceProviderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaResourceProviderDstu2.java
index 009bd14a0d4..770aa01c73f 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaResourceProviderDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaResourceProviderDstu2.java
@@ -25,7 +25,9 @@ import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
+import ca.uhn.fhir.model.primitive.BooleanDt;
import ca.uhn.fhir.model.primitive.IdDt;
+import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.MethodOutcome;
@@ -79,35 +81,33 @@ public class JpaResourceProviderDstu2 extends BaseJpaResour
}
@Operation(name = JpaConstants.OPERATION_NAME_EXPUNGE, idempotent = false, returnParameters = {
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = org.hl7.fhir.r4.model.IntegerType.class)
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerDt.class)
})
public Parameters expunge(
@IdParam IIdType theIdParam,
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) org.hl7.fhir.r4.model.IntegerType theLimit,
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) org.hl7.fhir.r4.model.BooleanType theExpungeDeletedResources,
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) org.hl7.fhir.r4.model.BooleanType theExpungeOldVersions
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerDt theLimit,
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanDt theExpungeDeletedResources,
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanDt theExpungeOldVersions
) {
org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(theIdParam, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null);
return JpaSystemProviderDstu2.toExpungeResponse(retVal);
}
@Operation(name = JpaConstants.OPERATION_NAME_EXPUNGE, idempotent = false, returnParameters = {
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = org.hl7.fhir.r4.model.IntegerType.class)
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerDt.class)
})
public Parameters expunge(
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) org.hl7.fhir.r4.model.IntegerType theLimit,
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) org.hl7.fhir.r4.model.BooleanType theExpungeDeletedResources,
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) org.hl7.fhir.r4.model.BooleanType theExpungeOldVersions
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerDt theLimit,
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanDt theExpungeDeletedResources,
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanDt theExpungeOldVersions
) {
org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(null, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null);
return JpaSystemProviderDstu2.toExpungeResponse(retVal);
}
- //@formatter:off
@Operation(name = OPERATION_NAME_META, idempotent = true, returnParameters = {
@OperationParam(name = "return", type = MetaDt.class)
})
- //@formatter:on
public Parameters meta(RequestDetails theRequestDetails) {
Parameters parameters = new Parameters();
MetaDt metaGetOperation = getDao().metaGetOperation(MetaDt.class, theRequestDetails);
@@ -115,11 +115,9 @@ public class JpaResourceProviderDstu2 extends BaseJpaResour
return parameters;
}
- //@formatter:off
@Operation(name = OPERATION_NAME_META, idempotent = true, returnParameters = {
@OperationParam(name = "return", type = MetaDt.class)
})
- //@formatter:on
public Parameters meta(@IdParam IdDt theId, RequestDetails theRequestDetails) {
Parameters parameters = new Parameters();
MetaDt metaGetOperation = getDao().metaGetOperation(MetaDt.class, theId, theRequestDetails);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaSystemProviderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaSystemProviderDstu2.java
index 1b608546029..87828c70b0a 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaSystemProviderDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaSystemProviderDstu2.java
@@ -187,8 +187,8 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProviderDstu2Plus counts = mySystemDao.getResourceCounts();
- counts = new TreeMap(counts);
+ Map counts = mySystemDao.getResourceCountsFromCache();
+ counts = new TreeMap<>(counts);
for (Entry nextEntry : counts.entrySet()) {
retVal.addParameter().setName(new StringDt(nextEntry.getKey())).setValue(new IntegerDt(nextEntry.getValue().intValue()));
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaConformanceProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaConformanceProviderDstu3.java
index 8869894c492..8f2962d182e 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaConformanceProviderDstu3.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaConformanceProviderDstu3.java
@@ -35,6 +35,8 @@ import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.ExtensionConstants;
+import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
+
public class JpaConformanceProviderDstu3 extends org.hl7.fhir.dstu3.hapi.rest.server.ServerCapabilityStatementProvider {
private volatile CapabilityStatement myCachedValue;
@@ -70,10 +72,11 @@ public class JpaConformanceProviderDstu3 extends org.hl7.fhir.dstu3.hapi.rest.se
public CapabilityStatement getServerConformance(HttpServletRequest theRequest) {
CapabilityStatement retVal = myCachedValue;
- Map counts = Collections.emptyMap();
+ Map counts = null;
if (myIncludeResourceCounts) {
- counts = mySystemDao.getResourceCounts();
+ counts = mySystemDao.getResourceCountsFromCache();
}
+ counts = defaultIfNull(counts, Collections.emptyMap());
retVal = super.getServerConformance(theRequest);
for (CapabilityStatementRestComponent nextRest : retVal.getRest()) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java
index 384a3185250..0248d74521e 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java
@@ -31,9 +31,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.convertors.VersionConvertor_30_40;
-import org.hl7.fhir.dstu3.model.IdType;
-import org.hl7.fhir.dstu3.model.Meta;
-import org.hl7.fhir.dstu3.model.Parameters;
+import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IIdType;
@@ -83,13 +81,13 @@ public class JpaResourceProviderDstu3 extends BaseJpaRes
}
@Operation(name = JpaConstants.OPERATION_NAME_EXPUNGE, idempotent = false, returnParameters = {
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = org.hl7.fhir.r4.model.IntegerType.class)
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
})
public Parameters expunge(
@IdParam IIdType theIdParam,
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) org.hl7.fhir.r4.model.IntegerType theLimit,
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) org.hl7.fhir.r4.model.BooleanType theExpungeDeletedResources,
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) org.hl7.fhir.r4.model.BooleanType theExpungeOldVersions
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions
) {
org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(theIdParam, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null);
try {
@@ -100,12 +98,12 @@ public class JpaResourceProviderDstu3 extends BaseJpaRes
}
@Operation(name = JpaConstants.OPERATION_NAME_EXPUNGE, idempotent = false, returnParameters = {
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = org.hl7.fhir.r4.model.IntegerType.class)
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
})
public Parameters expunge(
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) org.hl7.fhir.r4.model.IntegerType theLimit,
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) org.hl7.fhir.r4.model.BooleanType theExpungeDeletedResources,
- @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) org.hl7.fhir.r4.model.BooleanType theExpungeOldVersions
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
+ @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions
) {
org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(null, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null);
try {
@@ -139,11 +137,9 @@ public class JpaResourceProviderDstu3 extends BaseJpaRes
return parameters;
}
- //@formatter:off
@Operation(name = OPERATION_NAME_META_ADD, idempotent = true, returnParameters = {
@OperationParam(name = "return", type = Meta.class)
})
- //@formatter:on
public Parameters metaAdd(@IdParam IdType theId, @OperationParam(name = "meta") Meta theMeta, RequestDetails theRequestDetails) {
if (theMeta == null) {
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
@@ -154,11 +150,9 @@ public class JpaResourceProviderDstu3 extends BaseJpaRes
return parameters;
}
- //@formatter:off
@Operation(name = OPERATION_NAME_META_DELETE, idempotent = true, returnParameters = {
@OperationParam(name = "return", type = Meta.class)
})
- //@formatter:on
public Parameters metaDelete(@IdParam IdType theId, @OperationParam(name = "meta") Meta theMeta, RequestDetails theRequestDetails) {
if (theMeta == null) {
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java
index dcc97567391..035a5f4265b 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java
@@ -191,8 +191,8 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus counts = mySystemDao.getResourceCounts();
- counts = new TreeMap(counts);
+ Map counts = mySystemDao.getResourceCountsFromCache();
+ counts = new TreeMap<>(counts);
for (Entry nextEntry : counts.entrySet()) {
retVal.addParameter().setName((nextEntry.getKey())).setValue(new IntegerType(nextEntry.getValue().intValue()));
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/JpaConformanceProviderR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/JpaConformanceProviderR4.java
index a145e11880d..0bf42f6cb5e 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/JpaConformanceProviderR4.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/JpaConformanceProviderR4.java
@@ -35,6 +35,8 @@ import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.ExtensionConstants;
+import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
+
public class JpaConformanceProviderR4 extends org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider {
private volatile CapabilityStatement myCachedValue;
@@ -70,10 +72,11 @@ public class JpaConformanceProviderR4 extends org.hl7.fhir.r4.hapi.rest.server.S
public CapabilityStatement getServerConformance(HttpServletRequest theRequest) {
CapabilityStatement retVal = myCachedValue;
- Map counts = Collections.emptyMap();
+ Map counts = null;
if (myIncludeResourceCounts) {
- counts = mySystemDao.getResourceCounts();
+ counts = mySystemDao.getResourceCountsFromCache();
}
+ counts = defaultIfNull(counts, Collections.emptyMap());
retVal = super.getServerConformance(theRequest);
for (CapabilityStatementRestComponent nextRest : retVal.getRest()) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/JpaSystemProviderR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/JpaSystemProviderR4.java
index c4b0064e969..99351665854 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/JpaSystemProviderR4.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/JpaSystemProviderR4.java
@@ -177,8 +177,8 @@ public class JpaSystemProviderR4 extends BaseJpaSystemProviderDstu2Plus counts = mySystemDao.getResourceCounts();
- counts = new TreeMap(counts);
+ Map counts = mySystemDao.getResourceCountsFromCache();
+ counts = new TreeMap<>(counts);
for (Entry nextEntry : counts.entrySet()) {
retVal.addParameter().setName((nextEntry.getKey())).setValue(new IntegerType(nextEntry.getValue().intValue()));
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java
index 9883fd242cd..3363844c109 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java
@@ -92,4 +92,11 @@ public class JpaConstants {
* Output parameter name for the $expunge operation
*/
public static final String OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT = "count";
+ /**
+ * Header name for the "X-Meta-Snapshot-Mode" header, which
+ * specifies that properties in meta (tags, profiles, security labels)
+ * should be treated as a snapshot, meaning that these things will
+ * be removed if they are nt explicitly included in updates
+ */
+ public static final String HEADER_META_SNAPSHOT_MODE = "X-Meta-Snapshot-Mode";
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SingleItemLoadingCache.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SingleItemLoadingCache.java
new file mode 100644
index 00000000000..f8b0ec6981a
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SingleItemLoadingCache.java
@@ -0,0 +1,91 @@
+package ca.uhn.fhir.jpa.util;
+
+/*-
+ * #%L
+ * Smile CDR - CDR
+ * %%
+ * Copyright (C) 2016 - 2018 Simpatico Intelligent Systems Inc
+ * %%
+ * All rights reserved.
+ * #L%
+ */
+
+import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
+import com.google.common.annotations.VisibleForTesting;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.annotation.Scheduled;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * This is a simple cache for CapabilityStatement resources to
+ * be returned as server metadata.
+ */
+public class SingleItemLoadingCache {
+ private static final Logger ourLog = LoggerFactory.getLogger(SingleItemLoadingCache.class);
+ private static Long ourNowForUnitTest;
+ private final Callable myFetcher;
+ private volatile long myCacheMillis;
+ private AtomicReference myCapabilityStatement = new AtomicReference<>();
+ private long myLastFetched;
+
+ /**
+ * Constructor
+ */
+ public SingleItemLoadingCache(Callable theFetcher) {
+ myFetcher = theFetcher;
+ }
+
+ public synchronized void clear() {
+ ourLog.info("Clearning cache");
+ myCapabilityStatement.set(null);
+ myLastFetched = 0;
+ }
+
+ public synchronized T get() {
+ return myCapabilityStatement.get();
+ }
+
+ private T refresh() {
+ T retVal;
+ try {
+ retVal = myFetcher.call();
+ } catch (Exception e) {
+ throw new InternalErrorException(e);
+ }
+
+ myCapabilityStatement.set(retVal);
+ myLastFetched = now();
+ return retVal;
+ }
+
+ public void setCacheMillis(long theCacheMillis) {
+ myCacheMillis = theCacheMillis;
+ }
+
+ @Scheduled(fixedDelay = 60000)
+ public void update() {
+ if (myCacheMillis > 0) {
+ long now = now();
+ long expiry = now - myCacheMillis;
+ if (myLastFetched < expiry) {
+ refresh();
+ }
+ }
+ }
+
+ private static long now() {
+ if (ourNowForUnitTest != null) {
+ return ourNowForUnitTest;
+ }
+ return System.currentTimeMillis();
+ }
+
+ @VisibleForTesting
+ static void setNowForUnitTest(Long theNowForUnitTest) {
+ ourNowForUnitTest = theNowForUnitTest;
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SpringObjectCaster.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SpringObjectCaster.java
deleted file mode 100644
index 90483f46a30..00000000000
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SpringObjectCaster.java
+++ /dev/null
@@ -1,47 +0,0 @@
-
-package ca.uhn.fhir.jpa.util;
-
-/*-
- * #%L
- * HAPI FHIR JPA Server
- * %%
- * 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 org.springframework.aop.framework.Advised;
-import org.springframework.aop.support.AopUtils;
-
-/**
- * Utility to get the Spring proxy object's target object
- */
-public class SpringObjectCaster {
-
- /**
- * Retrieve the Spring proxy object's target object
- * @param proxy
- * @param clazz
- * @param
- * @return
- * @throws Exception
- */
- public static T getTargetObject(Object proxy, Class clazz) throws Exception {
- while( (AopUtils.isJdkDynamicProxy(proxy))) {
- return clazz.cast(getTargetObject(((Advised)proxy).getTargetSource().getTarget(), clazz));
- }
-
- return clazz.cast(proxy);
- }
-}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTagSnapshotTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTagSnapshotTest.java
new file mode 100644
index 00000000000..632dd8bfa86
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTagSnapshotTest.java
@@ -0,0 +1,149 @@
+package ca.uhn.fhir.jpa.dao.r4;
+
+import ca.uhn.fhir.jpa.util.JpaConstants;
+import ca.uhn.fhir.util.TestUtil;
+import com.google.common.collect.Lists;
+import org.hamcrest.Matchers;
+import org.hl7.fhir.r4.model.IdType;
+import org.hl7.fhir.r4.model.Patient;
+import org.junit.AfterClass;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+public class FhirResourceDaoR4UpdateTagSnapshotTest extends BaseJpaR4Test {
+
+ @Test
+ public void testUpdateWithDuplicateTagsWithHeader() {
+ when(mySrd.getHeaders(eq(JpaConstants.HEADER_META_SNAPSHOT_MODE))).thenReturn(Lists.newArrayList("TAG"));
+
+ Patient p = new Patient();
+ p.setId("A");
+ p.getMeta().addTag("urn:foo", "bar", "baz");
+ p.getMeta().addTag("urn:foo", "bar", "baz");
+ p.getMeta().addTag("urn:foo", "bar2", "baz");
+ p.getMeta().addTag("urn:foo", "bar2", "baz");
+ p.setActive(true);
+ myPatientDao.update(p, mySrd);
+
+ p = myPatientDao.read(new IdType("A"), mySrd);
+ assertEquals(2, p.getMeta().getTag().size());
+
+ p = new Patient();
+ p.setId("A");
+ p.getMeta().addTag("urn:foo", "bar", "baz");
+ p.getMeta().addTag("urn:foo", "bar", "baz");
+ p.setActive(true);
+ myPatientDao.update(p, mySrd);
+
+ p = myPatientDao.read(new IdType("A"), mySrd);
+ // It would be nice if this didn't trigger a version update but
+ // i guess it's not so bad that it does
+ assertEquals("2", p.getIdElement().getVersionIdPart());
+ assertEquals(true, p.getActive());
+ assertEquals(1, p.getMeta().getTag().size());
+ }
+
+ @Test
+ public void testUpdateWithFewerTagsNoHeader() {
+ Patient p = new Patient();
+ p.setId("A");
+ p.getMeta().addTag("urn:foo", "bar", "baz");
+ p.getMeta().addTag("urn:foo", "bar2", "baz");
+ p.setActive(true);
+ myPatientDao.update(p, mySrd);
+
+ p = new Patient();
+ p.setId("A");
+ p.getMeta().addTag("urn:foo", "bar", "baz");
+ p.setActive(true);
+ myPatientDao.update(p, mySrd);
+
+ p = myPatientDao.read(new IdType("A"), mySrd);
+ assertEquals("1", p.getIdElement().getVersionIdPart());
+ assertEquals(true, p.getActive());
+ assertEquals(2, p.getMeta().getTag().size());
+ assertEquals("urn:foo", p.getMeta().getTag().get(0).getSystem());
+ assertThat(p.getMeta().getTag().get(0).getCode(), Matchers.anyOf(Matchers.equalTo("bar"), Matchers.equalTo("bar2")));
+ }
+ @Test
+ public void testUpdateWithFewerTagsWithHeader() {
+ when(mySrd.getHeaders(eq(JpaConstants.HEADER_META_SNAPSHOT_MODE))).thenReturn(Lists.newArrayList("TAG"));
+
+ Patient p = new Patient();
+ p.setId("A");
+ p.getMeta().addTag("urn:foo", "bar", "baz");
+ p.getMeta().addTag("urn:foo", "bar2", "baz");
+ p.setActive(true);
+ myPatientDao.update(p, mySrd);
+
+ p = new Patient();
+ p.setId("A");
+ p.getMeta().addTag("urn:foo", "bar", "baz");
+ p.setActive(true);
+ myPatientDao.update(p, mySrd);
+
+ p = myPatientDao.read(new IdType("A"), mySrd);
+ // It would be nice if this didn't trigger a version update but
+ // i guess it's not so bad that it does
+ assertEquals("2", p.getIdElement().getVersionIdPart());
+ assertEquals(true, p.getActive());
+ assertEquals(1, p.getMeta().getTag().size());
+ assertEquals("urn:foo", p.getMeta().getTag().get(0).getSystem());
+ assertEquals("bar", p.getMeta().getTag().get(0).getCode());
+ assertEquals("baz", p.getMeta().getTag().get(0).getDisplay());
+ }
+
+ @Test
+ public void testUpdateWithNoTagsNoHeader() {
+ Patient p = new Patient();
+ p.setId("A");
+ p.getMeta().addTag("urn:foo", "bar", "baz");
+ p.setActive(true);
+ myPatientDao.update(p, mySrd);
+
+ p = new Patient();
+ p.setId("A");
+ p.setActive(true);
+ myPatientDao.update(p, mySrd);
+
+ p = myPatientDao.read(new IdType("A"), mySrd);
+ assertEquals("1", p.getIdElement().getVersionIdPart());
+ assertEquals(true, p.getActive());
+ assertEquals(1, p.getMeta().getTag().size());
+ assertEquals("urn:foo", p.getMeta().getTag().get(0).getSystem());
+ assertEquals("bar", p.getMeta().getTag().get(0).getCode());
+ assertEquals("baz", p.getMeta().getTag().get(0).getDisplay());
+ }
+
+ @Test
+ public void testUpdateWithNoTagsWithHeader() {
+ when(mySrd.getHeaders(eq(JpaConstants.HEADER_META_SNAPSHOT_MODE))).thenReturn(Lists.newArrayList("TAG"));
+
+ Patient p = new Patient();
+ p.setId("A");
+ p.getMeta().addTag("urn:foo", "bar", "baz");
+ p.setActive(true);
+ myPatientDao.update(p, mySrd);
+
+ p = new Patient();
+ p.setId("A");
+ p.setActive(true);
+ myPatientDao.update(p, mySrd);
+
+ p = myPatientDao.read(new IdType("A"), mySrd);
+ assertEquals(true, p.getActive());
+ assertEquals(0, p.getMeta().getTag().size());
+ // It would be nice if this didn't trigger a version update but
+ // i guess it's not so bad that it does
+ assertEquals("2", p.getIdElement().getVersionIdPart());
+ }
+
+ @AfterClass
+ public static void afterClassClearContext() {
+ TestUtil.clearAllStaticFieldsForUnitTest();
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java
index 4391e833786..33e9a4c5d71 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java
@@ -37,6 +37,7 @@ import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import static org.hamcrest.Matchers.*;
@@ -169,6 +170,24 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest {
return null;
}
+ @Test
+ public void testResourceCounts() {
+ Patient p = new Patient();
+ p.setActive(true);
+ myPatientDao.create(p);
+
+ Observation o = new Observation();
+ o.setStatus(ObservationStatus.AMENDED);
+ myObservationDao.create(o);
+
+ Map counts = mySystemDao.getResourceCounts();
+ assertEquals(new Long(1L), counts.get("Patient"));
+ assertEquals(new Long(1L), counts.get("Observation"));
+ assertEquals(null, counts.get("Organization"));
+
+ }
+
+
@Test
public void testBatchCreateWithBadRead() {
Bundle request = new Bundle();
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java
index 12e8f7f0967..81e322d42db 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java
@@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.dao.r4.SearchParamRegistryR4;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
+import ca.uhn.fhir.jpa.util.SingleItemLoadingCache;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR4;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.parser.StrictErrorHandler;
@@ -40,6 +41,7 @@ import org.springframework.web.servlet.DispatcherServlet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@@ -61,6 +63,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
private TerminologyUploaderProviderR4 myTerminologyUploaderProvider;
private Object ourGraphQLProvider;
private boolean ourRestHookSubscriptionInterceptorRequested;
+ protected SingleItemLoadingCache> ourResourceCountsCache;
public BaseResourceProviderR4Test() {
super();
@@ -99,6 +102,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
ourRestServer.setServerConformanceProvider(confProvider);
ourPagingProvider = myAppCtx.getBean(DatabaseBackedPagingProvider.class);
+ ourResourceCountsCache = (SingleItemLoadingCache>) myAppCtx.getBean("myResourceCountsCache");
Server server = new Server(ourPort);
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ServerR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ServerR4Test.java
index 65402320ee2..ca9766e73f3 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ServerR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ServerR4Test.java
@@ -1,30 +1,31 @@
package ca.uhn.fhir.jpa.provider.r4;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.HashSet;
-import java.util.Set;
-
+import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
+import ca.uhn.fhir.util.ExtensionConstants;
+import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.hl7.fhir.r4.model.CapabilityStatement;
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent;
+import org.hl7.fhir.r4.model.Extension;
+import org.hl7.fhir.r4.model.Patient;
import org.junit.AfterClass;
import org.junit.Test;
-import ca.uhn.fhir.util.TestUtil;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.junit.Assert.*;
public class ServerR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerR4Test.class);
-
-
+
/**
* See #519
*/
@@ -35,12 +36,12 @@ public class ServerR4Test extends BaseResourceProviderR4Test {
try {
ourLog.info(resp.toString());
assertEquals(200, resp.getStatusLine().getStatusCode());
-
+
String respString = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(respString);
-
+
CapabilityStatement cs = myFhirCtx.newXmlParser().parseResource(CapabilityStatement.class, respString);
-
+
for (CapabilityStatementRestResourceComponent nextResource : cs.getRest().get(0).getResource()) {
ourLog.info("Testing resource: " + nextResource.getType());
Set sps = new HashSet();
@@ -49,17 +50,70 @@ public class ServerR4Test extends BaseResourceProviderR4Test {
fail("Duplicate search parameter " + nextSp.getName() + " for resource " + nextResource.getType());
}
}
-
+
if (!sps.contains("_id")) {
fail("No search parameter _id for resource " + nextResource.getType());
}
- }
+ }
} finally {
- IOUtils.closeQuietly(resp.getEntity().getContent());
+ IOUtils.closeQuietly(resp.getEntity().getContent());
}
}
+ @Test
+ public void testMetadataIncludesResourceCounts() {
+ Patient p = new Patient();
+ p.setActive(true);
+ myClient.create().resource(p).execute();
+
+ /*
+ * Initial fetch after a clear should return
+ * no results
+ */
+ ourResourceCountsCache.clear();
+
+ CapabilityStatement capabilityStatement = myClient
+ .capabilities()
+ .ofType(CapabilityStatement.class)
+ .execute();
+
+ Extension patientCountExt = capabilityStatement
+ .getRest()
+ .get(0)
+ .getResource()
+ .stream()
+ .filter(t -> t.getType().equals("Patient"))
+ .findFirst()
+ .orElseThrow(() -> new InternalErrorException("No patient"))
+ .getExtensionByUrl(ExtensionConstants.CONF_RESOURCE_COUNT);
+ assertNull(patientCountExt);
+
+ /*
+ * Now run a background pass (the update
+ * method is called by the scheduler normally)
+ */
+ ourResourceCountsCache.update();
+
+ capabilityStatement = myClient
+ .capabilities()
+ .ofType(CapabilityStatement.class)
+ .execute();
+
+ patientCountExt = capabilityStatement
+ .getRest()
+ .get(0)
+ .getResource()
+ .stream()
+ .filter(t -> t.getType().equals("Patient"))
+ .findFirst()
+ .orElseThrow(() -> new InternalErrorException("No patient"))
+ .getExtensionByUrl(ExtensionConstants.CONF_RESOURCE_COUNT);
+ assertEquals("1", patientCountExt.getValueAsPrimitive().getValueAsString());
+
+ }
+
+
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/SingleItemLoadingCacheTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/SingleItemLoadingCacheTest.java
new file mode 100644
index 00000000000..09f8fcbc7c3
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/SingleItemLoadingCacheTest.java
@@ -0,0 +1,65 @@
+package ca.uhn.fhir.jpa.util;
+
+import org.hl7.fhir.dstu3.model.CapabilityStatement;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class SingleItemLoadingCacheTest {
+
+ @Mock
+ private Callable myFetcher;
+
+ @After
+ public void after() {
+ SingleItemLoadingCache.setNowForUnitTest(null);
+ }
+
+ @Before
+ public void before() throws Exception {
+ AtomicInteger id = new AtomicInteger();
+ when(myFetcher.call()).thenAnswer(t->{
+ CapabilityStatement retVal = new CapabilityStatement();
+ retVal.setId("" + id.incrementAndGet());
+ return retVal;
+ });
+ }
+
+ @Test
+ public void testCache() {
+ long start = System.currentTimeMillis();
+ SingleItemLoadingCache.setNowForUnitTest(start);
+
+ // Cache is initialized on startup
+ SingleItemLoadingCache cache = new SingleItemLoadingCache<>(myFetcher);
+ cache.setCacheMillis(500);
+ assertEquals(null, cache.get());
+
+ // Not time to update yet
+ cache.update();
+ assertEquals("1", cache.get().getId());
+
+ // Wait a bit, still not time to update
+ SingleItemLoadingCache.setNowForUnitTest(start + 400);
+ cache.update();
+ assertEquals("1", cache.get().getId());
+
+ // Wait a bit more and the cache is expired
+ SingleItemLoadingCache.setNowForUnitTest(start + 800);
+ cache.update();
+ assertEquals("2", cache.get().getId());
+
+ }
+
+}
diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/resource/BaseResource.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/resource/BaseResource.java
index 5dfb3219c1d..588a8311f8b 100644
--- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/resource/BaseResource.java
+++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/resource/BaseResource.java
@@ -257,7 +257,11 @@ public abstract class BaseResource extends BaseElement implements IResource {
@Override
public IBaseMetaType setLastUpdated(Date theHeaderDateValue) {
- ResourceMetadataKeyEnum.UPDATED.put(BaseResource.this, new InstantDt(theHeaderDateValue));
+ if (theHeaderDateValue == null) {
+ getResourceMetadata().remove(ResourceMetadataKeyEnum.UPDATED);
+ } else {
+ ResourceMetadataKeyEnum.UPDATED.put(BaseResource.this, new InstantDt(theHeaderDateValue));
+ }
return this;
}
diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java
index be8e1ab57a0..43ae2d54979 100644
--- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java
+++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java
@@ -669,14 +669,15 @@ public final class IdType extends UriType implements IPrimitiveType, IId
* Creates a new instance of this ID which is identical, but refers to the
* specific version of this resource ID noted by theVersion.
*
- * @param theVersion
- * The actual version string, e.g. "1"
+ * @param theVersion The actual version string, e.g. "1". If theVersion is blank or null, returns the same as {@link #toVersionless()}}
* @return A new instance of IdType which is identical, but refers to the
* specific version of this resource ID noted by theVersion.
*/
@Override
public IdType withVersion(String theVersion) {
- Validate.notBlank(theVersion, "Version may not be null or empty");
+ if (isBlank(theVersion)) {
+ return toVersionless();
+ }
if (isLocal() || isUrn()) {
return new IdType(getValueAsString());
diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/model/IdTypeDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/model/IdTypeDstu3Test.java
index 330e0dba256..30e2ae0b601 100644
--- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/model/IdTypeDstu3Test.java
+++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/model/IdTypeDstu3Test.java
@@ -1,12 +1,7 @@
package ca.uhn.fhir.model;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import java.math.BigDecimal;
-
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Reference;
@@ -14,82 +9,19 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
-import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.util.TestUtil;
+import java.math.BigDecimal;
+
+import static org.junit.Assert.*;
public class IdTypeDstu3Test {
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdTypeDstu3Test.class);
private static FhirContext ourCtx;
- private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdTypeDstu3Test.class);
-
- @AfterClass
- public static void afterClassClearContext() {
- TestUtil.clearAllStaticFieldsForUnitTest();
- }
-
- @Test
- public void testUuid() {
- IdType id = new IdType("urn:uuid:1234-5678");
- assertEquals("urn:uuid:1234-5678", id.getValueAsString());
- assertEquals("urn:uuid:1234-5678", id.getIdPart());
- assertEquals("urn:uuid:1234-5678", id.toUnqualified().getValueAsString());
- assertEquals("urn:uuid:1234-5678", id.toUnqualifiedVersionless().getValueAsString());
- assertEquals(null, id.getVersionIdPart());
- assertEquals(null, id.getResourceType());
- assertEquals(null, id.getBaseUrl());
-
- assertEquals("urn:uuid:1234-5678", id.withResourceType("Patient").getValue());
- assertEquals("urn:uuid:1234-5678", id.withServerBase("http://foo", "Patient").getValue());
- assertEquals("urn:uuid:1234-5678", id.withVersion("2").getValue());
- }
-
- @Test
- public void testOid() {
- IdType id = new IdType("urn:oid:1.2.3.4");
- assertEquals("urn:oid:1.2.3.4", id.getValueAsString());
- assertEquals("urn:oid:1.2.3.4", id.getIdPart());
- assertEquals("urn:oid:1.2.3.4", id.toUnqualified().getValueAsString());
- assertEquals("urn:oid:1.2.3.4", id.toUnqualifiedVersionless().getValueAsString());
- assertEquals(null, id.getVersionIdPart());
- assertEquals(null, id.getResourceType());
- assertEquals(null, id.getBaseUrl());
-
- assertEquals("urn:oid:1.2.3.4", id.withResourceType("Patient").getValue());
- assertEquals("urn:oid:1.2.3.4", id.withServerBase("http://foo", "Patient").getValue());
- assertEquals("urn:oid:1.2.3.4", id.withVersion("2").getValue());
- }
-
- @Test
- public void testLocal() {
- IdType id = new IdType("#foo");
- assertEquals("#foo", id.getValueAsString());
- assertEquals("#foo", id.getIdPart());
- assertEquals("#foo", id.toUnqualified().getValueAsString());
- assertEquals("#foo", id.toUnqualifiedVersionless().getValueAsString());
- assertEquals(null, id.getVersionIdPart());
- assertEquals(null, id.getResourceType());
- assertEquals(null, id.getBaseUrl());
-
- assertEquals("#foo", id.withResourceType("Patient").getValue());
- assertEquals("#foo", id.withServerBase("http://foo", "Patient").getValue());
- assertEquals("#foo", id.withVersion("2").getValue());
- }
-
- @Test
- public void testNormal() {
- IdType id = new IdType("foo");
- assertEquals("foo", id.getValueAsString());
- assertEquals("foo", id.getIdPart());
- assertEquals("foo", id.toUnqualified().getValueAsString());
- assertEquals("foo", id.toUnqualifiedVersionless().getValueAsString());
- assertEquals(null, id.getVersionIdPart());
- assertEquals(null, id.getResourceType());
- assertEquals(null, id.getBaseUrl());
-
- assertEquals("Patient/foo", id.withResourceType("Patient").getValue());
- assertEquals("http://foo/Patient/foo", id.withServerBase("http://foo", "Patient").getValue());
- assertEquals("foo/_history/2", id.withVersion("2").getValue());
+ private Patient parseAndEncode(Patient patient) {
+ String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
+ ourLog.info("\n" + encoded);
+ return ourCtx.newXmlParser().parseResource(Patient.class, encoded);
}
@Test
@@ -124,14 +56,52 @@ public class IdTypeDstu3Test {
assertEquals("http://my.org/a/b/c/foo/_history/2", id.withVersion("2").getValue());
}
+ @Test
+ public void testBigDecimalIds() {
+
+ IdType id = new IdType(new BigDecimal("123"));
+ assertEquals(id.getIdPartAsBigDecimal(), new BigDecimal("123"));
+
+ }
+
+ /**
+ * See #67
+ */
+ @Test
+ public void testComplicatedLocal() {
+ IdType id = new IdType("#Patient/cid:Patient-72/_history/1");
+ assertTrue(id.isLocal());
+ assertEquals(null, id.getBaseUrl());
+ assertNull(id.getResourceType());
+ assertNull(id.getVersionIdPart());
+ assertEquals("#Patient/cid:Patient-72/_history/1", id.getIdPart());
+
+ IdType id2 = new IdType("#Patient/cid:Patient-72/_history/1");
+ assertEquals(id, id2);
+
+ id2 = id2.toUnqualified();
+ assertTrue(id2.isLocal());
+ assertNull(id2.getBaseUrl());
+ assertNull(id2.getResourceType());
+ assertNull(id2.getVersionIdPart());
+ assertEquals("#Patient/cid:Patient-72/_history/1", id2.getIdPart());
+
+ }
+
+ @Test
+ public void testConstructorsWithNullArguments() {
+ IdType id = new IdType(null, null, null);
+ assertEquals(null, id.getValue());
+ }
+
@Test
public void testDetectLocal() {
IdType id;
-
+
id = new IdType("#123");
assertEquals("#123", id.getValue());
assertTrue(id.isLocal());
-
+
id = new IdType("#Medication/499059CE-CDD4-48BC-9014-528A35D15CED/_history/1");
assertEquals("#Medication/499059CE-CDD4-48BC-9014-528A35D15CED/_history/1", id.getValue());
assertTrue(id.isLocal());
@@ -140,12 +110,6 @@ public class IdTypeDstu3Test {
assertEquals("http://example.com/Patient/33#123", id.getValue());
assertFalse(id.isLocal());
}
-
- @Test
- public void testConstructorsWithNullArguments() {
- IdType id = new IdType(null, null, null);
- assertEquals(null, id.getValue());
- }
@Test
public void testDetectLocalBase() {
@@ -161,32 +125,7 @@ public class IdTypeDstu3Test {
assertEquals(null, new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl());
assertEquals("#180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart());
}
-
- /**
- * See #67
- */
- @Test
- public void testComplicatedLocal() {
- IdType id = new IdType("#Patient/cid:Patient-72/_history/1");
- assertTrue(id.isLocal());
- assertEquals(null, id.getBaseUrl());
- assertNull(id.getResourceType());
- assertNull(id.getVersionIdPart());
- assertEquals("#Patient/cid:Patient-72/_history/1", id.getIdPart());
-
- IdType id2 = new IdType("#Patient/cid:Patient-72/_history/1");
- assertEquals(id, id2);
-
- id2 = id2.toUnqualified();
- assertTrue(id2.isLocal());
- assertNull(id2.getBaseUrl());
- assertNull(id2.getResourceType());
- assertNull(id2.getVersionIdPart());
- assertEquals("#Patient/cid:Patient-72/_history/1", id2.getIdPart());
-
- }
-
@Test
public void testDetermineBase() {
@@ -197,12 +136,60 @@ public class IdTypeDstu3Test {
rr = new IdType("http://foo/fhir/Organization/123/_history/123");
assertEquals("http://foo/fhir", rr.getBaseUrl());
-
+
rr = new IdType("Organization/123/_history/123");
assertEquals(null, rr.getBaseUrl());
}
+ @Test
+ public void testLocal() {
+ IdType id = new IdType("#foo");
+ assertEquals("#foo", id.getValueAsString());
+ assertEquals("#foo", id.getIdPart());
+ assertEquals("#foo", id.toUnqualified().getValueAsString());
+ assertEquals("#foo", id.toUnqualifiedVersionless().getValueAsString());
+ assertEquals(null, id.getVersionIdPart());
+ assertEquals(null, id.getResourceType());
+ assertEquals(null, id.getBaseUrl());
+
+ assertEquals("#foo", id.withResourceType("Patient").getValue());
+ assertEquals("#foo", id.withServerBase("http://foo", "Patient").getValue());
+ assertEquals("#foo", id.withVersion("2").getValue());
+ }
+
+ @Test
+ public void testNormal() {
+ IdType id = new IdType("foo");
+ assertEquals("foo", id.getValueAsString());
+ assertEquals("foo", id.getIdPart());
+ assertEquals("foo", id.toUnqualified().getValueAsString());
+ assertEquals("foo", id.toUnqualifiedVersionless().getValueAsString());
+ assertEquals(null, id.getVersionIdPart());
+ assertEquals(null, id.getResourceType());
+ assertEquals(null, id.getBaseUrl());
+
+ assertEquals("Patient/foo", id.withResourceType("Patient").getValue());
+ assertEquals("http://foo/Patient/foo", id.withServerBase("http://foo", "Patient").getValue());
+ assertEquals("foo/_history/2", id.withVersion("2").getValue());
+ }
+
+ @Test
+ public void testOid() {
+ IdType id = new IdType("urn:oid:1.2.3.4");
+ assertEquals("urn:oid:1.2.3.4", id.getValueAsString());
+ assertEquals("urn:oid:1.2.3.4", id.getIdPart());
+ assertEquals("urn:oid:1.2.3.4", id.toUnqualified().getValueAsString());
+ assertEquals("urn:oid:1.2.3.4", id.toUnqualifiedVersionless().getValueAsString());
+ assertEquals(null, id.getVersionIdPart());
+ assertEquals(null, id.getResourceType());
+ assertEquals(null, id.getBaseUrl());
+
+ assertEquals("urn:oid:1.2.3.4", id.withResourceType("Patient").getValue());
+ assertEquals("urn:oid:1.2.3.4", id.withServerBase("http://foo", "Patient").getValue());
+ assertEquals("urn:oid:1.2.3.4", id.withVersion("2").getValue());
+ }
+
@Test
public void testParseValueAbsolute() {
Patient patient = new Patient();
@@ -218,14 +205,6 @@ public class IdTypeDstu3Test {
}
- @Test
- public void testBigDecimalIds() {
-
- IdType id = new IdType(new BigDecimal("123"));
- assertEquals(id.getIdPartAsBigDecimal(), new BigDecimal("123"));
-
- }
-
@Test
public void testParseValueAbsoluteWithVersion() {
Patient patient = new Patient();
@@ -241,30 +220,6 @@ public class IdTypeDstu3Test {
}
-
- @Test
- public void testViewMethods() {
- IdType i = new IdType("http://foo/fhir/Organization/123/_history/999");
- assertEquals("Organization/123/_history/999", i.toUnqualified().getValue());
- assertEquals("http://foo/fhir/Organization/123", i.toVersionless().getValue());
- assertEquals("Organization/123", i.toUnqualifiedVersionless().getValue());
- }
-
- @Test
- public void testParseValueWithVersion() {
- Patient patient = new Patient();
- IdType rr = new IdType();
- rr.setValue("/123/_history/999");
- patient.setManagingOrganization(new Reference(rr));
-
- Patient actual = parseAndEncode(patient);
- Reference ref = actual.getManagingOrganization();
- assertEquals(null, ref.getReferenceElement().getResourceType());
- assertEquals("123", ref.getReferenceElement().getIdPart());
- assertEquals(null, ref.getReferenceElement().getVersionIdPart());
-
- }
-
@Test
public void testParseValueMissingType1() {
Patient patient = new Patient();
@@ -321,10 +276,53 @@ public class IdTypeDstu3Test {
}
- private Patient parseAndEncode(Patient patient) {
- String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
- ourLog.info("\n" + encoded);
- return ourCtx.newXmlParser().parseResource(Patient.class, encoded);
+ @Test
+ public void testParseValueWithVersion() {
+ Patient patient = new Patient();
+ IdType rr = new IdType();
+ rr.setValue("/123/_history/999");
+ patient.setManagingOrganization(new Reference(rr));
+
+ Patient actual = parseAndEncode(patient);
+ Reference ref = actual.getManagingOrganization();
+ assertEquals(null, ref.getReferenceElement().getResourceType());
+ assertEquals("123", ref.getReferenceElement().getIdPart());
+ assertEquals(null, ref.getReferenceElement().getVersionIdPart());
+
+ }
+
+ @Test
+ public void testUuid() {
+ IdType id = new IdType("urn:uuid:1234-5678");
+ assertEquals("urn:uuid:1234-5678", id.getValueAsString());
+ assertEquals("urn:uuid:1234-5678", id.getIdPart());
+ assertEquals("urn:uuid:1234-5678", id.toUnqualified().getValueAsString());
+ assertEquals("urn:uuid:1234-5678", id.toUnqualifiedVersionless().getValueAsString());
+ assertEquals(null, id.getVersionIdPart());
+ assertEquals(null, id.getResourceType());
+ assertEquals(null, id.getBaseUrl());
+
+ assertEquals("urn:uuid:1234-5678", id.withResourceType("Patient").getValue());
+ assertEquals("urn:uuid:1234-5678", id.withServerBase("http://foo", "Patient").getValue());
+ assertEquals("urn:uuid:1234-5678", id.withVersion("2").getValue());
+ }
+
+ @Test
+ public void testViewMethods() {
+ IdType i = new IdType("http://foo/fhir/Organization/123/_history/999");
+ assertEquals("Organization/123/_history/999", i.toUnqualified().getValue());
+ assertEquals("http://foo/fhir/Organization/123", i.toVersionless().getValue());
+ assertEquals("Organization/123", i.toUnqualifiedVersionless().getValue());
+ }
+
+ @Test
+ public void testWithVersionNull() {
+ assertEquals("Patient/123", new IdType("Patient/123/_history/2").withVersion("").getValue());
+ }
+
+ @AfterClass
+ public static void afterClassClearContext() {
+ TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/IdType.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/IdType.java
index d7b66decb3f..292d6556e96 100644
--- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/IdType.java
+++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/IdType.java
@@ -709,13 +709,15 @@ public final class IdType extends UriType implements IPrimitiveType, IId
* Creates a new instance of this ID which is identical, but refers to the
* specific version of this resource ID noted by theVersion.
*
- * @param theVersion The actual version string, e.g. "1"
+ * @param theVersion The actual version string, e.g. "1". If theVersion is blank or null, returns the same as {@link #toVersionless()}}
* @return A new instance of IdType which is identical, but refers to the
* specific version of this resource ID noted by theVersion.
*/
@Override
public IdType withVersion(String theVersion) {
- Validate.notBlank(theVersion, "Version may not be null or empty");
+ if (isBlank(theVersion)) {
+ return toVersionless();
+ }
if (isLocal() || isUrn()) {
return new IdType(getValueAsString());
diff --git a/hapi-fhir-structures-r4/src/test/java/org/hl7/fhir/r4/model/IdTypeR4Test.java b/hapi-fhir-structures-r4/src/test/java/org/hl7/fhir/r4/model/IdTypeR4Test.java
index 73300adc607..85d493100b1 100644
--- a/hapi-fhir-structures-r4/src/test/java/org/hl7/fhir/r4/model/IdTypeR4Test.java
+++ b/hapi-fhir-structures-r4/src/test/java/org/hl7/fhir/r4/model/IdTypeR4Test.java
@@ -12,13 +12,13 @@ import static org.junit.Assert.*;
public class IdTypeR4Test {
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdTypeR4Test.class);
private static FhirContext ourCtx;
- private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdTypeR4Test.class);
-
- @AfterClass
- public static void afterClassClearContext() {
- TestUtil.clearAllStaticFieldsForUnitTest();
+ private Patient parseAndEncode(Patient patient) {
+ String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
+ ourLog.info("\n" + encoded);
+ return ourCtx.newXmlParser().parseResource(Patient.class, encoded);
}
@Test
@@ -53,79 +53,52 @@ public class IdTypeR4Test {
assertEquals("http://my.org/a/b/c/foo/_history/2", id.withVersion("2").getValue());
}
+ @Test
+ public void testBigDecimalIds() {
+
+ IdType id = new IdType(new BigDecimal("123"));
+ assertEquals(id.getIdPartAsBigDecimal(), new BigDecimal("123"));
- @Test
- public void testUuid() {
- IdType id = new IdType("urn:uuid:1234-5678");
- assertEquals("urn:uuid:1234-5678", id.getValueAsString());
- assertEquals("urn:uuid:1234-5678", id.getIdPart());
- assertEquals("urn:uuid:1234-5678", id.toUnqualified().getValueAsString());
- assertEquals("urn:uuid:1234-5678", id.toUnqualifiedVersionless().getValueAsString());
- assertEquals(null, id.getVersionIdPart());
- assertEquals(null, id.getResourceType());
- assertEquals(null, id.getBaseUrl());
-
- assertEquals("urn:uuid:1234-5678", id.withResourceType("Patient").getValue());
- assertEquals("urn:uuid:1234-5678", id.withServerBase("http://foo", "Patient").getValue());
- assertEquals("urn:uuid:1234-5678", id.withVersion("2").getValue());
}
-
+
+ /**
+ * See #67
+ */
@Test
- public void testOid() {
- IdType id = new IdType("urn:oid:1.2.3.4");
- assertEquals("urn:oid:1.2.3.4", id.getValueAsString());
- assertEquals("urn:oid:1.2.3.4", id.getIdPart());
- assertEquals("urn:oid:1.2.3.4", id.toUnqualified().getValueAsString());
- assertEquals("urn:oid:1.2.3.4", id.toUnqualifiedVersionless().getValueAsString());
- assertEquals(null, id.getVersionIdPart());
- assertEquals(null, id.getResourceType());
+ public void testComplicatedLocal() {
+ IdType id = new IdType("#Patient/cid:Patient-72/_history/1");
+ assertTrue(id.isLocal());
assertEquals(null, id.getBaseUrl());
-
- assertEquals("urn:oid:1.2.3.4", id.withResourceType("Patient").getValue());
- assertEquals("urn:oid:1.2.3.4", id.withServerBase("http://foo", "Patient").getValue());
- assertEquals("urn:oid:1.2.3.4", id.withVersion("2").getValue());
+ assertNull(id.getResourceType());
+ assertNull(id.getVersionIdPart());
+ assertEquals("#Patient/cid:Patient-72/_history/1", id.getIdPart());
+
+ IdType id2 = new IdType("#Patient/cid:Patient-72/_history/1");
+ assertEquals(id, id2);
+
+ id2 = id2.toUnqualified();
+ assertTrue(id2.isLocal());
+ assertNull(id2.getBaseUrl());
+ assertNull(id2.getResourceType());
+ assertNull(id2.getVersionIdPart());
+ assertEquals("#Patient/cid:Patient-72/_history/1", id2.getIdPart());
+
}
@Test
- public void testLocal() {
- IdType id = new IdType("#foo");
- assertEquals("#foo", id.getValueAsString());
- assertEquals("#foo", id.getIdPart());
- assertEquals("#foo", id.toUnqualified().getValueAsString());
- assertEquals("#foo", id.toUnqualifiedVersionless().getValueAsString());
- assertEquals(null, id.getVersionIdPart());
- assertEquals(null, id.getResourceType());
- assertEquals(null, id.getBaseUrl());
-
- assertEquals("#foo", id.withResourceType("Patient").getValue());
- assertEquals("#foo", id.withServerBase("http://foo", "Patient").getValue());
- assertEquals("#foo", id.withVersion("2").getValue());
- }
-
- @Test
- public void testNormal() {
- IdType id = new IdType("foo");
- assertEquals("foo", id.getValueAsString());
- assertEquals("foo", id.getIdPart());
- assertEquals("foo", id.toUnqualified().getValueAsString());
- assertEquals("foo", id.toUnqualifiedVersionless().getValueAsString());
- assertEquals(null, id.getVersionIdPart());
- assertEquals(null, id.getResourceType());
- assertEquals(null, id.getBaseUrl());
-
- assertEquals("Patient/foo", id.withResourceType("Patient").getValue());
- assertEquals("http://foo/Patient/foo", id.withServerBase("http://foo", "Patient").getValue());
- assertEquals("foo/_history/2", id.withVersion("2").getValue());
+ public void testConstructorsWithNullArguments() {
+ IdType id = new IdType(null, null, null);
+ assertEquals(null, id.getValue());
}
@Test
public void testDetectLocal() {
IdType id;
-
+
id = new IdType("#123");
assertEquals("#123", id.getValue());
assertTrue(id.isLocal());
-
+
id = new IdType("#Medication/499059CE-CDD4-48BC-9014-528A35D15CED/_history/1");
assertEquals("#Medication/499059CE-CDD4-48BC-9014-528A35D15CED/_history/1", id.getValue());
assertTrue(id.isLocal());
@@ -134,12 +107,6 @@ public class IdTypeR4Test {
assertEquals("http://example.com/Patient/33#123", id.getValue());
assertFalse(id.isLocal());
}
-
- @Test
- public void testConstructorsWithNullArguments() {
- IdType id = new IdType(null, null, null);
- assertEquals(null, id.getValue());
- }
@Test
public void testDetectLocalBase() {
@@ -155,32 +122,7 @@ public class IdTypeR4Test {
assertEquals(null, new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl());
assertEquals("#180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart());
}
-
- /**
- * See #67
- */
- @Test
- public void testComplicatedLocal() {
- IdType id = new IdType("#Patient/cid:Patient-72/_history/1");
- assertTrue(id.isLocal());
- assertEquals(null, id.getBaseUrl());
- assertNull(id.getResourceType());
- assertNull(id.getVersionIdPart());
- assertEquals("#Patient/cid:Patient-72/_history/1", id.getIdPart());
-
- IdType id2 = new IdType("#Patient/cid:Patient-72/_history/1");
- assertEquals(id, id2);
-
- id2 = id2.toUnqualified();
- assertTrue(id2.isLocal());
- assertNull(id2.getBaseUrl());
- assertNull(id2.getResourceType());
- assertNull(id2.getVersionIdPart());
- assertEquals("#Patient/cid:Patient-72/_history/1", id2.getIdPart());
-
- }
-
@Test
public void testDetermineBase() {
@@ -191,12 +133,67 @@ public class IdTypeR4Test {
rr = new IdType("http://foo/fhir/Organization/123/_history/123");
assertEquals("http://foo/fhir", rr.getBaseUrl());
-
+
rr = new IdType("Organization/123/_history/123");
assertEquals(null, rr.getBaseUrl());
}
+ @Test
+ public void testEncodeParts() {
+ IdType id = new IdType("http://foo", "Patient", "123", "456");
+ assertEquals("http://foo/Patient/123/_history/456", id.getValue());
+ assertEquals("http://foo/Patient/123/_history/9", id.withVersion("9").getValue());
+ }
+
+ @Test
+ public void testLocal() {
+ IdType id = new IdType("#foo");
+ assertEquals("#foo", id.getValueAsString());
+ assertEquals("#foo", id.getIdPart());
+ assertEquals("#foo", id.toUnqualified().getValueAsString());
+ assertEquals("#foo", id.toUnqualifiedVersionless().getValueAsString());
+ assertEquals(null, id.getVersionIdPart());
+ assertEquals(null, id.getResourceType());
+ assertEquals(null, id.getBaseUrl());
+
+ assertEquals("#foo", id.withResourceType("Patient").getValue());
+ assertEquals("#foo", id.withServerBase("http://foo", "Patient").getValue());
+ assertEquals("#foo", id.withVersion("2").getValue());
+ }
+
+ @Test
+ public void testNormal() {
+ IdType id = new IdType("foo");
+ assertEquals("foo", id.getValueAsString());
+ assertEquals("foo", id.getIdPart());
+ assertEquals("foo", id.toUnqualified().getValueAsString());
+ assertEquals("foo", id.toUnqualifiedVersionless().getValueAsString());
+ assertEquals(null, id.getVersionIdPart());
+ assertEquals(null, id.getResourceType());
+ assertEquals(null, id.getBaseUrl());
+
+ assertEquals("Patient/foo", id.withResourceType("Patient").getValue());
+ assertEquals("http://foo/Patient/foo", id.withServerBase("http://foo", "Patient").getValue());
+ assertEquals("foo/_history/2", id.withVersion("2").getValue());
+ }
+
+ @Test
+ public void testOid() {
+ IdType id = new IdType("urn:oid:1.2.3.4");
+ assertEquals("urn:oid:1.2.3.4", id.getValueAsString());
+ assertEquals("urn:oid:1.2.3.4", id.getIdPart());
+ assertEquals("urn:oid:1.2.3.4", id.toUnqualified().getValueAsString());
+ assertEquals("urn:oid:1.2.3.4", id.toUnqualifiedVersionless().getValueAsString());
+ assertEquals(null, id.getVersionIdPart());
+ assertEquals(null, id.getResourceType());
+ assertEquals(null, id.getBaseUrl());
+
+ assertEquals("urn:oid:1.2.3.4", id.withResourceType("Patient").getValue());
+ assertEquals("urn:oid:1.2.3.4", id.withServerBase("http://foo", "Patient").getValue());
+ assertEquals("urn:oid:1.2.3.4", id.withVersion("2").getValue());
+ }
+
@Test
public void testParseValueAbsolute() {
Patient patient = new Patient();
@@ -212,14 +209,6 @@ public class IdTypeR4Test {
}
- @Test
- public void testBigDecimalIds() {
-
- IdType id = new IdType(new BigDecimal("123"));
- assertEquals(id.getIdPartAsBigDecimal(), new BigDecimal("123"));
-
- }
-
@Test
public void testParseValueAbsoluteWithVersion() {
Patient patient = new Patient();
@@ -235,30 +224,6 @@ public class IdTypeR4Test {
}
-
- @Test
- public void testViewMethods() {
- IdType i = new IdType("http://foo/fhir/Organization/123/_history/999");
- assertEquals("Organization/123/_history/999", i.toUnqualified().getValue());
- assertEquals("http://foo/fhir/Organization/123", i.toVersionless().getValue());
- assertEquals("Organization/123", i.toUnqualifiedVersionless().getValue());
- }
-
- @Test
- public void testParseValueWithVersion() {
- Patient patient = new Patient();
- IdType rr = new IdType();
- rr.setValue("/123/_history/999");
- patient.setManagingOrganization(new Reference(rr));
-
- Patient actual = parseAndEncode(patient);
- Reference ref = actual.getManagingOrganization();
- assertEquals(null, ref.getReferenceElement().getResourceType());
- assertEquals("123", ref.getReferenceElement().getIdPart());
- assertEquals(null, ref.getReferenceElement().getVersionIdPart());
-
- }
-
@Test
public void testParseValueMissingType1() {
Patient patient = new Patient();
@@ -301,13 +266,6 @@ public class IdTypeR4Test {
}
- @Test
- public void testEncodeParts() {
- IdType id = new IdType("http://foo", "Patient", "123", "456");
- assertEquals("http://foo/Patient/123/_history/456", id.getValue());
- assertEquals("http://foo/Patient/123/_history/9", id.withVersion("9").getValue());
- }
-
@Test
public void testParseValueRelative2() {
Patient patient = new Patient();
@@ -322,10 +280,53 @@ public class IdTypeR4Test {
}
- private Patient parseAndEncode(Patient patient) {
- String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
- ourLog.info("\n" + encoded);
- return ourCtx.newXmlParser().parseResource(Patient.class, encoded);
+ @Test
+ public void testParseValueWithVersion() {
+ Patient patient = new Patient();
+ IdType rr = new IdType();
+ rr.setValue("/123/_history/999");
+ patient.setManagingOrganization(new Reference(rr));
+
+ Patient actual = parseAndEncode(patient);
+ Reference ref = actual.getManagingOrganization();
+ assertEquals(null, ref.getReferenceElement().getResourceType());
+ assertEquals("123", ref.getReferenceElement().getIdPart());
+ assertEquals(null, ref.getReferenceElement().getVersionIdPart());
+
+ }
+
+ @Test
+ public void testUuid() {
+ IdType id = new IdType("urn:uuid:1234-5678");
+ assertEquals("urn:uuid:1234-5678", id.getValueAsString());
+ assertEquals("urn:uuid:1234-5678", id.getIdPart());
+ assertEquals("urn:uuid:1234-5678", id.toUnqualified().getValueAsString());
+ assertEquals("urn:uuid:1234-5678", id.toUnqualifiedVersionless().getValueAsString());
+ assertEquals(null, id.getVersionIdPart());
+ assertEquals(null, id.getResourceType());
+ assertEquals(null, id.getBaseUrl());
+
+ assertEquals("urn:uuid:1234-5678", id.withResourceType("Patient").getValue());
+ assertEquals("urn:uuid:1234-5678", id.withServerBase("http://foo", "Patient").getValue());
+ assertEquals("urn:uuid:1234-5678", id.withVersion("2").getValue());
+ }
+
+ @Test
+ public void testViewMethods() {
+ IdType i = new IdType("http://foo/fhir/Organization/123/_history/999");
+ assertEquals("Organization/123/_history/999", i.toUnqualified().getValue());
+ assertEquals("http://foo/fhir/Organization/123", i.toVersionless().getValue());
+ assertEquals("Organization/123", i.toUnqualifiedVersionless().getValue());
+ }
+
+ @Test
+ public void testWithVersionNull() {
+ assertEquals("Patient/123", new IdType("Patient/123/_history/2").withVersion("").getValue());
+ }
+
+ @AfterClass
+ public static void afterClassClearContext() {
+ TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index c5acecfd0bc..a42f71ccc2b 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -78,6 +78,37 @@
applies to the operation by name whether it is at the
server, type, or instance level.
+
+ Calling IdType#withVersion(String)
]]>
+ with a null/blank parameter will now return a copy of the
+ ID with the version removed. Previously this call would
+ deliberately cause an IllegalArgumentException.
+
+