Add validation suppression mode (#2675)

* Add validation suppression mode

* Add changelog

* Compile fix
This commit is contained in:
James Agnew 2021-05-26 17:41:48 -04:00 committed by GitHub
parent af44b9c7a0
commit 7a83c76e65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 7252 additions and 234 deletions

View File

@ -25,6 +25,7 @@ import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.instance.model.api.IBaseConformance;
import javax.annotation.Nonnull;
@ -45,6 +46,7 @@ import java.util.Set;
* <li>SERVER_xxx: Hooks on the HAPI FHIR Server framework</li>
* <li>SUBSCRIPTION_xxx: Hooks on the HAPI FHIR Subscription framework</li>
* <li>STORAGE_xxx: Hooks on the storage engine</li>
* <li>VALIDATION_xxx: Hooks on the HAPI FHIR Validation framework</li>
* <li>JPA_PERFTRACE_xxx: Performance tracing hooks on the JPA server</li>
* </ul>
* </p>
@ -1851,6 +1853,37 @@ public enum Pointcut implements IPointcut {
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
),
/**
* <b>Validation Hook:</b>
* This hook is called after validation has completed, regardless of whether the validation was successful or failed.
* Typically this is used to modify validation results.
* <p>
* <b>Note on validation Pointcuts:</b> The HAPI FHIR interceptor framework is a part of the client and server frameworks and
* not a part of the core FhirContext. Therefore this Pointcut is invoked by the
* </p>
* <p>
* Hooks may accept the following parameters:
* <ul>
* <li>
* org.hl7.fhir.instance.model.api.IBaseResource - The resource being validated, if a parsed version is available (null otherwise)
* </li>
* <li>
* java.lang.String - The resource being validated, if a raw version is available (null otherwise)
* </li>
* <li>
* ca.uhn.fhir.validation.ValidationResult - The outcome of the validation. Hooks methods should not modify this object, but they can return a new one.
* </li>
* </ul>
* </p>
* Hook methods may return an instance of {@link ca.uhn.fhir.validation.ValidationResult} if they wish to override the validation results, or they may return <code>null</code> or <code>void</code> otherwise.
*/
VALIDATION_COMPLETED(ValidationResult.class,
"org.hl7.fhir.instance.model.api.IBaseResource",
"java.lang.String",
"ca.uhn.fhir.validation.ValidationResult"
),
/**
* <b>MDM(EMPI) Hook:</b>
* Invoked whenever a persisted resource (a resource that has just been stored in the

View File

@ -19,13 +19,19 @@ package ca.uhn.fhir.validation;
* limitations under the License.
* #L%
*/
import java.util.*;
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.validation.schematron.SchematronProvider;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.schematron.SchematronProvider;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Resource validator, which checks resources for compliance against various validation schemes (schemas, schematrons, profiles, etc.)
@ -46,6 +52,7 @@ public class FhirValidator {
private static volatile Boolean ourPhPresentOnClasspath;
private final FhirContext myContext;
private List<IValidatorModule> myValidators = new ArrayList<>();
private IInterceptorBroadcaster myInterceptorBraodcaster;
/**
* Constructor (this should not be called directly, but rather {@link FhirContext#newValidator()} should be called to obtain an instance of {@link FhirValidator})
@ -65,7 +72,7 @@ public class FhirValidator {
registerValidatorModule(theInstance);
}
} else {
for (Iterator<IValidatorModule> iter = myValidators.iterator(); iter.hasNext();) {
for (Iterator<IValidatorModule> iter = myValidators.iterator(); iter.hasNext(); ) {
IValidatorModule next = iter.next();
if (next.getClass().equals(type)) {
unregisterValidatorModule(next);
@ -91,6 +98,16 @@ public class FhirValidator {
return haveValidatorOfType(SchemaBaseValidator.class);
}
/**
* Should the validator validate the resource against the base schema (the schema provided with the FHIR distribution itself)
*
* @return Returns a referens to <code>this<code> for method chaining
*/
public synchronized FhirValidator setValidateAgainstStandardSchema(boolean theValidateAgainstStandardSchema) {
addOrRemoveValidator(theValidateAgainstStandardSchema, SchemaBaseValidator.class, new SchemaBaseValidator(myContext));
return this;
}
/**
* Should the validator validate the resource against the base schema (the schema provided with the FHIR distribution itself)
*/
@ -104,33 +121,6 @@ public class FhirValidator {
return haveValidatorOfType(cls);
}
/**
* Add a new validator module to this validator. You may register as many modules as you like at any time.
*
* @param theValidator
* The validator module. Must not be null.
* @return Returns a reference to <code>this</code> for easy method chaining.
*/
public synchronized FhirValidator registerValidatorModule(IValidatorModule theValidator) {
Validate.notNull(theValidator, "theValidator must not be null");
ArrayList<IValidatorModule> newValidators = new ArrayList<IValidatorModule>(myValidators.size() + 1);
newValidators.addAll(myValidators);
newValidators.add(theValidator);
myValidators = newValidators;
return this;
}
/**
* Should the validator validate the resource against the base schema (the schema provided with the FHIR distribution itself)
*
* @return Returns a referens to <code>this<code> for method chaining
*/
public synchronized FhirValidator setValidateAgainstStandardSchema(boolean theValidateAgainstStandardSchema) {
addOrRemoveValidator(theValidateAgainstStandardSchema, SchemaBaseValidator.class, new SchemaBaseValidator(myContext));
return this;
}
/**
* Should the validator validate the resource against the base schematron (the schematron provided with the FHIR distribution itself)
*
@ -149,11 +139,26 @@ public class FhirValidator {
return this;
}
/**
* Add a new validator module to this validator. You may register as many modules as you like at any time.
*
* @param theValidator The validator module. Must not be null.
* @return Returns a reference to <code>this</code> for easy method chaining.
*/
public synchronized FhirValidator registerValidatorModule(IValidatorModule theValidator) {
Validate.notNull(theValidator, "theValidator must not be null");
ArrayList<IValidatorModule> newValidators = new ArrayList<IValidatorModule>(myValidators.size() + 1);
newValidators.addAll(myValidators);
newValidators.add(theValidator);
myValidators = newValidators;
return this;
}
/**
* Removes a validator module from this validator. You may register as many modules as you like, and remove them at any time.
*
* @param theValidator
* The validator module. Must not be null.
* @param theValidator The validator module. Must not be null.
*/
public synchronized void unregisterValidatorModule(IValidatorModule theValidator) {
Validate.notNull(theValidator, "theValidator must not be null");
@ -175,12 +180,10 @@ public class FhirValidator {
}
/**
* Validates a resource instance returning a {@link ca.uhn.fhir.validation.ValidationResult} which contains the results.
* Validates a resource instance returning a {@link ValidationResult} which contains the results.
*
* @param theResource
* the resource to validate
* @param theResource the resource to validate
* @return the results of validation
* @since 0.7
*/
@ -189,10 +192,9 @@ public class FhirValidator {
}
/**
* Validates a resource instance returning a {@link ca.uhn.fhir.validation.ValidationResult} which contains the results.
* Validates a resource instance returning a {@link ValidationResult} which contains the results.
*
* @param theResource
* the resource to validate
* @param theResource the resource to validate
* @return the results of validation
* @since 1.1
*/
@ -201,12 +203,10 @@ public class FhirValidator {
}
/**
* Validates a resource instance returning a {@link ca.uhn.fhir.validation.ValidationResult} which contains the results.
* Validates a resource instance returning a {@link ValidationResult} which contains the results.
*
* @param theResource
* the resource to validate
* @param theOptions
* Optionally provides options to the validator
* @param theResource the resource to validate
* @param theOptions Optionally provides options to the validator
* @return the results of validation
* @since 4.0.0
*/
@ -221,16 +221,32 @@ public class FhirValidator {
next.validateResource(ctx);
}
return ctx.toResult();
ValidationResult result = ctx.toResult();
result = invokeValidationCompletedHooks(theResource, null, result);
return result;
}
private ValidationResult invokeValidationCompletedHooks(IBaseResource theResourceParsed, String theResourceRaw, ValidationResult theValidationResult) {
if (myInterceptorBraodcaster != null) {
if (myInterceptorBraodcaster.hasHooks(Pointcut.VALIDATION_COMPLETED)) {
HookParams params = new HookParams()
.add(IBaseResource.class, theResourceParsed)
.add(String.class, theResourceRaw)
.add(ValidationResult.class, theValidationResult);
Object newResult = myInterceptorBraodcaster.callHooksAndReturnObject(Pointcut.VALIDATION_COMPLETED, params);
if (newResult != null) {
theValidationResult = (ValidationResult) newResult;
}
}
}
return theValidationResult;
}
/**
* Validates a resource instance returning a {@link ca.uhn.fhir.validation.ValidationResult} which contains the results.
* Validates a resource instance returning a {@link ValidationResult} which contains the results.
*
* @param theResource
* the resource to validate
* @param theOptions
* Optionally provides options to the validator
* @param theResource the resource to validate
* @param theOptions Optionally provides options to the validator
* @return the results of validation
* @since 4.0.0
*/
@ -245,6 +261,17 @@ public class FhirValidator {
next.validateResource(ctx);
}
return ctx.toResult();
ValidationResult result = ctx.toResult();
result = invokeValidationCompletedHooks(null, theResource, result);
return result;
}
/**
* Optionally supplies an interceptor broadcaster that will be used to invoke validation related Pointcut events
*
* @since 5.5.0
*/
public void setInterceptorBraodcaster(IInterceptorBroadcaster theInterceptorBraodcaster) {
myInterceptorBraodcaster = theInterceptorBraodcaster;
}
}

View File

@ -149,4 +149,11 @@ public class ValidationResult {
public String toString() {
return "ValidationResult{" + "messageCount=" + myMessages.size() + ", isSuccessful=" + myIsSuccessful + ", description='" + toDescription() + '\'' + '}';
}
/**
* @since 5.5.0
*/
public FhirContext getContext() {
return myCtx;
}
}

View File

@ -0,0 +1,5 @@
---
type: add
issue: 2675
title: "A new interceptor ValidationMessageSuppressingInterceptor has been added. This interceptor can be used
to selectively suppress specific vaLidation messages."

View File

@ -58,7 +58,7 @@ import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryResourceMatcher;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.jpa.util.AddRemoveCount;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.jpa.util.MemoryCacheService;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
@ -1308,14 +1308,14 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
// Interceptor broadcast: JPA_PERFTRACE_INFO
if (!presenceCount.isEmpty()) {
if (JpaInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequest)) {
if (CompositeInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequest)) {
StorageProcessingMessage message = new StorageProcessingMessage();
message.setMessage("For " + entity.getIdDt().toUnqualifiedVersionless().getValue() + " added " + presenceCount.getAddCount() + " and removed " + presenceCount.getRemoveCount() + " resource search parameter presence entries");
HookParams params = new HookParams()
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, message);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
}
}
@ -1336,14 +1336,14 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
// Interceptor broadcast: JPA_PERFTRACE_INFO
if (!searchParamAddRemoveCount.isEmpty()) {
if (JpaInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequest)) {
if (CompositeInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequest)) {
StorageProcessingMessage message = new StorageProcessingMessage();
message.setMessage("For " + entity.getIdDt().toUnqualifiedVersionless().getValue() + " added " + searchParamAddRemoveCount.getAddCount() + " and removed " + searchParamAddRemoveCount.getRemoveCount() + " resource search parameter index entries");
HookParams params = new HookParams()
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, message);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
}
}

View File

@ -57,7 +57,7 @@ import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.jpa.util.MemoryCacheService;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.dstu2.resource.ListResource;
@ -1156,7 +1156,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
.add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
if (accessDetails.isDontReturnResourceAtIndex(0)) {
throw new ResourceNotFoundException(theId);
}
@ -1169,7 +1169,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
.add(IPreResourceShowDetails.class, showDetails)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
//noinspection unchecked
retVal = (T) showDetails.getResource(0);
}
@ -1700,6 +1700,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
FhirValidator validator = getContext().newValidator();
validator.setInterceptorBraodcaster(CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest));
validator.registerValidatorModule(getInstanceValidator());
validator.registerValidatorModule(new IdChecker(theMode));

View File

@ -37,7 +37,7 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.util.JpaParamUtil;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.rest.api.QualifiedParamList;
import ca.uhn.fhir.rest.api.server.IPreResourceAccessDetails;
@ -247,7 +247,7 @@ public abstract class BaseStorageDao {
.add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
JpaInterceptorBroadcaster.doCallHooks(getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
CompositeInterceptorBroadcaster.doCallHooks(getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
if (accessDetails.isDontReturnResourceAtIndex(0)) {
outcome.setResource(null);
}
@ -264,7 +264,7 @@ public abstract class BaseStorageDao {
.add(IPreResourceShowDetails.class, showDetails)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
JpaInterceptorBroadcaster.doCallHooks(getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
CompositeInterceptorBroadcaster.doCallHooks(getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
outcome.setResource(showDetails.getResource(0));
}
});
@ -285,7 +285,7 @@ public abstract class BaseStorageDao {
.add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
JpaInterceptorBroadcaster.doCallHooks(getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
CompositeInterceptorBroadcaster.doCallHooks(getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
if (accessDetails.isDontReturnResourceAtIndex(0)) {
outcome.setResource(null);
}
@ -302,7 +302,7 @@ public abstract class BaseStorageDao {
.add(IPreResourceShowDetails.class, showDetails)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
JpaInterceptorBroadcaster.doCallHooks(getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
CompositeInterceptorBroadcaster.doCallHooks(getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
outcome.setResource(showDetails.getResource(0));
}
});
@ -316,7 +316,7 @@ public abstract class BaseStorageDao {
if (theTransactionDetails.isAcceptingDeferredInterceptorBroadcasts(thePointcut)) {
theTransactionDetails.addDeferredInterceptorBroadcast(thePointcut, theParams);
} else {
JpaInterceptorBroadcaster.doCallHooks(getInterceptorBroadcaster(), theRequestDetails, thePointcut, theParams);
CompositeInterceptorBroadcaster.doCallHooks(getInterceptorBroadcaster(), theRequestDetails, thePointcut, theParams);
}
}

View File

@ -39,7 +39,7 @@ import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.parser.DataFormatException;
@ -526,7 +526,7 @@ public abstract class BaseTransactionProcessor {
transactionStopWatch.endCurrentTask();
// Interceptor broadcast: JPA_PERFTRACE_INFO
if (JpaInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequestDetails)) {
if (CompositeInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequestDetails)) {
String taskDurations = transactionStopWatch.formatTaskDurations();
StorageProcessingMessage message = new StorageProcessingMessage();
message.setMessage("Transaction timing:\n" + taskDurations);
@ -534,7 +534,7 @@ public abstract class BaseTransactionProcessor {
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(StorageProcessingMessage.class, message);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_INFO, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_INFO, params);
}
return response;
@ -955,7 +955,7 @@ public abstract class BaseTransactionProcessor {
for (Map.Entry<Pointcut, HookParams> nextEntry : deferredBroadcastEvents.entries()) {
Pointcut nextPointcut = nextEntry.getKey();
HookParams nextParams = nextEntry.getValue();
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, nextPointcut, nextParams);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, nextPointcut, nextParams);
}
DeferredInterceptorBroadcasts deferredInterceptorBroadcasts = new DeferredInterceptorBroadcasts(deferredBroadcastEvents);
@ -965,7 +965,7 @@ public abstract class BaseTransactionProcessor {
.add(DeferredInterceptorBroadcasts.class, deferredInterceptorBroadcasts)
.add(TransactionDetails.class, theTransactionDetails)
.add(IBaseBundle.class, theResponse);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_TRANSACTION_PROCESSED, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_TRANSACTION_PROCESSED, params);
theTransactionDetails.deferredBroadcastProcessingFinished();

View File

@ -56,7 +56,7 @@ import ca.uhn.fhir.jpa.searchparam.util.Dstu3DistanceHelper;
import ca.uhn.fhir.jpa.searchparam.util.LastNParameterHelper;
import ca.uhn.fhir.jpa.util.BaseIterator;
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.jpa.util.QueryChunker;
import ca.uhn.fhir.jpa.util.ScrollableResultsIterator;
import ca.uhn.fhir.jpa.util.SqlQueryList;
@ -305,7 +305,7 @@ public class LegacySearchBuilder implements ISearchBuilder {
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(SearchRuntimeDetails.class, theSearchRuntimeDetails);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INDEXSEARCH_QUERY_COMPLETE, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INDEXSEARCH_QUERY_COMPLETE, params);
}
if (pids.isEmpty()) {
@ -823,7 +823,7 @@ public class LegacySearchBuilder implements ISearchBuilder {
.add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
allAdded = new HashSet<>(includedPidList);
@ -925,7 +925,7 @@ public class LegacySearchBuilder implements ISearchBuilder {
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, msg);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
addPredicateCompositeStringUnique(theParams, indexString, myRequestPartitionId);
}
@ -1093,8 +1093,8 @@ public class LegacySearchBuilder implements ISearchBuilder {
myStillNeedToFetchIncludes = true;
}
myHavePerfTraceFoundIdHook = JpaInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, myInterceptorBroadcaster, myRequest);
myHaveRawSqlHooks = JpaInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_RAW_SQL, myInterceptorBroadcaster, myRequest);
myHavePerfTraceFoundIdHook = CompositeInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, myInterceptorBroadcaster, myRequest);
myHaveRawSqlHooks = CompositeInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_RAW_SQL, myInterceptorBroadcaster, myRequest);
}
@ -1157,7 +1157,7 @@ public class LegacySearchBuilder implements ISearchBuilder {
HookParams params = new HookParams()
.add(Integer.class, System.identityHashCode(this))
.add(Object.class, nextLong);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, params);
}
if (nextLong != null) {
@ -1184,7 +1184,7 @@ public class LegacySearchBuilder implements ISearchBuilder {
.add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(StorageProcessingMessage.class, message);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_WARNING, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_WARNING, params);
initializeIteratorQuery(null, myMaxResultsToFetch);
}
@ -1226,7 +1226,7 @@ public class LegacySearchBuilder implements ISearchBuilder {
.add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(SqlQueryList.class, capturedQueries);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_RAW_SQL, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_RAW_SQL, params);
}
}
@ -1235,7 +1235,7 @@ public class LegacySearchBuilder implements ISearchBuilder {
.add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED, params);
myFirst = false;
}
@ -1244,7 +1244,7 @@ public class LegacySearchBuilder implements ISearchBuilder {
.add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE, params);
}
}

View File

@ -31,7 +31,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.jpa.util.MemoryCacheService;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
@ -43,8 +43,6 @@ import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import java.util.Collections;
import java.util.Set;
@ -99,14 +97,14 @@ public class MatchResourceUrlService {
Set<ResourcePersistentId> retVal = dao.searchForIds(theParamMap, theRequest);
// Interceptor broadcast: JPA_PERFTRACE_INFO
if (JpaInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequest)) {
if (CompositeInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequest)) {
StorageProcessingMessage message = new StorageProcessingMessage();
message.setMessage("Processed conditional resource URL with " + retVal.size() + " result(s) in " + sw.toString());
HookParams params = new HookParams()
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, message);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
}
return retVal;
}

View File

@ -30,7 +30,7 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.dao.data.IResourceLinkDao;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
@ -87,7 +87,7 @@ public class DeleteExpungeService {
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(String.class, theUrl);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRE_DELETE_EXPUNGE, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRE_DELETE_EXPUNGE, params);
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
txTemplate.executeWithoutResult(t -> validateOkToDeleteAndExpunge(thePids));
@ -172,7 +172,7 @@ public class DeleteExpungeService {
.add(AtomicLong.class, theExpungedEntitiesCount)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRE_DELETE_EXPUNGE_PID_LIST, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRE_DELETE_EXPUNGE_PID_LIST, params);
String pidListString = thePids.toString().replace("[", "(").replace("]", ")");
List<ResourceForeignKey> resourceForeignKeys = myResourceTableFKProvider.getResourceForeignKeys();

View File

@ -64,7 +64,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.StopWatch;
@ -113,7 +113,7 @@ public class ExpungeEverythingService {
.add(AtomicInteger.class, counter)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESTORAGE_EXPUNGE_EVERYTHING, hooks);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESTORAGE_EXPUNGE_EVERYTHING, hooks);
ourLog.info("BEGINNING GLOBAL $expunge");
myTxTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

View File

@ -46,7 +46,7 @@ import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.model.entity.ForcedId;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.jpa.util.MemoryCacheService;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -201,7 +201,7 @@ public class ResourceExpungeService implements IResourceExpungeService {
private void callHooks(RequestDetails theRequestDetails, AtomicInteger theRemainingCount, ResourceHistoryTable theVersion, IdDt theId) {
final AtomicInteger counter = new AtomicInteger();
if (JpaInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, myInterceptorBroadcaster, theRequestDetails)) {
if (CompositeInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, myInterceptorBroadcaster, theRequestDetails)) {
IFhirResourceDao<?> resourceDao = myDaoRegistry.getResourceDao(theId.getResourceType());
IBaseResource resource = resourceDao.toResource(theVersion, false);
HookParams params = new HookParams()
@ -210,7 +210,7 @@ public class ResourceExpungeService implements IResourceExpungeService {
.add(IBaseResource.class, resource)
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, params);
}
theRemainingCount.addAndGet(-1 * counter.get());
}

View File

@ -50,7 +50,7 @@ import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
import ca.uhn.fhir.jpa.searchparam.util.JpaParamUtil;
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
import ca.uhn.fhir.jpa.searchparam.util.SourceParam;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
@ -475,7 +475,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, msg);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_WARNING, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_WARNING, params);
}
Predicate createResourceLinkPathPredicate(String theResourceName, String theParamName, From<?, ? extends ResourceLink> from) {

View File

@ -24,7 +24,7 @@ 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.jpa.api.model.ResourceVersionConflictResolutionStrategy;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
@ -107,7 +107,7 @@ public class HapiTransactionService {
HookParams params = new HookParams()
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
ResourceVersionConflictResolutionStrategy conflictResolutionStrategy = (ResourceVersionConflictResolutionStrategy) JpaInterceptorBroadcaster.doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_VERSION_CONFLICT, params);
ResourceVersionConflictResolutionStrategy conflictResolutionStrategy = (ResourceVersionConflictResolutionStrategy) CompositeInterceptorBroadcaster.doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_VERSION_CONFLICT, params);
if (conflictResolutionStrategy != null && conflictResolutionStrategy.isRetry()) {
maxRetries = conflictResolutionStrategy.getMaxRetries();
}

View File

@ -31,7 +31,7 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.data.IResourceLinkDao;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
@ -121,7 +121,7 @@ public class DeleteConflictService {
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(TransactionDetails.class, theTransactionDetails);
return (DeleteConflictOutcome) JpaInterceptorBroadcaster.doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESTORAGE_DELETE_CONFLICTS, hooks);
return (DeleteConflictOutcome) CompositeInterceptorBroadcaster.doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESTORAGE_DELETE_CONFLICTS, hooks);
}
private void addConflictsToList(DeleteConflictList theDeleteConflicts, ResourceTable theEntity, List<ResourceLink> theResultList) {

View File

@ -32,7 +32,7 @@ import ca.uhn.fhir.jpa.api.model.DeleteConflict;
import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
import ca.uhn.fhir.jpa.delete.DeleteConflictOutcome;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.DeleteCascadeModeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -135,7 +135,7 @@ public class CascadingDeleteInterceptor {
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(DeleteConflictList.class, theConflictList)
.add(IBaseResource.class, resource);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_CASCADE_DELETE, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_CASCADE_DELETE, params);
// Actually perform the delete
ourLog.info("Have delete conflict {} - Cascading delete", next);

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.interceptor.validation;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.jpa.validation.ValidatorResourceFetcher;
import ca.uhn.fhir.rest.server.interceptor.ValidationResultEnrichingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;
@ -54,6 +55,8 @@ public final class RepositoryValidatingRuleBuilder implements IRuleRoot {
private IValidationSupport myValidationSupport;
@Autowired
private ValidatorResourceFetcher myValidatorResourceFetcher;
@Autowired
private IInterceptorBroadcaster myInterceptorBroadcaster;
/**
* Begin a new rule for a specific resource type.
@ -171,7 +174,7 @@ public final class RepositoryValidatingRuleBuilder implements IRuleRoot {
* @see ValidationResultEnrichingInterceptor
*/
public FinalizedRequireValidationRule requireValidationToDeclaredProfiles() {
RequireValidationRule rule = new RequireValidationRule(myFhirContext, myType, myValidationSupport, myValidatorResourceFetcher);
RequireValidationRule rule = new RequireValidationRule(myFhirContext, myType, myValidationSupport, myValidatorResourceFetcher, myInterceptorBroadcaster);
myRules.add(rule);
return new FinalizedRequireValidationRule(rule);
}
@ -248,6 +251,7 @@ public final class RepositoryValidatingRuleBuilder implements IRuleRoot {
}
/**
* Specifies the minimum validation result severity that should cause a rejection. For example, if
* Specifies the minimum validation result severity that should cause a rejection. For example, if
* this is set to <code>ERROR</code> (which is the default), any validation results with a severity
* of <code>ERROR</code> or <code>FATAL</code> will cause the create/update operation to be rejected and

View File

@ -22,6 +22,8 @@ package ca.uhn.fhir.jpa.interceptor.validation;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.jpa.validation.ValidatorResourceFetcher;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.interceptor.ValidationResultEnrichingInterceptor;
@ -43,12 +45,15 @@ import java.util.List;
class RequireValidationRule extends BaseTypedRule {
private final FhirInstanceValidator myValidator;
private final IInterceptorBroadcaster myInterceptorBroadcaster;
private ResultSeverityEnum myRejectOnSeverity = ResultSeverityEnum.ERROR;
private List<TagOnSeverity> myTagOnSeverity = Collections.emptyList();
public RequireValidationRule(FhirContext theFhirContext, String theType, IValidationSupport theValidationSupport, ValidatorResourceFetcher theValidatorResourceFetcher) {
public RequireValidationRule(FhirContext theFhirContext, String theType, IValidationSupport theValidationSupport, ValidatorResourceFetcher theValidatorResourceFetcher, IInterceptorBroadcaster theInterceptorBroadcaster) {
super(theFhirContext, theType);
myInterceptorBroadcaster = theInterceptorBroadcaster;
myValidator = new FhirInstanceValidator(theValidationSupport);
myValidator.setValidatorResourceFetcher(theValidatorResourceFetcher);
myValidator.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Warning);
@ -63,6 +68,7 @@ class RequireValidationRule extends BaseTypedRule {
public RuleEvaluation evaluate(RequestDetails theRequestDetails, @Nonnull IBaseResource theResource) {
FhirValidator validator = getFhirContext().newValidator();
validator.setInterceptorBraodcaster(CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails));
validator.registerValidatorModule(myValidator);
ValidationResult outcome = validator.validateWithResult(theResource);

View File

@ -46,9 +46,9 @@ import java.util.List;
import java.util.Objects;
import static ca.uhn.fhir.jpa.model.util.JpaConstants.ALL_PARTITIONS_NAME;
import static ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster.doCallHooks;
import static ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster.doCallHooksAndReturnObject;
import static ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster.hasHooks;
import static ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster.doCallHooks;
import static ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster.doCallHooksAndReturnObject;
import static ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster.hasHooks;
import static org.slf4j.LoggerFactory.getLogger;
public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc {

View File

@ -41,7 +41,7 @@ import ca.uhn.fhir.jpa.partition.RequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.search.cache.ISearchCacheSvc;
import ca.uhn.fhir.jpa.search.cache.SearchCacheStatusEnum;
import ca.uhn.fhir.jpa.util.InterceptorUtil;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.jpa.util.MemoryCacheService;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
@ -171,7 +171,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
.add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
for (int i = retVal.size() - 1; i >= 0; i--) {
if (accessDetails.isDontReturnResourceAtIndex(i)) {
@ -187,7 +187,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
.add(IPreResourceShowDetails.class, showDetails)
.add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
retVal = showDetails.toList();
}

View File

@ -45,7 +45,7 @@ import ca.uhn.fhir.jpa.search.cache.ISearchResultCacheSvc;
import ca.uhn.fhir.jpa.search.cache.SearchCacheStatusEnum;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.InterceptorUtil;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.CacheControlDirective;
@ -316,7 +316,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(SearchParameterMap.class, theParams);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRESEARCH_REGISTERED, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRESEARCH_REGISTERED, params);
Class<? extends IBaseResource> resourceTypeClass = myContext.getResourceDefinition(theResourceType).getImplementingClass();
final ISearchBuilder sb = mySearchBuilderFactory.newSearchBuilder(theCallingDao, theResourceType, resourceTypeClass);
sb.setFetchSize(mySyncSize);
@ -400,7 +400,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
// .add(RequestDetails.class, theRequestDetails)
// .addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
// .add(SearchParameterMap.class, theParams);
// JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRESEARCH_REGISTERED, params);
// CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRESEARCH_REGISTERED, params);
SearchTask task = new SearchTask(theSearch, theCallingDao, theParams, theResourceType, theRequestDetails, theRequestPartitionId);
myIdToSearchTask.put(theSearch.getUuid(), task);
@ -424,7 +424,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
.add(SearchParameterMap.class, theParams)
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
Object outcome = JpaInterceptorBroadcaster.doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH, params);
Object outcome = CompositeInterceptorBroadcaster.doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH, params);
if (Boolean.FALSE.equals(outcome)) {
return null;
}
@ -441,7 +441,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
.add(SearchParameterMap.class, theParams)
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_SEARCH_REUSING_CACHED, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_SEARCH_REUSING_CACHED, params);
return myPersistedJpaBundleProviderFactory.newInstance(theRequestDetails, searchToUse.getUuid());
});
@ -515,7 +515,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
.add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
for (int i = pids.size() - 1; i >= 0; i--) {
if (accessDetails.isDontReturnResourceAtIndex(i)) {
@ -854,7 +854,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
.add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, mySearchRuntimeDetails.getRequestDetails())
.addIfMatchesType(ServletRequestDetails.class, mySearchRuntimeDetails.getRequestDetails());
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
for (int i = unsyncedPids.size() - 1; i >= 0; i--) {
if (accessDetails.isDontReturnResourceAtIndex(i)) {
@ -974,13 +974,13 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
.add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_COMPLETE, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_COMPLETE, params);
} else {
HookParams params = new HookParams()
.add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_PASS_COMPLETE, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_PASS_COMPLETE, params);
}
ourLog.trace("Have completed search for [{}{}] and found {} resources in {}ms - Status is {}", mySearch.getResourceType(), mySearch.getSearchQueryString(), mySyncedPids.size(), sw.getMillis(), mySearch.getStatus());
@ -1028,7 +1028,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
.add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FAILED, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FAILED, params);
saveSearch();
span.captureException(t);

View File

@ -63,7 +63,7 @@ import ca.uhn.fhir.jpa.searchparam.util.LastNParameterHelper;
import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
import ca.uhn.fhir.jpa.util.BaseIterator;
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.jpa.util.QueryChunker;
import ca.uhn.fhir.jpa.util.SqlQueryList;
import ca.uhn.fhir.model.api.IQueryParameterType;
@ -331,7 +331,7 @@ public class SearchBuilder implements ISearchBuilder {
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(SearchRuntimeDetails.class, theSearchRuntimeDetails);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INDEXSEARCH_QUERY_COMPLETE, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INDEXSEARCH_QUERY_COMPLETE, params);
}
if (pids.isEmpty()) {
@ -952,14 +952,14 @@ public class SearchBuilder implements ISearchBuilder {
// the user has a chance to know that they were in the results
if (allAdded.size() > 0) {
if (JpaInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES, myInterceptorBroadcaster, theRequest)) {
if (CompositeInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES, myInterceptorBroadcaster, theRequest)) {
List<ResourcePersistentId> includedPidList = new ArrayList<>(allAdded);
JpaPreResourceAccessDetails accessDetails = new JpaPreResourceAccessDetails(includedPidList, () -> this);
HookParams params = new HookParams()
.add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
for (int i = includedPidList.size() - 1; i >= 0; i--) {
if (accessDetails.isDontReturnResourceAtIndex(i)) {
@ -1060,7 +1060,7 @@ public class SearchBuilder implements ISearchBuilder {
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, msg);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
theQueryStack3.addPredicateCompositeUnique(indexString, myRequestPartitionId);
@ -1182,8 +1182,8 @@ public class SearchBuilder implements ISearchBuilder {
myStillNeedToFetchIncludes = true;
}
myHavePerfTraceFoundIdHook = JpaInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, myInterceptorBroadcaster, myRequest);
myHaveRawSqlHooks = JpaInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_RAW_SQL, myInterceptorBroadcaster, myRequest);
myHavePerfTraceFoundIdHook = CompositeInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, myInterceptorBroadcaster, myRequest);
myHaveRawSqlHooks = CompositeInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_RAW_SQL, myInterceptorBroadcaster, myRequest);
}
@ -1239,7 +1239,7 @@ public class SearchBuilder implements ISearchBuilder {
HookParams params = new HookParams()
.add(Integer.class, System.identityHashCode(this))
.add(Object.class, nextLong);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, params);
}
if (nextLong != null) {
@ -1266,7 +1266,7 @@ public class SearchBuilder implements ISearchBuilder {
.add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(StorageProcessingMessage.class, message);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_WARNING, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_WARNING, params);
initializeIteratorQuery(myOffset, myMaxResultsToFetch);
}
@ -1308,7 +1308,7 @@ public class SearchBuilder implements ISearchBuilder {
.add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(SqlQueryList.class, capturedQueries);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_RAW_SQL, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_RAW_SQL, params);
}
}
@ -1317,7 +1317,7 @@ public class SearchBuilder implements ISearchBuilder {
.add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED, params);
myFirst = false;
}
@ -1326,7 +1326,7 @@ public class SearchBuilder implements ISearchBuilder {
.add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE, params);
}
}

View File

@ -46,7 +46,7 @@ import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
import ca.uhn.fhir.jpa.searchparam.util.JpaParamUtil;
import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.DataFormatException;
@ -291,7 +291,7 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder {
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, msg);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_WARNING, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_WARNING, params);
}

View File

@ -25,11 +25,10 @@ import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.UriParam;
@ -111,7 +110,7 @@ public class UriPredicateBuilder extends BaseSearchParamPredicateBuilder {
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(StorageProcessingMessage.class, message);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_WARNING, params);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_WARNING, params);
Collection<String> candidates = myResourceIndexedSearchParamUriDao.findAllByResourceTypeAndParamName(getResourceType(), theParamName);
List<String> toFind = new ArrayList<>();

View File

@ -27,6 +27,7 @@ import ca.uhn.fhir.rest.api.server.IPreResourceShowDetails;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.SimplePreResourceShowDetails;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.util.List;
@ -51,7 +52,7 @@ public class InterceptorUtil {
.add(IPreResourceShowDetails.class, accessDetails)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
JpaInterceptorBroadcaster.doCallHooks(theInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
CompositeInterceptorBroadcaster.doCallHooks(theInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
retVal = accessDetails.toList();
retVal.removeIf(t -> t == null);

View File

@ -573,6 +573,10 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
return dao.update(theResource, mySrd).getId().toUnqualifiedVersionless();
}
protected String encode(IBaseResource theResource) {
return myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theResource);
}
@Override
public FhirContext getFhirContext() {
return myFhirCtx;

View File

@ -1142,11 +1142,6 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
}
private String encode(IBaseResource theResource) {
return myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theResource);
}
private OperationOutcome doTestValidateResourceContainingProfileDeclaration(String methodName, EncodingEnum enc) throws IOException {
Bundle vss = loadResourceFromClasspath(Bundle.class, "/org/hl7/fhir/r4/model/valueset/valuesets.xml");
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-status"), mySrd);

View File

@ -0,0 +1,158 @@
package ca.uhn.fhir.jpa.interceptor.validation;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.config.BaseConfig;
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.validation.ValidationMessageSuppressingInterceptor;
import ca.uhn.fhir.validation.IValidatorModule;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Encounter;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.matchesPattern;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
public class ValidationMessageSuppressingInterceptorTest extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidationMessageSuppressingInterceptorTest.class);
@Autowired
private ApplicationContext myApplicationContext;
@Autowired
private IValidationSupport myValidationSupport;
@Override
@AfterEach
public void after() throws Exception {
super.after();
myInterceptorRegistry.unregisterInterceptorsIf(t -> t instanceof RepositoryValidatingInterceptor);
myInterceptorRegistry.unregisterInterceptorsIf(t -> t instanceof ValidationMessageSuppressingInterceptor);
}
@Test
public void testDaoValidation() throws IOException {
upload("/r4/uscore/CodeSystem-dummy-loinc.json");
upload("/r4/uscore/StructureDefinition-us-core-pulse-oximetry.json");
String input = loadResource("/r4/uscore/observation-pulseox.json");
Observation inputObs = loadResource(myFhirCtx, Observation.class, "/r4/uscore/observation-pulseox.json");
try {
myObservationDao.validate(inputObs, null, input, null, null, null, null);
fail();
} catch (PreconditionFailedException e) {
// good
}
ValidationMessageSuppressingInterceptor interceptor = new ValidationMessageSuppressingInterceptor();
interceptor.addMessageSuppressionPatterns("Unknown code 'http://loinc.org#59408-5'");
myInterceptorRegistry.registerInterceptor(interceptor);
MethodOutcome validationOutcome = myObservationDao.validate(inputObs, null, input, null, null, null, null);
OperationOutcome oo = (OperationOutcome) validationOutcome.getOperationOutcome();
String encode = encode(oo);
ourLog.info(encode);
assertThat(encode, containsString("All observations should have a performer"));
}
@Test
public void testRequestValidatingInterceptor() throws IOException {
createPatient(withActiveTrue(), withId("AmyBaxter"));
upload("/r4/uscore/CodeSystem-dummy-loinc.json");
upload("/r4/uscore/StructureDefinition-us-core-pulse-oximetry.json");
RequestValidatingInterceptor requestInterceptor = new RequestValidatingInterceptor();
requestInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
requestInterceptor.setValidatorModules(Collections.singletonList(new FhirInstanceValidator(myValidationSupport)));
requestInterceptor.setInterceptorBroadcaster(myInterceptorRegistry);
ourRestServer.registerInterceptor(requestInterceptor);
// Without suppression
{
Observation inputObs = loadResource(myFhirCtx, Observation.class, "/r4/uscore/observation-pulseox.json");
try {
myClient.create().resource(inputObs).execute().getId().toUnqualifiedVersionless().getValue();
fail();
} catch (UnprocessableEntityException e) {
String encode = encode(e.getOperationOutcome());
ourLog.info(encode);
assertThat(encode, containsString("Unknown code 'http://loinc.org#59408-5'"));
}
}
// With suppression
ValidationMessageSuppressingInterceptor interceptor = new ValidationMessageSuppressingInterceptor();
interceptor.addMessageSuppressionPatterns("Unknown code 'http://loinc.org#59408-5'");
myInterceptorRegistry.registerInterceptor(interceptor);
{
Observation inputObs = loadResource(myFhirCtx, Observation.class, "/r4/uscore/observation-pulseox.json");
String id = myClient.create().resource(inputObs).execute().getId().toUnqualifiedVersionless().getValue();
assertThat(id, matchesPattern("Observation/[0-9]+"));
}
}
@Test
public void testRepositoryValidation() {
createPatient(withActiveTrue(), withId("A"));
List<IRepositoryValidatingRule> rules = myApplicationContext.getBean(BaseConfig.REPOSITORY_VALIDATING_RULE_BUILDER, RepositoryValidatingRuleBuilder.class)
.forResourcesOfType("Encounter")
.requireValidationToDeclaredProfiles().withBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Ignore)
.build();
RepositoryValidatingInterceptor repositoryValidatingInterceptor = new RepositoryValidatingInterceptor();
repositoryValidatingInterceptor.setFhirContext(myFhirCtx);
repositoryValidatingInterceptor.setRules(rules);
myInterceptorRegistry.registerInterceptor(repositoryValidatingInterceptor);
// Without suppression
try {
Encounter encounter = new Encounter();
encounter.setSubject(new Reference("Patient/A"));
IIdType id = myEncounterDao.create(encounter).getId();
assertEquals("1", id.getVersionIdPart());
fail();
} catch (PreconditionFailedException e) {
String encode = encode(e.getOperationOutcome());
ourLog.info(encode);
assertThat(encode, containsString("Encounter.status: minimum required = 1"));
}
// With suppression
ValidationMessageSuppressingInterceptor interceptor = new ValidationMessageSuppressingInterceptor();
interceptor.addMessageSuppressionPatterns("Encounter.status");
interceptor.addMessageSuppressionPatterns("Encounter.class");
myInterceptorRegistry.registerInterceptor(interceptor);
Encounter encounter = new Encounter();
encounter.setSubject(new Reference("Patient/A"));
IIdType id = myEncounterDao.create(encounter).getId().toUnqualifiedVersionless();
assertThat(id.getValue(), matchesPattern("Encounter/[0-9]+"));
}
}

View File

@ -0,0 +1,33 @@
{
"resourceType": "CodeSystem",
"id": "loinc",
"url": "http://loinc.org",
"version": "20160128",
"name": "LOINC",
"title": "ACME Codes for Cholesterol in Serum/Plasma",
"status": "active",
"experimental": true,
"date": "2016-01-28",
"publisher": "Acme Co",
"description": "This is an example code system that includes all the ACME codes for serum/plasma cholesterol from v2.36.",
"caseSensitive": true,
"content": "complete",
"concept": [
{
"code" : "59408-5",
"display" : "Oxygen saturation in Arterial blood by Pulse oximetry"
},
{
"code" : "3151-8",
"display" : "Inhaled oxygen flow rate"
},
{
"code": "2708-6",
"display": "Oxygen saturation in Arterial blood"
},
{
"code": "3150-0",
"display": "Inhaled oxygen concentration"
}
]
}

View File

@ -0,0 +1,92 @@
{
"resourceType": "Observation",
"id": "satO2-fiO2",
"meta": {
"profile": [
"http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry"
]
},
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative</b></p><p><b>id</b>: satO2-fiO2</p><p><b>meta</b>: </p><p><b>identifier</b>: o1223435-10</p><p></p><p><b>category</b>: <span title=\"Codes: {http://terminology.hl7.org/CodeSystem/observation-category vital-signs}\">Vital Signs</span></p><p><b>code</b>: <span title=\"Codes: {http://loinc.org 2708-6}, {http://loinc.org 59408-5}, {urn:iso:std:iso:11073:10101 150456}\">Oxygen saturation in Arterial blood</span></p><p><b>subject</b>: <a href=\"Patient-example.html\">Generated Summary: id: example; Medical Record Number = 1032702 (USUAL); active; Amy V. Shaw , Amy V. Baxter ; ph: 555-555-5555(HOME), amy.shaw@example.com; gender: female; birthDate: 1987-02-20</a></p><p><b>effective</b>: Dec 5, 2014, 8:30:10 AM</p><p><b>value</b>: 95 %</p><p><b>interpretation</b>: <span title=\"Codes: {http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation N}\">Normal (applies to non-numeric results)</span></p><p><b>device</b>: <span>Acme Pulse Oximeter 2000</span></p><h3>ReferenceRanges</h3><table class=\"grid\"><tr><td>-</td><td><b>Low</b></td><td><b>High</b></td></tr><tr><td>*</td><td>90 %</td><td>99 %</td></tr></table><h3>Components</h3><table class=\"grid\"><tr><td>-</td><td><b>Code</b></td><td><b>Value[x]</b></td></tr><tr><td>*</td><td><span title=\"Codes: {http://loinc.org 3151-8}\">Inhaled oxygen flow rate</span></td><td>6 liters/min</td></tr></table></div>"
},
"identifier": [
{
"system": "http://goodcare.org/observation/id",
"value": "o1223435-10"
}
],
"status": "final",
"category": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/observation-category",
"code": "vital-signs",
"display": "Vital Signs"
}
],
"text": "Vital Signs"
}
],
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "2708-6",
"display": "Oxygen saturation in Arterial blood"
},
{
"system": "http://loinc.org",
"code": "59408-5",
"display": "Oxygen saturation in Arterial blood by Pulse oximetry"
},
{
"system": "urn:iso:std:iso:11073:10101",
"code": "150456",
"display": "MDC_PULS_OXIM_SAT_O2"
}
]
},
"subject": {
"reference": "Patient/AmyBaxter"
},
"effectiveDateTime": "2014-12-05T09:30:10+01:00",
"valueQuantity": {
"value": 95,
"unit": "%",
"system": "http://unitsofmeasure.org",
"code": "%"
},
"interpretation": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
"code": "N",
"display": "Normal"
}
],
"text": "Normal (applies to non-numeric results)"
}
],
"device": {
"display": "Acme Pulse Oximeter 2000"
},
"referenceRange": [
{
"low": {
"value": 90,
"unit": "%",
"system": "http://unitsofmeasure.org",
"code": "%"
},
"high": {
"value": 99,
"unit": "%",
"system": "http://unitsofmeasure.org",
"code": "%"
}
}
]
}

View File

@ -1,79 +0,0 @@
package ca.uhn.fhir.jpa.util;
/*-
* #%L
* HAPI FHIR JPA Model
* %%
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import javax.annotation.Nullable;
public class JpaInterceptorBroadcaster {
/**
* Non instantiable
*/
private JpaInterceptorBroadcaster() {
// nothing
}
/**
* Broadcast hooks to both the interceptor service associated with the request, as well
* as the one associated with the JPA module.
*/
public static boolean doCallHooks(IInterceptorBroadcaster theInterceptorBroadcaster, @Nullable RequestDetails theRequestDetails, Pointcut thePointcut, HookParams theParams) {
boolean retVal = true;
if (theInterceptorBroadcaster != null) {
retVal = theInterceptorBroadcaster.callHooks(thePointcut, theParams);
}
if (theRequestDetails != null && theRequestDetails.getInterceptorBroadcaster() != null && retVal) {
IInterceptorBroadcaster interceptorBroadcaster = theRequestDetails.getInterceptorBroadcaster();
interceptorBroadcaster.callHooks(thePointcut, theParams);
}
return retVal;
}
/**
* Broadcast hooks to both the interceptor service associated with the request, as well
* as the one associated with the JPA module.
*/
public static Object doCallHooksAndReturnObject(IInterceptorBroadcaster theInterceptorBroadcaster, RequestDetails theRequestDetails, Pointcut thePointcut, HookParams theParams) {
Object retVal = true;
if (theInterceptorBroadcaster != null) {
retVal = theInterceptorBroadcaster.callHooksAndReturnObject(thePointcut, theParams);
}
if (theRequestDetails != null && theRequestDetails.getInterceptorBroadcaster() != null && retVal == null) {
IInterceptorBroadcaster interceptorBroadcaster = theRequestDetails.getInterceptorBroadcaster();
retVal = interceptorBroadcaster.callHooksAndReturnObject(thePointcut, theParams);
}
return retVal;
}
public static boolean hasHooks(Pointcut thePointcut, IInterceptorBroadcaster theInterceptorBroadcaster, RequestDetails theRequestDetails) {
if (theInterceptorBroadcaster != null && theInterceptorBroadcaster.hasHooks(thePointcut)) {
return true;
}
return theRequestDetails != null &&
theRequestDetails.getInterceptorBroadcaster() != null &&
theRequestDetails.getInterceptorBroadcaster().hasHooks(thePointcut);
}
}

View File

@ -44,7 +44,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
@ -469,8 +469,8 @@ public class SearchParamExtractorService {
}
@VisibleForTesting
void setInterceptorBroadcasterForUnitTest(IInterceptorBroadcaster theJpaInterceptorBroadcaster) {
myInterceptorBroadcaster = theJpaInterceptorBroadcaster;
void setInterceptorBroadcasterForUnitTest(IInterceptorBroadcaster theInterceptorBroadcaster) {
myInterceptorBroadcaster = theInterceptorBroadcaster;
}
@Nonnull
@ -491,7 +491,7 @@ public class SearchParamExtractorService {
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(StorageProcessingMessage.class, messageHolder);
JpaInterceptorBroadcaster.doCallHooks(theInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_WARNING, params);
CompositeInterceptorBroadcaster.doCallHooks(theInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_WARNING, params);
}
}
}

View File

@ -12,7 +12,7 @@ import ca.uhn.fhir.jpa.subscription.match.matcher.matching.IResourceModifiedCons
import ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionMatchingSubscriber;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.Validate;
@ -100,7 +100,7 @@ public class SubscriptionMatcherInterceptor implements IResourceModifiedConsumer
// Interceptor call: SUBSCRIPTION_RESOURCE_MODIFIED
HookParams params = new HookParams()
.add(ResourceModifiedMessage.class, msg);
boolean outcome = JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.SUBSCRIPTION_RESOURCE_MODIFIED, params);
boolean outcome = CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.SUBSCRIPTION_RESOURCE_MODIFIED, params);
if (!outcome) {
return;
}

View File

@ -21,18 +21,23 @@ package ca.uhn.fhir.rest.server.interceptor;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.util.OperationOutcomeUtil;
import ca.uhn.fhir.validation.*;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.text.StrLookup;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
@ -55,7 +60,7 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
*/
public static final String DEFAULT_RESPONSE_HEADER_VALUE = "${row}:${col} ${severity} ${message} (${location})";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseValidatingInterceptor.class);
private static final Logger ourLog = LoggerFactory.getLogger(BaseValidatingInterceptor.class);
private Integer myAddResponseIssueHeaderOnSeverity = null;
private Integer myAddResponseOutcomeHeaderOnSeverity = null;
@ -68,6 +73,7 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
private String myResponseOutcomeHeaderName = provideDefaultResponseHeaderName();
private List<IValidatorModule> myValidatorModules;
private IInterceptorBroadcaster myInterceptorBroadcaster;
private void addResponseIssueHeader(RequestDetails theRequestDetails, SingleValidationMessage theNext) {
// Perform any string substitutions from the message format
@ -279,6 +285,10 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
*/
protected ValidationResult validate(T theRequest, RequestDetails theRequestDetails) {
FhirValidator validator = theRequestDetails.getServer().getFhirContext().newValidator();
IInterceptorBroadcaster interceptorBroadcaster = CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
validator.setInterceptorBraodcaster(interceptorBroadcaster);
if (myValidatorModules != null) {
for (IValidatorModule next : myValidatorModules) {
validator.registerValidatorModule(next);
@ -357,6 +367,14 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
return validationResult;
}
/**
* This can be used to specify an interceptor to broadcast validation events through. This
* is mostly intended for the
*/
public void setInterceptorBroadcaster(IInterceptorBroadcaster theInterceptorBroadcaster) {
myInterceptorBroadcaster = theInterceptorBroadcaster;
}
private static class MyLookup extends StrLookup<String> {
private SingleValidationMessage myMessage;

View File

@ -0,0 +1,88 @@
package ca.uhn.fhir.rest.server.interceptor.validation;
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.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@Interceptor
public class ValidationMessageSuppressingInterceptor {
private List<Pattern> mySuppressPatterns = new ArrayList<>();
/**
* Constructor
*/
public ValidationMessageSuppressingInterceptor() {
super();
}
/**
* Supplies one or more patterns to suppress. Any validation messages (of any severity) will be suppressed
* if they match this pattern. Patterns are in Java Regular Expression format (as defined by the {@link Pattern} class)
* and are treated as partial maches. They are also case insensitive.
* <p>
* For example, a pattern of <code>loinc.*1234</code> would suppress the following message:<br/>
* <code>The LOINC code 1234 is not valid</code>
* </p>
*/
public ValidationMessageSuppressingInterceptor addMessageSuppressionPatterns(String... thePatterns) {
return addMessageSuppressionPatterns(Arrays.asList(thePatterns));
}
/**
* Supplies one or more patterns to suppress. Any validation messages (of any severity) will be suppressed
* if they match this pattern. Patterns are in Java Regular Expression format (as defined by the {@link Pattern} class)
* and are treated as partial maches. They are also case insensitive.
* <p>
* For example, a pattern of <code>loinc.*1234</code> would suppress the following message:<br/>
* <code>The LOINC code 1234 is not valid</code>
* </p>
*/
public ValidationMessageSuppressingInterceptor addMessageSuppressionPatterns(List<String> thePatterns) {
for (String next : thePatterns) {
if (isNotBlank(next)) {
Pattern pattern = Pattern.compile(next, Pattern.CASE_INSENSITIVE);
mySuppressPatterns.add(pattern);
}
}
return this;
}
@Hook(Pointcut.VALIDATION_COMPLETED)
public ValidationResult handle(ValidationResult theResult) {
List<SingleValidationMessage> newMessages = new ArrayList<>(theResult.getMessages().size());
for (SingleValidationMessage next : theResult.getMessages()) {
String nextMessage = next.getMessage();
boolean suppress = false;
for (Pattern nextSuppressPattern : mySuppressPatterns) {
if (nextSuppressPattern.matcher(nextMessage).find()) {
suppress = true;
break;
}
}
if (!suppress) {
newMessages.add(next);
}
}
if (newMessages.size() == theResult.getMessages().size()) {
return null;
}
return new ValidationResult(theResult.getContext(), newMessages);
}
}

View File

@ -0,0 +1,101 @@
package ca.uhn.fhir.rest.server.util;
/*-
* #%L
* HAPI FHIR JPA Model
* %%
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import javax.annotation.Nullable;
public class CompositeInterceptorBroadcaster {
/**
* Non instantiable
*/
private CompositeInterceptorBroadcaster() {
// nothing
}
/**
* Broadcast hooks to both the interceptor service associated with the request, as well
* as the one associated with the JPA module.
*/
public static boolean doCallHooks(IInterceptorBroadcaster theInterceptorBroadcaster, @Nullable RequestDetails theRequestDetails, Pointcut thePointcut, HookParams theParams) {
return newCompositeBroadcaster(theInterceptorBroadcaster, theRequestDetails).callHooks(thePointcut, theParams);
}
/**
* Broadcast hooks to both the interceptor service associated with the request, as well
* as the one associated with the JPA module.
*/
public static Object doCallHooksAndReturnObject(IInterceptorBroadcaster theInterceptorBroadcaster, RequestDetails theRequestDetails, Pointcut thePointcut, HookParams theParams) {
return newCompositeBroadcaster(theInterceptorBroadcaster, theRequestDetails).callHooksAndReturnObject(thePointcut, theParams);
}
public static boolean hasHooks(Pointcut thePointcut, IInterceptorBroadcaster theInterceptorBroadcaster, RequestDetails theRequestDetails) {
return newCompositeBroadcaster(theInterceptorBroadcaster, theRequestDetails).hasHooks(thePointcut);
}
/**
* @since 5.5.0
*/
public static IInterceptorBroadcaster newCompositeBroadcaster(IInterceptorBroadcaster theInterceptorBroadcaster, RequestDetails theRequestDetails) {
return new IInterceptorBroadcaster() {
@Override
public boolean callHooks(Pointcut thePointcut, HookParams theParams) {
boolean retVal = true;
if (theInterceptorBroadcaster != null) {
retVal = theInterceptorBroadcaster.callHooks(thePointcut, theParams);
}
if (theRequestDetails != null && theRequestDetails.getInterceptorBroadcaster() != null && retVal) {
IInterceptorBroadcaster interceptorBroadcaster = theRequestDetails.getInterceptorBroadcaster();
interceptorBroadcaster.callHooks(thePointcut, theParams);
}
return retVal;
}
@Override
public Object callHooksAndReturnObject(Pointcut thePointcut, HookParams theParams) {
Object retVal = true;
if (theInterceptorBroadcaster != null) {
retVal = theInterceptorBroadcaster.callHooksAndReturnObject(thePointcut, theParams);
}
if (theRequestDetails != null && theRequestDetails.getInterceptorBroadcaster() != null && retVal == null) {
IInterceptorBroadcaster interceptorBroadcaster = theRequestDetails.getInterceptorBroadcaster();
retVal = interceptorBroadcaster.callHooksAndReturnObject(thePointcut, theParams);
}
return retVal;
}
@Override
public boolean hasHooks(Pointcut thePointcut) {
if (theInterceptorBroadcaster != null && theInterceptorBroadcaster.hasHooks(thePointcut)) {
return true;
}
return theRequestDetails != null &&
theRequestDetails.getInterceptorBroadcaster() != null &&
theRequestDetails.getInterceptorBroadcaster().hasHooks(thePointcut);
}
};
}
}