diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IInterceptorService.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IInterceptorService.java index 04a6d513bbc..c4655ce8acb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IInterceptorService.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IInterceptorService.java @@ -23,6 +23,7 @@ package ca.uhn.fhir.interceptor.api; import javax.annotation.Nullable; import java.util.Collection; import java.util.List; +import java.util.function.Function; public interface IInterceptorService extends IInterceptorBroadcaster { @@ -90,4 +91,8 @@ public interface IInterceptorService extends IInterceptorBroadcaster { void registerInterceptors(@Nullable Collection> theInterceptors); + /** + * Unregisters all interceptors that are indicated by the given callback function returning true + */ + void unregisterInterceptorsIf(Function theShouldUnregisterFunction); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java index 634ccade76e..e569f7ca872 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java @@ -1321,6 +1321,43 @@ public enum Pointcut { "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails" ), + /** + * Storage Hook: + * Invoked before an $expunge operation on all data (expungeEverything) is called. + * + * Hooks will be passed a reference to a counter containing the current number of records that have been deleted. + * If the hook deletes any records, the hook is expected to increment this counter by the number of records deleted. + * + * Hooks may accept the following parameters: + * + * org.hl7.fhir.instance.model.api.IBaseResource - The resource that will be created and needs a tenant ID assigned. + * + * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the + * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been + * pulled out of the servlet request. Note that the bean + * properties are not all guaranteed to be populated, depending on how early during processing the + * exception occurred. + * + * + * ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the + * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been + * pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will + * only be populated when operating in a RestfulServer implementation. It is provided as a convenience. + * + * + * + * Hooks should return an instance of ca.uhn.fhir.jpa.model.entity.TenantId or null. + * + */ + STORAGE_TENANT_IDENTIFY_CREATE ( + // Return type + "ca.uhn.fhir.jpa.model.entity.TenantId", + // Params + "org.hl7.fhir.instance.model.api.IBaseResource", + "ca.uhn.fhir.rest.api.server.RequestDetails", + "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails" + ), + /** * Performance Tracing Hook: * This hook is invoked when any informational messages generated by the diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/InterceptorService.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/InterceptorService.java index 0c01735b266..25ff3fed920 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/InterceptorService.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/InterceptorService.java @@ -41,6 +41,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; import java.util.stream.Collectors; public class InterceptorService implements IInterceptorService, IInterceptorBroadcaster { @@ -145,6 +146,22 @@ public class InterceptorService implements IInterceptorService, IInterceptorBroa } } + @Override + public void unregisterInterceptorsIf(Function theShouldUnregisterFunction) { + unregisterInterceptorsIf(theShouldUnregisterFunction, myGlobalInvokers); + unregisterInterceptorsIf(theShouldUnregisterFunction, myAnonymousInvokers); + } + + private void unregisterInterceptorsIf(Function theShouldUnregisterFunction, ListMultimap theGlobalInvokers) { + for (Iterator> iter = theGlobalInvokers.entries().iterator(); iter.hasNext(); ) { + Map.Entry next = iter.next(); + Object nextInterceptor = next.getValue().getInterceptor(); + if (theShouldUnregisterFunction.apply(nextInterceptor)) { + iter.remove(); + } + } + } + @Override public boolean registerThreadLocalInterceptor(Object theInterceptor) { if (!myThreadlocalInvokersEnabled) { diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/changes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/changes.yaml index fd6d640f6b3..a763784c8b7 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/changes.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/changes.yaml @@ -4,7 +4,7 @@ title: "The version of a few dependencies have been bumped to the latest versions (dependent HAPI modules listed in brackets): - Hibernate ORM (JPA): 5.4.6 -> 5.4.10 + Hibernate ORM (JPA): 5.4.6.Final -> 5.4.11.Final " - item: type: change 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 8a5bfe6f555..8b2a70ea717 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 @@ -390,6 +390,19 @@ public abstract class BaseHapiFhirResourceDao extends B ResourceTable entity = new ResourceTable(); entity.setResourceType(toResourceName(theResource)); + if (myDaoConfig.isMultiTenancyEnabled()) { + // Interceptor call: STORAGE_TENANT_IDENTIFY_CREATE + HookParams params = new HookParams() + .add(IBaseResource.class, theResource) + .add(RequestDetails.class, theRequest) + .addIfMatchesType(ServletRequestDetails.class, theRequest); + TenantId tenantId = (TenantId) doCallHooksAndReturnObject(theRequest, Pointcut.STORAGE_TENANT_IDENTIFY_CREATE, params); + if (tenantId != null) { + ourLog.debug("Resource has been assigned tenant ID: {}", tenantId); + entity.setTenantId(tenantId); + } + } + if (isNotBlank(theIfNoneExist)) { Set match = myMatchResourceUrlService.processMatchUrl(theIfNoneExist, myResourceType, theRequest); if (match.size() > 1) { @@ -432,7 +445,7 @@ public abstract class BaseHapiFhirResourceDao extends B notifyInterceptors(RestOperationTypeEnum.CREATE, requestDetails); } - // Notify JPA interceptors + // Interceptor call: STORAGE_PRESTORAGE_RESOURCE_CREATED HookParams hookParams = new HookParams() .add(IBaseResource.class, theResource) .add(RequestDetails.class, theRequest) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java index b845ea07598..f996a9936cc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java @@ -54,7 +54,6 @@ import java.util.Set; import static ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.OO_SEVERITY_ERROR; import static ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.OO_SEVERITY_INFO; import static org.apache.commons.lang3.StringUtils.defaultString; -import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; public abstract class BaseStorageDao { @@ -161,6 +160,10 @@ public abstract class BaseStorageDao { JpaInterceptorBroadcaster.doCallHooks(getInterceptorBroadcaster(), theRequestDetails, thePointcut, theParams); } + protected Object doCallHooksAndReturnObject(RequestDetails theRequestDetails, Pointcut thePointcut, HookParams theParams) { + return JpaInterceptorBroadcaster.doCallHooksAndReturnObject(getInterceptorBroadcaster(), theRequestDetails, thePointcut, theParams); + } + protected abstract IInterceptorBroadcaster getInterceptorBroadcaster(); public IBaseOperationOutcome createErrorOperationOutcome(String theMessage, String theCode) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java index 6168d8fe5e4..2f5936b3c0e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java @@ -183,6 +183,11 @@ public class DaoConfig { */ private boolean myPopulateIdentifierInAutoCreatedPlaceholderReferenceTargets; + /** + * @since 4.3.0 + */ + private boolean myMultiTenancyEnabled; + /** * Constructor */ @@ -1907,7 +1912,25 @@ public class DaoConfig { setPreExpandValueSetsDefaultCount(Math.min(getPreExpandValueSetsDefaultCount(), getPreExpandValueSetsMaxCount())); } - public enum StoreMetaSourceInformationEnum { + /** + * If enabled (default is false) the JPA server will support multitenant queries + * + * @since 4.3.0 + */ + public void setMultiTenancyEnabled(boolean theMultiTenancyEnabled) { + myMultiTenancyEnabled = theMultiTenancyEnabled; + } + + /** + * If enabled (default is false) the JPA server will support multitenant queries + * + * @since 4.3.0 + */ + public boolean isMultiTenancyEnabled() { + return myMultiTenancyEnabled; + } + + public enum StoreMetaSourceInformationEnum { NONE(false, false), SOURCE_URI(true, false), REQUEST_ID(false, true), diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java index a5b96de34b1..54a5e373e84 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java @@ -70,6 +70,7 @@ public class DaoSearchParamSynchronizer { theEntity.getParamsQuantity().remove(next); } for (T next : quantitiesToAdd) { + next.setTenantId(theEntity.getTenantId()); myEntityManager.merge(next); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/MultitenantR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/MultitenantR4Test.java index bc68bfaef01..23597b1e688 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/MultitenantR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/MultitenantR4Test.java @@ -1,84 +1,31 @@ package ca.uhn.fhir.jpa.dao.r4; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; +import ca.uhn.fhir.interceptor.api.Hook; +import ca.uhn.fhir.interceptor.api.Interceptor; +import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.dao.DaoConfig; -import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum; -import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; +import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString; import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.model.entity.ResourceTag; -import ca.uhn.fhir.jpa.model.entity.TagTypeEnum; +import ca.uhn.fhir.jpa.model.entity.TenantId; import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; -import ca.uhn.fhir.model.primitive.IdDt; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; -import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.hamcrest.Matchers; -import org.hl7.fhir.instance.model.api.IAnyResource; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.r4.model.*; -import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; -import org.hl7.fhir.r4.model.Bundle.BundleEntryRequestComponent; -import org.hl7.fhir.r4.model.Bundle.BundleEntryResponseComponent; -import org.hl7.fhir.r4.model.Bundle.BundleType; -import org.hl7.fhir.r4.model.Bundle.HTTPVerb; -import org.hl7.fhir.r4.model.Observation.ObservationStatus; -import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity; +import org.apache.commons.lang3.Validate; +import org.hl7.fhir.r4.model.Patient; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.TransactionCallback; -import org.springframework.transaction.support.TransactionCallbackWithoutResult; -import org.springframework.transaction.support.TransactionTemplate; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.math.BigDecimal; -import java.nio.charset.StandardCharsets; +import javax.servlet.ServletException; import java.time.LocalDate; import java.time.Month; +import java.util.Collections; import java.util.Date; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.emptyString; -import static org.hamcrest.Matchers.endsWith; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.matchesPattern; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.startsWith; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; public class MultitenantR4Test extends BaseJpaR4SystemTest { @@ -86,10 +33,17 @@ public class MultitenantR4Test extends BaseJpaR4SystemTest { @After public void after() { + myDaoConfig.setMultiTenancyEnabled(new DaoConfig().isMultiTenancyEnabled()); + + myInterceptorRegistry.unregisterInterceptorsIf(t -> t instanceof MyInterceptor); } + @Override @Before - public void beforeDisableResultReuse() { + public void before() throws ServletException { + super.before(); + + myDaoConfig.setMultiTenancyEnabled(true); } @@ -100,28 +54,57 @@ public class MultitenantR4Test extends BaseJpaR4SystemTest { p.setBirthDate(new Date()); Long patientId = myPatientDao.create(p).getId().getIdPartAsLong(); - runInTransaction(()->{ - ResourceTable resourceTable = myResourceTableDao.findById(patientId).orElseThrow(() -> new IllegalArgumentException()); + runInTransaction(() -> { + ResourceTable resourceTable = myResourceTableDao.findById(patientId).orElseThrow(IllegalArgumentException::new); assertNull(resourceTable.getTenantId()); }); } @Test public void testCreateResourceWithTenant() { + int expectId = 3; + LocalDate expectDate = LocalDate.of(2020, Month.JANUARY, 14); + myInterceptorRegistry.registerInterceptor(new MyInterceptor(new TenantId(expectId, expectDate))); + Patient p = new Patient(); - p.setUserData(JpaConstants.USERDATA_TENANT_ID, 3); - p.setUserData(JpaConstants.USERDATA_TENANT_DATE, LocalDate.of(2020, Month.JANUARY, 14)); + p.addName().setFamily("FAM"); p.addIdentifier().setSystem("system").setValue("value"); p.setBirthDate(new Date()); Long patientId = myPatientDao.create(p).getId().getIdPartAsLong(); - runInTransaction(()->{ - ResourceTable resourceTable = myResourceTableDao.findById(patientId).orElseThrow(() -> new IllegalArgumentException()); - assertNull(resourceTable.getTenantId()); + runInTransaction(() -> { + ResourceTable resourceTable = myResourceTableDao.findById(patientId).orElseThrow(IllegalArgumentException::new); + assertEquals(expectId, resourceTable.getTenantId().getTenantId().intValue()); + assertEquals(expectDate, resourceTable.getTenantId().getTenantDate()); + + List strings = myResourceIndexedSearchParamStringDao.findAll(); + ourLog.info("\n * {}", strings.stream().map(ResourceIndexedSearchParamString::toString).collect(Collectors.joining("\n * "))); + assertEquals(10, strings.size()); + assertEquals(expectId, strings.get(0).getTenantId().getTenantId().intValue()); + assertEquals(expectDate, strings.get(0).getTenantId().getTenantDate()); }); } + @Interceptor + public static class MyInterceptor { + + private final List myTenantIds; + + public MyInterceptor(TenantId theTenantId) { + Validate.notNull(theTenantId); + myTenantIds = Collections.singletonList(theTenantId); + } + + @Hook(Pointcut.STORAGE_TENANT_IDENTIFY_CREATE) + public TenantId tenantIdentifyCreate() { + TenantId retVal = myTenantIds.get(0); + ourLog.info("Returning tenant ID: {}", retVal); + return retVal; + } + + } + @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndex.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndex.java index df24e65b0b4..2f2d9b7024d 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndex.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndex.java @@ -20,10 +20,24 @@ package ca.uhn.fhir.jpa.model.entity; * #L% */ +import javax.persistence.Embedded; +import javax.persistence.MappedSuperclass; import java.io.Serializable; +@MappedSuperclass public abstract class BaseResourceIndex implements Serializable { + @Embedded + private TenantId myTenantId; + + public TenantId getTenantId() { + return myTenantId; + } + + public void setTenantId(TenantId theTenantId) { + myTenantId = theTenantId; + } + public abstract Long getId(); public abstract void setId(Long theId); diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java index b88d63e6cb1..032db5adac8 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java @@ -80,17 +80,6 @@ public abstract class BaseResourceIndexedSearchParam extends BaseResourceIndex { @Temporal(TemporalType.TIMESTAMP) private Date myUpdated; - @Embedded - private TenantId myTenantId; - - public TenantId getTenantId() { - return myTenantId; - } - - public void setTenantId(TenantId theTenantId) { - myTenantId = theTenantId; - } - /** * Subclasses may override */ diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java index cf6511f28ab..1fea6e588af 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java @@ -221,6 +221,13 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas @Transient private transient ResourceHistoryTable myCurrentVersionEntity; + /** + * Constructor + */ + public ResourceTable() { + super(); + } + @Override public ResourceTag addTag(TagDefinition theTag) { for (ResourceTag next : getTags()) { @@ -423,6 +430,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas return this; } + @Override public Collection getTags() { if (myTags == null) { myTags = new HashSet<>(); @@ -551,6 +559,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas retVal.setFhirVersion(getFhirVersion()); retVal.setDeleted(getDeleted()); retVal.setForcedId(getForcedId()); + retVal.setTenantId(getTenantId()); retVal.getTags().clear(); diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/TenantId.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/TenantId.java index 3143e8559cf..555f98cd016 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/TenantId.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/TenantId.java @@ -1,5 +1,8 @@ package ca.uhn.fhir.jpa.model.entity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + import javax.persistence.Column; import javax.persistence.Embeddable; import java.time.LocalDate; @@ -12,7 +15,22 @@ public class TenantId implements Cloneable { @Column(name = "TENANT_DATE", nullable = true) private LocalDate myTenantDate; - public Integer getTenantId() { + /** + * Constructor + */ + public TenantId() { + super(); + } + + /** + * Constructor + */ + public TenantId(int theTenantId, LocalDate theTenantDate) { + setTenantId(theTenantId); + setTenantDate(theTenantDate); + } + + public Integer getTenantId() { return myTenantId; } @@ -36,4 +54,12 @@ public class TenantId implements Cloneable { .setTenantId(getTenantId()) .setTenantDate(getTenantDate()); } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append("id", myTenantId) + .append("date", myTenantDate) + .toString(); + } } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java index d5a9a547828..98265649049 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java @@ -24,9 +24,6 @@ import ca.uhn.fhir.rest.api.Constants; public class JpaConstants { - public static final String USERDATA_TENANT_ID = JpaConstants.class.getName() + "_USERDATA_TENANT_ID"; - public static final String USERDATA_TENANT_DATE = JpaConstants.class.getName() + "_USERDATA_TENANT_DATE"; - /** * Operation name for the $apply-codesystem-delta-add operation */ diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java index fdb1dccca7d..e66b4eb4e26 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java @@ -9,6 +9,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; import org.apache.commons.dbcp2.BasicDataSource; +import org.apache.commons.lang3.time.DateUtils; import org.hibernate.dialect.PostgreSQL94Dialect; import org.hl7.fhir.dstu2.model.Subscription; import org.springframework.beans.factory.annotation.Value; @@ -94,6 +95,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 { retVal.setUsername(myDbUsername); retVal.setPassword(myDbPassword); retVal.setDefaultQueryTimeout(20); + retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE); return retVal; } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java index eed61ff64af..e174228ffe1 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java @@ -10,6 +10,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; import org.apache.commons.dbcp2.BasicDataSource; +import org.apache.commons.lang3.time.DateUtils; import org.hibernate.dialect.PostgreSQL94Dialect; import org.hl7.fhir.dstu2.model.Subscription; import org.springframework.beans.factory.annotation.Autowire; @@ -101,6 +102,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 { retVal.setUsername(myDbUsername); retVal.setPassword(myDbPassword); retVal.setDefaultQueryTimeout(20); + retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE); return retVal; } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java index 6b836aab1b4..d290cf3c385 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java @@ -10,6 +10,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; import org.apache.commons.dbcp2.BasicDataSource; +import org.apache.commons.lang3.time.DateUtils; import org.hibernate.dialect.PostgreSQL94Dialect; import org.hl7.fhir.dstu2.model.Subscription; import org.springframework.beans.factory.annotation.Autowire; @@ -86,6 +87,7 @@ public class TestR4Config extends BaseJavaConfigR4 { retVal.setUsername(myDbUsername); retVal.setPassword(myDbPassword); retVal.setDefaultQueryTimeout(20); + retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE); return retVal; } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java index c71bfd1259c..922e2087dad 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java @@ -10,6 +10,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; import org.apache.commons.dbcp2.BasicDataSource; +import org.apache.commons.lang3.time.DateUtils; import org.hibernate.dialect.PostgreSQL94Dialect; import org.hl7.fhir.dstu2.model.Subscription; import org.springframework.beans.factory.annotation.Autowire; @@ -86,6 +87,7 @@ public class TestR5Config extends BaseJavaConfigR5 { retVal.setUsername(myDbUsername); retVal.setPassword(myDbPassword); retVal.setDefaultQueryTimeout(20); + retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE); return retVal; } diff --git a/pom.xml b/pom.xml index 2ef99fd2479..719bb34d765 100644 --- a/pom.xml +++ b/pom.xml @@ -645,7 +645,7 @@ 3.0.2 6.1.0 - 5.4.10.Final + 5.4.11.Final 5.11.3.Final 5.5.5 @@ -1326,7 +1326,7 @@ org.postgresql postgresql - 42.2.9 + 42.2.10 org.quartz-scheduler
true
$expunge
+ * Hooks will be passed a reference to a counter containing the current number of records that have been deleted. + * If the hook deletes any records, the hook is expected to increment this counter by the number of records deleted. + *
+ * Hooks should return an instance of ca.uhn.fhir.jpa.model.entity.TenantId or null. + *
ca.uhn.fhir.jpa.model.entity.TenantId
null
false