Rewrite deferred interceptor callback framework (#2121)
* Rewrite deferred interceptor callback framework * Improve deferred handling * Null guard * Test fix
This commit is contained in:
parent
ebbdafb107
commit
d2aae361bf
|
@ -46,6 +46,7 @@ import org.hl7.fhir.instance.model.api.IBaseParameters;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
@ -81,7 +82,7 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
|||
* won't be indexed and searches won't work.
|
||||
* @param theRequestDetails TODO
|
||||
*/
|
||||
DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, TransactionDetails theTransactionDetails, RequestDetails theRequestDetails);
|
||||
DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, @Nonnull TransactionDetails theTransactionDetails, RequestDetails theRequestDetails);
|
||||
|
||||
DaoMethodOutcome create(T theResource, String theIfNoneExist, RequestDetails theRequestDetails);
|
||||
|
||||
|
@ -95,7 +96,7 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
|||
* This method does not throw an exception if there are delete conflicts, but populates them
|
||||
* in the provided list
|
||||
*/
|
||||
DaoMethodOutcome delete(IIdType theResource, DeleteConflictList theDeleteConflictsListToPopulate, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails);
|
||||
DaoMethodOutcome delete(IIdType theResource, DeleteConflictList theDeleteConflictsListToPopulate, RequestDetails theRequestDetails, @Nonnull TransactionDetails theTransactionDetails);
|
||||
|
||||
/**
|
||||
* This method throws an exception if there are delete conflicts
|
||||
|
@ -167,7 +168,6 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
|||
T readByPid(ResourcePersistentId thePid);
|
||||
|
||||
/**
|
||||
* @param theId
|
||||
* @param theRequestDetails TODO
|
||||
* @throws ResourceNotFoundException If the ID is not known to the server
|
||||
*/
|
||||
|
@ -244,7 +244,7 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
|||
* @param theForceUpdateVersion Create a new version with the same contents as the current version even if the content hasn't changed (this is mostly useful for
|
||||
* resources mapping to external content such as external code systems)
|
||||
*/
|
||||
DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing, boolean theForceUpdateVersion, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails);
|
||||
DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing, boolean theForceUpdateVersion, RequestDetails theRequestDetails, @Nonnull TransactionDetails theTransactionDetails);
|
||||
|
||||
/**
|
||||
* Not supported in DSTU1!
|
||||
|
|
|
@ -113,8 +113,6 @@ import org.springframework.context.ApplicationContext;
|
|||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.persistence.EntityManager;
|
||||
|
@ -1258,7 +1256,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
.add(RequestDetails.class, theRequestDetails)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
|
||||
.add(TransactionDetails.class, theTransactionDetails);
|
||||
doCallHooks(theRequestDetails, Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED, hookParams);
|
||||
doCallHooks(theTransactionDetails, theRequestDetails, Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED, hookParams);
|
||||
|
||||
// Perform update
|
||||
ResourceTable savedEntity = updateEntity(theRequestDetails, theResource, entity, null, thePerformIndexing, thePerformIndexing, theTransactionDetails, theForceUpdateVersion, thePerformIndexing);
|
||||
|
@ -1283,18 +1281,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
|
||||
// Notify interceptors
|
||||
if (!savedEntity.isUnchangedInCurrentOperation()) {
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
|
||||
@Override
|
||||
public void beforeCommit(boolean readOnly) {
|
||||
HookParams hookParams = new HookParams()
|
||||
.add(IBaseResource.class, theOldResource)
|
||||
.add(IBaseResource.class, theResource)
|
||||
.add(RequestDetails.class, theRequestDetails)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
|
||||
.add(TransactionDetails.class, theTransactionDetails);
|
||||
doCallHooks(theRequestDetails, Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED, hookParams);
|
||||
}
|
||||
});
|
||||
hookParams = new HookParams()
|
||||
.add(IBaseResource.class, theOldResource)
|
||||
.add(IBaseResource.class, theResource)
|
||||
.add(RequestDetails.class, theRequestDetails)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
|
||||
.add(TransactionDetails.class, theTransactionDetails);
|
||||
doCallHooks(theTransactionDetails, theRequestDetails, Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED, hookParams);
|
||||
}
|
||||
|
||||
return savedEntity;
|
||||
|
@ -1461,8 +1454,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
|
||||
StringBuilder retVal = new StringBuilder();
|
||||
List<IPrimitiveType<String>> childElements = theContext.newTerser().getAllPopulatedChildElementsOfType(theResource, stringType);
|
||||
for (@SuppressWarnings("rawtypes")
|
||||
IPrimitiveType<String> nextType : childElements) {
|
||||
for (IPrimitiveType<String> nextType : childElements) {
|
||||
if (stringType.equals(nextType.getClass())) {
|
||||
String nextValue = nextType.getValueAsString();
|
||||
if (isNotBlank(nextValue)) {
|
||||
|
|
|
@ -207,7 +207,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
|
||||
@Override
|
||||
public DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, TransactionDetails theTransactionDetails, RequestDetails theRequestDetails) {
|
||||
public DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, @Nonnull TransactionDetails theTransactionDetails, RequestDetails theRequestDetails) {
|
||||
return myTransactionService.execute(theRequestDetails, tx -> doCreateForPost(theResource, theIfNoneExist, thePerformIndexing, theTransactionDetails, theRequestDetails));
|
||||
}
|
||||
|
||||
|
@ -305,7 +305,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
.add(RequestDetails.class, theRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequest)
|
||||
.add(TransactionDetails.class, theTransactionDetails);
|
||||
doCallHooks(theRequest, Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED, hookParams);
|
||||
doCallHooks(theTransactionDetails, theRequest, Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED, hookParams);
|
||||
|
||||
// Perform actual DB update
|
||||
ResourceTable updatedEntity = updateEntity(theRequest, theResource, entity, null, thePerformIndexing, thePerformIndexing, theTransactionDetails, false, thePerformIndexing);
|
||||
|
@ -343,17 +343,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
// Notify JPA interceptors
|
||||
if (!updatedEntity.isUnchangedInCurrentOperation()) {
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
|
||||
@Override
|
||||
public void beforeCommit(boolean readOnly) {
|
||||
HookParams hookParams = new HookParams()
|
||||
.add(IBaseResource.class, theResource)
|
||||
.add(RequestDetails.class, theRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequest)
|
||||
.add(TransactionDetails.class, theTransactionDetails);
|
||||
doCallHooks(theRequest, Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED, hookParams);
|
||||
}
|
||||
});
|
||||
hookParams = new HookParams()
|
||||
.add(IBaseResource.class, theResource)
|
||||
.add(RequestDetails.class, theRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequest)
|
||||
.add(TransactionDetails.class, theTransactionDetails);
|
||||
doCallHooks(theTransactionDetails, theRequest, Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED, hookParams);
|
||||
}
|
||||
|
||||
DaoMethodOutcome outcome = toMethodOutcome(theRequest, entity, theResource).setCreated(true);
|
||||
|
@ -400,7 +395,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
|
||||
@Override
|
||||
public DaoMethodOutcome delete(IIdType theId, DeleteConflictList theDeleteConflicts, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails) {
|
||||
public DaoMethodOutcome delete(IIdType theId, DeleteConflictList theDeleteConflicts, RequestDetails theRequestDetails, @Nonnull TransactionDetails theTransactionDetails) {
|
||||
validateIdPresentForDelete(theId);
|
||||
validateDeleteEnabled();
|
||||
|
||||
|
@ -439,7 +434,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
.add(RequestDetails.class, theRequestDetails)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
|
||||
.add(TransactionDetails.class, theTransactionDetails);
|
||||
doCallHooks(theRequestDetails, Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED, hook);
|
||||
doCallHooks(theTransactionDetails, theRequestDetails, Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED, hook);
|
||||
|
||||
myDeleteConflictService.validateOkToDelete(theDeleteConflicts, entity, false, theRequestDetails, theTransactionDetails);
|
||||
|
||||
|
@ -455,17 +450,16 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
resourceToDelete.setId(entity.getIdDt());
|
||||
|
||||
// Notify JPA interceptors
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
|
||||
@Override
|
||||
public void beforeCommit(boolean readOnly) {
|
||||
HookParams hookParams = new HookParams()
|
||||
.add(IBaseResource.class, resourceToDelete)
|
||||
.add(RequestDetails.class, theRequestDetails)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
|
||||
.add(TransactionDetails.class, theTransactionDetails);
|
||||
doCallHooks(theRequestDetails, Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED, hookParams);
|
||||
}
|
||||
});
|
||||
HookParams hookParams = new HookParams()
|
||||
.add(IBaseResource.class, resourceToDelete)
|
||||
.add(RequestDetails.class, theRequestDetails)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
|
||||
.add(TransactionDetails.class, theTransactionDetails);
|
||||
if (theTransactionDetails.isAcceptingDeferredInterceptorBroadcasts()) {
|
||||
theTransactionDetails.addDeferredInterceptorBroadcast(Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED, hookParams);
|
||||
} else {
|
||||
doCallHooks(theTransactionDetails, theRequestDetails, Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED, hookParams);
|
||||
}
|
||||
|
||||
DaoMethodOutcome outcome = toMethodOutcome(theRequestDetails, savedEntity, resourceToDelete).setCreated(true);
|
||||
|
||||
|
@ -516,7 +510,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
@NotNull
|
||||
@Override
|
||||
public DeleteMethodOutcome deletePidList(String theUrl, Collection<ResourcePersistentId> theResourceIds, DeleteConflictList theDeleteConflicts, RequestDetails theTheRequest) {
|
||||
public DeleteMethodOutcome deletePidList(String theUrl, Collection<ResourcePersistentId> theResourceIds, DeleteConflictList theDeleteConflicts, RequestDetails theRequest) {
|
||||
StopWatch w = new StopWatch();
|
||||
TransactionDetails transactionDetails = new TransactionDetails();
|
||||
List<ResourceTable> deletedResources = new ArrayList<>();
|
||||
|
@ -529,23 +523,23 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
// Notify IServerOperationInterceptors about pre-action call
|
||||
HookParams hooks = new HookParams()
|
||||
.add(IBaseResource.class, resourceToDelete)
|
||||
.add(RequestDetails.class, theTheRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theTheRequest)
|
||||
.add(RequestDetails.class, theRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequest)
|
||||
.add(TransactionDetails.class, transactionDetails);
|
||||
doCallHooks(theTheRequest, Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED, hooks);
|
||||
doCallHooks(transactionDetails, theRequest, Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED, hooks);
|
||||
|
||||
myDeleteConflictService.validateOkToDelete(theDeleteConflicts, entity, false, theTheRequest, transactionDetails);
|
||||
myDeleteConflictService.validateOkToDelete(theDeleteConflicts, entity, false, theRequest, transactionDetails);
|
||||
|
||||
// Notify interceptors
|
||||
IdDt idToDelete = entity.getIdDt();
|
||||
if (theTheRequest != null) {
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theTheRequest, idToDelete.getResourceType(), idToDelete);
|
||||
if (theRequest != null) {
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, idToDelete.getResourceType(), idToDelete);
|
||||
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
|
||||
}
|
||||
|
||||
// Perform delete
|
||||
|
||||
updateEntityForDelete(theTheRequest, transactionDetails, entity);
|
||||
updateEntityForDelete(theRequest, transactionDetails, entity);
|
||||
resourceToDelete.setId(entity.getIdDt());
|
||||
|
||||
// Notify JPA interceptors
|
||||
|
@ -554,10 +548,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
public void beforeCommit(boolean readOnly) {
|
||||
HookParams hookParams = new HookParams()
|
||||
.add(IBaseResource.class, resourceToDelete)
|
||||
.add(RequestDetails.class, theTheRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theTheRequest)
|
||||
.add(RequestDetails.class, theRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequest)
|
||||
.add(TransactionDetails.class, transactionDetails);
|
||||
doCallHooks(theTheRequest, Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED, hookParams);
|
||||
doCallHooks(transactionDetails, theRequest, Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED, hookParams);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1359,7 +1353,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
|
||||
@Override
|
||||
public DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing, boolean theForceUpdateVersion, RequestDetails theRequest, TransactionDetails theTransactionDetails) {
|
||||
public DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing, boolean theForceUpdateVersion, RequestDetails theRequest, @Nonnull TransactionDetails theTransactionDetails) {
|
||||
if (theResource == null) {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "missingBody");
|
||||
throw new InvalidRequestException(msg);
|
||||
|
|
|
@ -40,6 +40,7 @@ import ca.uhn.fhir.rest.api.server.IPreResourceShowDetails;
|
|||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.SimplePreResourceAccessDetails;
|
||||
import ca.uhn.fhir.rest.api.server.SimplePreResourceShowDetails;
|
||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||
import ca.uhn.fhir.rest.param.QualifierDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
@ -184,8 +185,12 @@ public abstract class BaseStorageDao {
|
|||
return outcome;
|
||||
}
|
||||
|
||||
protected void doCallHooks(RequestDetails theRequestDetails, Pointcut thePointcut, HookParams theParams) {
|
||||
JpaInterceptorBroadcaster.doCallHooks(getInterceptorBroadcaster(), theRequestDetails, thePointcut, theParams);
|
||||
protected void doCallHooks(TransactionDetails theTransactionDetails, RequestDetails theRequestDetails, Pointcut thePointcut, HookParams theParams) {
|
||||
if (theTransactionDetails.isAcceptingDeferredInterceptorBroadcasts(thePointcut)) {
|
||||
theTransactionDetails.addDeferredInterceptorBroadcast(thePointcut, theParams);
|
||||
} else {
|
||||
JpaInterceptorBroadcaster.doCallHooks(getInterceptorBroadcaster(), theRequestDetails, thePointcut, theParams);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract IInterceptorBroadcaster getInterceptorBroadcaster();
|
||||
|
|
|
@ -68,6 +68,7 @@ import ca.uhn.fhir.util.ResourceReferenceInfo;
|
|||
import ca.uhn.fhir.util.StopWatch;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
|
@ -540,9 +541,11 @@ public abstract class BaseTransactionProcessor {
|
|||
private Map<IBase, IBasePersistedResource> doTransactionWriteOperations(final ServletRequestDetails theRequest, String theActionName, TransactionDetails theTransactionDetails, Set<IIdType> theAllIds,
|
||||
Map<IIdType, IIdType> theIdSubstitutions, Map<IIdType, DaoMethodOutcome> theIdToPersistedOutcome, IBaseBundle theResponse, IdentityHashMap<IBase, Integer> theOriginalRequestOrder, List<IBase> theEntries, StopWatch theTransactionStopWatch) {
|
||||
|
||||
if (theRequest != null) {
|
||||
theRequest.startDeferredOperationCallback();
|
||||
}
|
||||
theTransactionDetails.beginAcceptingDeferredInterceptorBroadcasts(
|
||||
Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED,
|
||||
Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED,
|
||||
Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED
|
||||
);
|
||||
try {
|
||||
|
||||
Set<String> deletedResources = new HashSet<>();
|
||||
|
@ -980,11 +983,19 @@ public abstract class BaseTransactionProcessor {
|
|||
}
|
||||
ourLog.debug("Placeholder resource ID \"{}\" was replaced with permanent ID \"{}\"", next, replacement);
|
||||
}
|
||||
|
||||
ListMultimap<Pointcut, HookParams> deferredBroadcastEvents = theTransactionDetails.endAcceptingDeferredInterceptorBroadcasts();
|
||||
for (Map.Entry<Pointcut, HookParams> nextEntry : deferredBroadcastEvents.entries()) {
|
||||
Pointcut nextPointcut = nextEntry.getKey();
|
||||
HookParams nextParams = nextEntry.getValue();
|
||||
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, nextPointcut, nextParams);
|
||||
}
|
||||
|
||||
return entriesToProcess;
|
||||
|
||||
} finally {
|
||||
if (theRequest != null) {
|
||||
theRequest.stopDeferredRequestOperationCallbackAndRunDeferredItems();
|
||||
if (theTransactionDetails.isAcceptingDeferredInterceptorBroadcasts()) {
|
||||
theTransactionDetails.endAcceptingDeferredInterceptorBroadcasts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.api.model.DeleteConflict;
|
|||
import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
|
||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Condition;
|
||||
|
@ -243,11 +244,12 @@ public class DeleteConflictServiceR4Test extends BaseJpaR4Test {
|
|||
}
|
||||
|
||||
private DeleteConflictOutcome deleteConflictsFixedRetryCount(DeleteConflictList theList) {
|
||||
TransactionDetails transactionDetails = new TransactionDetails();
|
||||
for (DeleteConflict next : theList) {
|
||||
IdDt source = next.getSourceId();
|
||||
if ("Patient".equals(source.getResourceType())) {
|
||||
ourLog.info("Deleting {}", source);
|
||||
myPatientDao.delete(source, theList, null, null);
|
||||
myPatientDao.delete(source, theList, null, transactionDetails);
|
||||
++myInterceptorDeleteCount;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
|||
import ca.uhn.fhir.jpa.dao.expunge.ExpungeService;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -95,10 +96,11 @@ public class EmpiPersonDeletingSvc {
|
|||
|
||||
private void deleteConflictBatch(DeleteConflictList theDcl, IFhirResourceDao<IBaseResource> theDao) {
|
||||
DeleteConflictList newBatch = new DeleteConflictList();
|
||||
TransactionDetails transactionDetails = new TransactionDetails();
|
||||
for (DeleteConflict next : theDcl) {
|
||||
IdDt nextSource = next.getSourceId();
|
||||
ourLog.info("Have delete conflict {} - Cascading delete", nextSource);
|
||||
theDao.delete(nextSource.toVersionless(), newBatch, null, null);
|
||||
theDao.delete(nextSource.toVersionless(), newBatch, null, transactionDetails);
|
||||
}
|
||||
theDcl.removeAll();
|
||||
theDcl.addAll(newBatch);
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package ca.uhn.fhir.rest.api.server;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
|
@ -15,7 +13,6 @@ import org.apache.commons.lang3.Validate;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
@ -64,7 +61,6 @@ public abstract class RequestDetails {
|
|||
private String myOperation;
|
||||
private Map<String, String[]> myParameters;
|
||||
private byte[] myRequestContents;
|
||||
private DeferredOperationCallback myDeferredInterceptorBroadcaster;
|
||||
private String myRequestPath;
|
||||
private RequestTypeEnum myRequestType;
|
||||
private String myResourceName;
|
||||
|
@ -305,9 +301,6 @@ public abstract class RequestDetails {
|
|||
* all interceptors
|
||||
*/
|
||||
public IInterceptorBroadcaster getInterceptorBroadcaster() {
|
||||
if (myDeferredInterceptorBroadcaster != null) {
|
||||
return myDeferredInterceptorBroadcaster;
|
||||
}
|
||||
return myInterceptorBroadcaster;
|
||||
}
|
||||
|
||||
|
@ -500,26 +493,6 @@ public abstract class RequestDetails {
|
|||
myRequestContents = theRequestContents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getInterceptorBroadcaster()} () interceptor broadcaster} handler in
|
||||
* deferred mode, meaning that any notifications will be queued up for delivery, but
|
||||
* won't be delivered until {@link #stopDeferredRequestOperationCallbackAndRunDeferredItems()}
|
||||
* is called.
|
||||
*/
|
||||
public void startDeferredOperationCallback() {
|
||||
myDeferredInterceptorBroadcaster = new DeferredOperationCallback(myInterceptorBroadcaster);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #startDeferredOperationCallback()
|
||||
*/
|
||||
public void stopDeferredRequestOperationCallbackAndRunDeferredItems() {
|
||||
DeferredOperationCallback deferredCallback = myDeferredInterceptorBroadcaster;
|
||||
deferredCallback.playDeferredActions();
|
||||
myInterceptorBroadcaster = deferredCallback.getWrap();
|
||||
myDeferredInterceptorBroadcaster = null;
|
||||
}
|
||||
|
||||
public String getTransactionGuid() {
|
||||
return myTransactionGuid;
|
||||
}
|
||||
|
@ -529,47 +502,4 @@ public abstract class RequestDetails {
|
|||
}
|
||||
|
||||
|
||||
private class DeferredOperationCallback implements IInterceptorBroadcaster {
|
||||
|
||||
private final IInterceptorBroadcaster myWrap;
|
||||
private final List<Runnable> myDeferredTasks = new ArrayList<>();
|
||||
|
||||
private DeferredOperationCallback(@Nonnull IInterceptorBroadcaster theWrap) {
|
||||
Validate.notNull(theWrap);
|
||||
myWrap = theWrap;
|
||||
}
|
||||
|
||||
|
||||
void playDeferredActions() {
|
||||
myDeferredTasks.forEach(Runnable::run);
|
||||
}
|
||||
|
||||
IInterceptorBroadcaster getWrap() {
|
||||
return myWrap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean callHooks(Pointcut thePointcut, HookParams theParams) {
|
||||
myDeferredTasks.add(() -> myWrap.callHooks(thePointcut, theParams));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object callHooksAndReturnObject(Pointcut thePointcut, HookParams theParams) {
|
||||
if (!thePointcut.getReturnType().equals(void.class)) {
|
||||
return myWrap.callHooksAndReturnObject(thePointcut, theParams);
|
||||
}
|
||||
|
||||
myDeferredTasks.add(() -> myWrap.callHooksAndReturnObject(thePointcut, theParams));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasHooks(Pointcut thePointcut) {
|
||||
return myWrap.hasHooks(thePointcut);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -20,10 +20,16 @@ package ca.uhn.fhir.rest.api.server.storage;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -34,7 +40,7 @@ import java.util.function.Supplier;
|
|||
* (i.e. a FHIR create, read, transaction, etc.).
|
||||
* <p>
|
||||
* The intent with this class is to hold things we want to pass from operation to operation within a transaction in
|
||||
* order to avoid looking things up multime times, etc.
|
||||
* order to avoid looking things up multiple times, etc.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.0.0
|
||||
|
@ -44,12 +50,14 @@ public class TransactionDetails {
|
|||
private final Date myTransactionDate;
|
||||
private Map<IIdType, ResourcePersistentId> myResolvedResourceIds = Collections.emptyMap();
|
||||
private Map<String, Object> myUserData;
|
||||
private ListMultimap<Pointcut, HookParams> myDeferredInterceptorBroadcasts;
|
||||
private EnumSet<Pointcut> myDeferredInterceptorBroadcastPointcuts;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public TransactionDetails() {
|
||||
myTransactionDate = new Date();
|
||||
this(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,5 +134,57 @@ public class TransactionDetails {
|
|||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be used by processors for FHIR transactions to defer interceptor broadcasts on sub-requests if needed
|
||||
*
|
||||
* @since 5.2.0
|
||||
*/
|
||||
public void beginAcceptingDeferredInterceptorBroadcasts(Pointcut... thePointcuts) {
|
||||
Validate.isTrue(!isAcceptingDeferredInterceptorBroadcasts());
|
||||
myDeferredInterceptorBroadcasts = ArrayListMultimap.create();
|
||||
myDeferredInterceptorBroadcastPointcuts = EnumSet.of(thePointcuts[0], thePointcuts);
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be used by processors for FHIR transactions to defer interceptor broadcasts on sub-requests if needed
|
||||
*
|
||||
* @since 5.2.0
|
||||
*/
|
||||
public boolean isAcceptingDeferredInterceptorBroadcasts() {
|
||||
return myDeferredInterceptorBroadcasts != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be used by processors for FHIR transactions to defer interceptor broadcasts on sub-requests if needed
|
||||
*
|
||||
* @since 5.2.0
|
||||
*/
|
||||
public boolean isAcceptingDeferredInterceptorBroadcasts(Pointcut thePointcut) {
|
||||
return myDeferredInterceptorBroadcasts != null && myDeferredInterceptorBroadcastPointcuts.contains(thePointcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be used by processors for FHIR transactions to defer interceptor broadcasts on sub-requests if needed
|
||||
*
|
||||
* @since 5.2.0
|
||||
*/
|
||||
public ListMultimap<Pointcut, HookParams> endAcceptingDeferredInterceptorBroadcasts() {
|
||||
Validate.isTrue(isAcceptingDeferredInterceptorBroadcasts());
|
||||
ListMultimap<Pointcut, HookParams> retVal = myDeferredInterceptorBroadcasts;
|
||||
myDeferredInterceptorBroadcasts = null;
|
||||
myDeferredInterceptorBroadcastPointcuts = null;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be used by processors for FHIR transactions to defer interceptor broadcasts on sub-requests if needed
|
||||
*
|
||||
* @since 5.2.0
|
||||
*/
|
||||
public void addDeferredInterceptorBroadcast(Pointcut thePointcut, HookParams theHookParams) {
|
||||
Validate.isTrue(isAcceptingDeferredInterceptorBroadcasts(thePointcut));
|
||||
myDeferredInterceptorBroadcasts.put(thePointcut, theHookParams);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue