Compare commits

...

9 Commits

Author SHA1 Message Date
Mangala Ekanayake b223410940
Merge a43d12e042 into 061390d76b 2024-11-27 08:24:34 -05:00
James Agnew 061390d76b
Add composite interceptor registry (#6511)
* Composite interceptor improvements

* Add composite interceptor registry

* Add changelog

* Composite Interceptor Broadcaster Improvements

* Fix compile error

* Update hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_8_0/6511-rework-composite-interceptor-broadcaster.yaml

Co-authored-by: Tadgh <garygrantgraham@gmail.com>

* Address review comments

* Test fixes

* Test fix

* Test fix

---------

Co-authored-by: Tadgh <garygrantgraham@gmail.com>
2024-11-27 07:14:48 -05:00
Michael Buckley 3b8569127e
Start removing dependency from FhirVersionEnum to FhirContext (#6512)
Deprecate path from FhirVersionEnum to FhirContext and replace usages.
2024-11-26 13:46:05 -05:00
mangala.ekanayake a43d12e042 [6463] Fix ValidateWithRemoteTerminologyTest 2024-11-25 21:05:13 +05:30
mangala.ekanayake 0c656f072a [6463] re-add removed comments 2024-11-25 19:38:06 +05:30
mangala.ekanayake 7d4a243c5e [6463] unit tests for JpaPersistedResourceValidationSupport when fetch StructureDefinitions 2024-11-25 19:38:06 +05:30
mangala.ekanayake 883281afee [6463] resource url can have only one | character 2024-11-25 19:38:05 +05:30
Mangala Ekanayake 1d0b18d989 Update VersionSpecificWorkerContextWrapper.java
[6463] version is required in url of resource to identify it
2024-11-25 19:38:05 +05:30
Mangala Ekanayake 674f4637a9 [6463] use version in canonical url of StructureDefinition to identify it 2024-11-25 19:38:05 +05:30
72 changed files with 1193 additions and 733 deletions

View File

@ -1293,7 +1293,15 @@ public class FhirContext {
* @since 5.1.0 * @since 5.1.0
*/ */
public static FhirContext forCached(FhirVersionEnum theFhirVersionEnum) { public static FhirContext forCached(FhirVersionEnum theFhirVersionEnum) {
return ourStaticContexts.computeIfAbsent(theFhirVersionEnum, v -> new FhirContext(v)); return ourStaticContexts.computeIfAbsent(theFhirVersionEnum, FhirContext::forVersion);
}
/**
* An uncached version of forCached()
* @return a new FhirContext for theFhirVersionEnum
*/
public static FhirContext forVersion(FhirVersionEnum theFhirVersionEnum) {
return new FhirContext(theFhirVersionEnum);
} }
private static Collection<Class<? extends IBaseResource>> toCollection( private static Collection<Class<? extends IBaseResource>> toCollection(

View File

@ -135,15 +135,19 @@ public enum FhirVersionEnum {
/** /**
* Creates a new FhirContext for this FHIR version * Creates a new FhirContext for this FHIR version
* @deprecated since 7.7. Use {@link FhirContext#forVersion(FhirVersionEnum)} instead
*/ */
@Deprecated(forRemoval = true, since = "7.7")
public FhirContext newContext() { public FhirContext newContext() {
return new FhirContext(this); return FhirContext.forVersion(this);
} }
/** /**
* Creates a new FhirContext for this FHIR version, or returns a previously created one if one exists. This * Creates a new FhirContext for this FHIR version, or returns a previously created one if one exists. This
* method uses {@link FhirContext#forCached(FhirVersionEnum)} to return a cached instance. * method uses {@link FhirContext#forCached(FhirVersionEnum)} to return a cached instance.
* @deprecated since 7.7. Use {@link FhirContext#forCached(FhirVersionEnum)} instead
*/ */
@Deprecated(forRemoval = true, since = "7.7")
public FhirContext newContextCached() { public FhirContext newContextCached() {
return FhirContext.forCached(this); return FhirContext.forCached(this);
} }

View File

@ -46,7 +46,11 @@ public @interface Hook {
* and allowable values can be positive or negative or 0. * and allowable values can be positive or negative or 0.
* <p> * <p>
* If no order is specified, or the order is set to <code>0</code> (the default order), * If no order is specified, or the order is set to <code>0</code> (the default order),
* the order specified at the interceptor type level will take precedence. * the order specified at the {@link Interceptor#order() interceptor type level} will be used.
* </p>
* <p>
* Note that if two hook methods have the same order, then the order of execution is undefined. If
* order is important, then an order must always be explicitly stated.
* </p> * </p>
*/ */
int order() default Interceptor.DEFAULT_ORDER; int order() default Interceptor.DEFAULT_ORDER;

View File

@ -19,6 +19,7 @@
*/ */
package ca.uhn.fhir.interceptor.api; package ca.uhn.fhir.interceptor.api;
import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
public interface IBaseInterceptorBroadcaster<POINTCUT extends IPointcut> { public interface IBaseInterceptorBroadcaster<POINTCUT extends IPointcut> {
@ -73,4 +74,15 @@ public interface IBaseInterceptorBroadcaster<POINTCUT extends IPointcut> {
* @since 4.0.0 * @since 4.0.0
*/ */
boolean hasHooks(POINTCUT thePointcut); boolean hasHooks(POINTCUT thePointcut);
List<IInvoker> getInvokersForPointcut(POINTCUT thePointcut);
interface IInvoker extends Comparable<IInvoker> {
Object invoke(HookParams theParams);
int getOrder();
Object getInterceptor();
}
} }

View File

@ -27,6 +27,8 @@ public interface IPointcut {
@Nonnull @Nonnull
Class<?> getReturnType(); Class<?> getReturnType();
Class<?> getBooleanReturnTypeForEnum();
@Nonnull @Nonnull
List<String> getParameterTypes(); List<String> getParameterTypes();

View File

@ -26,6 +26,7 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.validation.ValidationResult; import ca.uhn.fhir.validation.ValidationResult;
import jakarta.annotation.Nonnull; import jakarta.annotation.Nonnull;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseConformance; import org.hl7.fhir.instance.model.api.IBaseConformance;
import java.io.Writer; import java.io.Writer;
@ -3107,6 +3108,10 @@ public enum Pointcut implements IPointcut {
@Nonnull Class<?> theReturnType, @Nonnull Class<?> theReturnType,
@Nonnull ExceptionHandlingSpec theExceptionHandlingSpec, @Nonnull ExceptionHandlingSpec theExceptionHandlingSpec,
String... theParameterTypes) { String... theParameterTypes) {
// This enum uses the lowercase-b boolean type to indicate boolean return pointcuts
Validate.isTrue(!theReturnType.equals(Boolean.class), "Return type Boolean not allowed here, must be boolean");
myReturnType = theReturnType; myReturnType = theReturnType;
myExceptionHandlingSpec = theExceptionHandlingSpec; myExceptionHandlingSpec = theExceptionHandlingSpec;
myParameterTypes = Collections.unmodifiableList(Arrays.asList(theParameterTypes)); myParameterTypes = Collections.unmodifiableList(Arrays.asList(theParameterTypes));
@ -3132,6 +3137,11 @@ public enum Pointcut implements IPointcut {
return myReturnType; return myReturnType;
} }
@Override
public Class<?> getBooleanReturnTypeForEnum() {
return boolean.class;
}
@Override @Override
@Nonnull @Nonnull
public List<String> getParameterTypes() { public List<String> getParameterTypes() {

View File

@ -20,6 +20,7 @@
package ca.uhn.fhir.interceptor.executor; package ca.uhn.fhir.interceptor.executor;
import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IBaseInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.IBaseInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.IBaseInterceptorService; import ca.uhn.fhir.interceptor.api.IBaseInterceptorService;
@ -57,12 +58,13 @@ import java.util.HashMap;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & IPointcut> public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & IPointcut>
implements IBaseInterceptorService<POINTCUT>, IBaseInterceptorBroadcaster<POINTCUT> { implements IBaseInterceptorService<POINTCUT>, IBaseInterceptorBroadcaster<POINTCUT> {
private static final Logger ourLog = LoggerFactory.getLogger(BaseInterceptorService.class); private static final Logger ourLog = LoggerFactory.getLogger(BaseInterceptorService.class);
@ -74,12 +76,11 @@ public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & I
AttributeKey.stringKey("hapifhir.interceptor.method_name"); AttributeKey.stringKey("hapifhir.interceptor.method_name");
private final List<Object> myInterceptors = new ArrayList<>(); private final List<Object> myInterceptors = new ArrayList<>();
private final ListMultimap<POINTCUT, BaseInvoker> myGlobalInvokers = ArrayListMultimap.create(); private final ListMultimap<POINTCUT, IInvoker> myGlobalInvokers = ArrayListMultimap.create();
private final ListMultimap<POINTCUT, BaseInvoker> myAnonymousInvokers = ArrayListMultimap.create(); private final ListMultimap<POINTCUT, IInvoker> myAnonymousInvokers = ArrayListMultimap.create();
private final Object myRegistryMutex = new Object(); private final Object myRegistryMutex = new Object();
private final Class<POINTCUT> myPointcutType; private final Class<POINTCUT> myPointcutType;
private volatile EnumSet<POINTCUT> myRegisteredPointcuts; private volatile EnumSet<POINTCUT> myRegisteredPointcuts;
private String myName;
private boolean myWarnOnInterceptorWithNoHooks = true; private boolean myWarnOnInterceptorWithNoHooks = true;
/** /**
@ -93,10 +94,11 @@ public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & I
* Constructor * Constructor
* *
* @param theName The name for this registry (useful for troubleshooting) * @param theName The name for this registry (useful for troubleshooting)
* @deprecated The name parameter is not used for anything
*/ */
@Deprecated(since = "8.0.0", forRemoval = true)
public BaseInterceptorService(Class<POINTCUT> thePointcutType, String theName) { public BaseInterceptorService(Class<POINTCUT> thePointcutType, String theName) {
super(); super();
myName = theName;
myPointcutType = thePointcutType; myPointcutType = thePointcutType;
rebuildRegisteredPointcutSet(); rebuildRegisteredPointcutSet();
} }
@ -113,13 +115,17 @@ public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & I
return myInterceptors; return myInterceptors;
} }
public void setName(String theName) { /**
myName = theName; * @deprecated This value is not used anywhere
*/
@Deprecated(since = "8.0.0", forRemoval = true)
public void setName(@SuppressWarnings("unused") String theName) {
// nothing
} }
protected void registerAnonymousInterceptor(POINTCUT thePointcut, Object theInterceptor, BaseInvoker theInvoker) { protected void registerAnonymousInterceptor(POINTCUT thePointcut, Object theInterceptor, BaseInvoker theInvoker) {
Validate.notNull(thePointcut); Validate.notNull(thePointcut, "thePointcut must not be null");
Validate.notNull(theInterceptor); Validate.notNull(theInterceptor, "theInterceptor must not be null");
synchronized (myRegistryMutex) { synchronized (myRegistryMutex) {
myAnonymousInvokers.put(thePointcut, theInvoker); myAnonymousInvokers.put(thePointcut, theInvoker);
if (!isInterceptorAlreadyRegistered(theInterceptor)) { if (!isInterceptorAlreadyRegistered(theInterceptor)) {
@ -179,9 +185,9 @@ public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & I
} }
private void unregisterInterceptorsIf( private void unregisterInterceptorsIf(
Predicate<Object> theShouldUnregisterFunction, ListMultimap<POINTCUT, BaseInvoker> theGlobalInvokers) { Predicate<Object> theShouldUnregisterFunction, ListMultimap<POINTCUT, IInvoker> theGlobalInvokers) {
synchronized (myRegistryMutex) { synchronized (myRegistryMutex) {
for (Map.Entry<POINTCUT, BaseInvoker> nextInvoker : new ArrayList<>(theGlobalInvokers.entries())) { for (Map.Entry<POINTCUT, IInvoker> nextInvoker : new ArrayList<>(theGlobalInvokers.entries())) {
if (theShouldUnregisterFunction.test(nextInvoker.getValue().getInterceptor())) { if (theShouldUnregisterFunction.test(nextInvoker.getValue().getInterceptor())) {
unregisterInterceptor(nextInvoker.getValue().getInterceptor()); unregisterInterceptor(nextInvoker.getValue().getInterceptor());
} }
@ -265,7 +271,7 @@ public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & I
assert haveAppropriateParams(thePointcut, theParams); assert haveAppropriateParams(thePointcut, theParams);
assert thePointcut.getReturnType() != void.class; assert thePointcut.getReturnType() != void.class;
return doCallHooks(thePointcut, theParams, null); return doCallHooks(thePointcut, theParams);
} }
@Override @Override
@ -282,116 +288,47 @@ public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & I
assert haveAppropriateParams(thePointcut, theParams); assert haveAppropriateParams(thePointcut, theParams);
assert thePointcut.getReturnType() == void.class || thePointcut.getReturnType() == getBooleanReturnType(); assert thePointcut.getReturnType() == void.class || thePointcut.getReturnType() == getBooleanReturnType();
Object retValObj = doCallHooks(thePointcut, theParams, true); Object retValObj = doCallHooks(thePointcut, theParams);
retValObj = defaultIfNull(retValObj, true);
return (Boolean) retValObj; return (Boolean) retValObj;
} }
private Object doCallHooks(POINTCUT thePointcut, HookParams theParams, Object theRetVal) { private Object doCallHooks(POINTCUT thePointcut, HookParams theParams) {
// use new list for loop to avoid ConcurrentModificationException in case invoker gets added while looping List<IInvoker> invokers = getInvokersForPointcut(thePointcut);
List<BaseInvoker> invokers = new ArrayList<>(getInvokersForPointcut(thePointcut)); return callInvokers(thePointcut, theParams, invokers);
/*
* Call each hook in order
*/
for (BaseInvoker nextInvoker : invokers) {
Object nextOutcome = nextInvoker.invoke(theParams);
Class<?> pointcutReturnType = thePointcut.getReturnType();
if (pointcutReturnType.equals(getBooleanReturnType())) {
Boolean nextOutcomeAsBoolean = (Boolean) nextOutcome;
if (Boolean.FALSE.equals(nextOutcomeAsBoolean)) {
ourLog.trace("callHooks({}) for invoker({}) returned false", thePointcut, nextInvoker);
theRetVal = false;
break;
} else {
theRetVal = true;
}
} else if (!pointcutReturnType.equals(void.class)) {
if (nextOutcome != null) {
theRetVal = nextOutcome;
break;
}
}
}
return theRetVal;
} }
@VisibleForTesting @VisibleForTesting
List<Object> getInterceptorsWithInvokersForPointcut(POINTCUT thePointcut) { List<Object> getInterceptorsWithInvokersForPointcut(POINTCUT thePointcut) {
return getInvokersForPointcut(thePointcut).stream() return getInvokersForPointcut(thePointcut).stream()
.map(BaseInvoker::getInterceptor) .map(IInvoker::getInterceptor)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
/** /**
* Returns an ordered list of invokers for the given pointcut. Note that * Returns a list of all invokers registered for the given pointcut. The list
* a new and stable list is returned to.. do whatever you want with it. * is ordered by the invoker order (specified on the {@link Interceptor#order()}
* and {@link Hook#order()} values.
*
* @return The list returned by this method will always be a newly created list, so it will be stable and can be modified.
*/ */
private List<BaseInvoker> getInvokersForPointcut(POINTCUT thePointcut) { @Override
List<BaseInvoker> invokers; public List<IInvoker> getInvokersForPointcut(POINTCUT thePointcut) {
List<IInvoker> invokers;
synchronized (myRegistryMutex) { synchronized (myRegistryMutex) {
List<BaseInvoker> globalInvokers = myGlobalInvokers.get(thePointcut); List<IInvoker> globalInvokers = myGlobalInvokers.get(thePointcut);
List<BaseInvoker> anonymousInvokers = myAnonymousInvokers.get(thePointcut); List<IInvoker> anonymousInvokers = myAnonymousInvokers.get(thePointcut);
List<BaseInvoker> threadLocalInvokers = null; invokers = union(Arrays.asList(globalInvokers, anonymousInvokers));
invokers = union(globalInvokers, anonymousInvokers, threadLocalInvokers);
} }
return invokers; return invokers;
} }
/**
* First argument must be the global invoker list!!
*/
@SafeVarargs
private List<BaseInvoker> union(List<BaseInvoker>... theInvokersLists) {
List<BaseInvoker> haveOne = null;
boolean haveMultiple = false;
for (List<BaseInvoker> nextInvokerList : theInvokersLists) {
if (nextInvokerList == null || nextInvokerList.isEmpty()) {
continue;
}
if (haveOne == null) {
haveOne = nextInvokerList;
} else {
haveMultiple = true;
}
}
if (haveOne == null) {
return Collections.emptyList();
}
List<BaseInvoker> retVal;
if (!haveMultiple) {
// The global list doesn't need to be sorted every time since it's sorted on
// insertion each time. Doing so is a waste of cycles..
if (haveOne == theInvokersLists[0]) {
retVal = haveOne;
} else {
retVal = new ArrayList<>(haveOne);
retVal.sort(Comparator.naturalOrder());
}
} else {
retVal = Arrays.stream(theInvokersLists)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.sorted()
.collect(Collectors.toList());
}
return retVal;
}
/** /**
* Only call this when assertions are enabled, it's expensive * Only call this when assertions are enabled, it's expensive
*/ */
final boolean haveAppropriateParams(POINTCUT thePointcut, HookParams theParams) { public static boolean haveAppropriateParams(IPointcut thePointcut, HookParams theParams) {
if (theParams.getParamsForType().values().size() if (theParams.getParamsForType().values().size()
!= thePointcut.getParameterTypes().size()) { != thePointcut.getParameterTypes().size()) {
throw new IllegalArgumentException(Msg.code(1909) throw new IllegalArgumentException(Msg.code(1909)
@ -430,7 +367,7 @@ public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & I
} }
private List<HookInvoker> scanInterceptorAndAddToInvokerMultimap( private List<HookInvoker> scanInterceptorAndAddToInvokerMultimap(
Object theInterceptor, ListMultimap<POINTCUT, BaseInvoker> theInvokers) { Object theInterceptor, ListMultimap<POINTCUT, IInvoker> theInvokers) {
Class<?> interceptorClass = theInterceptor.getClass(); Class<?> interceptorClass = theInterceptor.getClass();
int typeOrder = determineOrder(interceptorClass); int typeOrder = determineOrder(interceptorClass);
@ -452,7 +389,7 @@ public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & I
// Make sure we're always sorted according to the order declared in @Order // Make sure we're always sorted according to the order declared in @Order
for (POINTCUT nextPointcut : theInvokers.keys()) { for (POINTCUT nextPointcut : theInvokers.keys()) {
List<BaseInvoker> nextInvokerList = theInvokers.get(nextPointcut); List<IInvoker> nextInvokerList = theInvokers.get(nextPointcut);
nextInvokerList.sort(Comparator.naturalOrder()); nextInvokerList.sort(Comparator.naturalOrder());
} }
@ -483,6 +420,108 @@ public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & I
protected abstract Optional<HookDescriptor> scanForHook(Method nextMethod); protected abstract Optional<HookDescriptor> scanForHook(Method nextMethod);
public static Object callInvokers(IPointcut thePointcut, HookParams theParams, List<IInvoker> invokers) {
Object retVal = null;
/*
* Call each hook in order
*/
for (IInvoker nextInvoker : invokers) {
Object nextOutcome = nextInvoker.invoke(theParams);
Class<?> pointcutReturnType = thePointcut.getReturnType();
if (pointcutReturnType.equals(thePointcut.getBooleanReturnTypeForEnum())) {
Boolean nextOutcomeAsBoolean = (Boolean) nextOutcome;
if (Boolean.FALSE.equals(nextOutcomeAsBoolean)) {
ourLog.trace("callHooks({}) for invoker({}) returned false", thePointcut, nextInvoker);
retVal = false;
break;
} else {
retVal = true;
}
} else if (!pointcutReturnType.equals(void.class)) {
if (nextOutcome != null) {
retVal = nextOutcome;
break;
}
}
}
return retVal;
}
/**
* First argument must be the global invoker list!!
*/
public static List<IInvoker> union(List<List<IInvoker>> theInvokersLists) {
List<IInvoker> haveOne = null;
boolean haveMultiple = false;
for (List<IInvoker> nextInvokerList : theInvokersLists) {
if (nextInvokerList == null || nextInvokerList.isEmpty()) {
continue;
}
if (haveOne == null) {
haveOne = nextInvokerList;
} else {
haveMultiple = true;
}
}
if (haveOne == null) {
return Collections.emptyList();
}
List<IInvoker> retVal;
if (!haveMultiple) {
// The global list doesn't need to be sorted every time since it's sorted on
// insertion each time. Doing so is a waste of cycles..
if (haveOne == theInvokersLists.get(0)) {
retVal = haveOne;
} else {
retVal = new ArrayList<>(haveOne);
retVal.sort(Comparator.naturalOrder());
}
} else {
int totalSize = 0;
for (List<IInvoker> list : theInvokersLists) {
totalSize += list.size();
}
retVal = new ArrayList<>(totalSize);
for (List<IInvoker> list : theInvokersLists) {
retVal.addAll(list);
}
retVal.sort(Comparator.naturalOrder());
}
return retVal;
}
protected static <T extends Annotation> Optional<T> findAnnotation(
AnnotatedElement theObject, Class<T> theHookClass) {
T annotation;
if (theObject instanceof Method) {
annotation = MethodUtils.getAnnotation((Method) theObject, theHookClass, true, true);
} else {
annotation = theObject.getAnnotation(theHookClass);
}
return Optional.ofNullable(annotation);
}
private static int determineOrder(Class<?> theInterceptorClass) {
return findAnnotation(theInterceptorClass, Interceptor.class)
.map(Interceptor::order)
.orElse(Interceptor.DEFAULT_ORDER);
}
private static String toErrorString(List<String> theParameterTypes) {
return theParameterTypes.stream().sorted().collect(Collectors.joining(","));
}
private class HookInvoker extends BaseInvoker { private class HookInvoker extends BaseInvoker {
private final Method myMethod; private final Method myMethod;
@ -501,10 +540,11 @@ public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & I
myMethod = theHookMethod; myMethod = theHookMethod;
Class<?> returnType = theHookMethod.getReturnType(); Class<?> returnType = theHookMethod.getReturnType();
if (myPointcut.getReturnType().equals(getBooleanReturnType())) { if (myPointcut.getReturnType().equals(myPointcut.getBooleanReturnTypeForEnum())) {
Validate.isTrue( Validate.isTrue(
getBooleanReturnType().equals(returnType) || void.class.equals(returnType), myPointcut.getBooleanReturnTypeForEnum().equals(returnType) || void.class.equals(returnType),
"Method does not return boolean or void: %s", "Method does not return %s or void: %s",
myPointcut.getBooleanReturnTypeForEnum().getSimpleName(),
theHookMethod); theHookMethod);
} else if (myPointcut.getReturnType().equals(void.class)) { } else if (myPointcut.getReturnType().equals(void.class)) {
Validate.isTrue(void.class.equals(returnType), "Method does not return void: %s", theHookMethod); Validate.isTrue(void.class.equals(returnType), "Method does not return void: %s", theHookMethod);
@ -541,7 +581,7 @@ public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & I
* @return Returns true/false if the hook method returns a boolean, returns true otherwise * @return Returns true/false if the hook method returns a boolean, returns true otherwise
*/ */
@Override @Override
Object invoke(HookParams theParams) { public Object invoke(HookParams theParams) {
Object[] args = new Object[myParameterTypes.length]; Object[] args = new Object[myParameterTypes.length];
for (int i = 0; i < myParameterTypes.length; i++) { for (int i = 0; i < myParameterTypes.length; i++) {
@ -610,7 +650,7 @@ public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & I
} }
} }
protected abstract static class BaseInvoker implements Comparable<BaseInvoker> { public abstract static class BaseInvoker implements IInvoker {
private final int myOrder; private final int myOrder;
private final Object myInterceptor; private final Object myInterceptor;
@ -620,36 +660,19 @@ public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & I
myOrder = theOrder; myOrder = theOrder;
} }
@Override
public Object getInterceptor() { public Object getInterceptor() {
return myInterceptor; return myInterceptor;
} }
abstract Object invoke(HookParams theParams); @Override
public int getOrder() {
return myOrder;
}
@Override @Override
public int compareTo(BaseInvoker theInvoker) { public int compareTo(IInvoker theInvoker) {
return myOrder - theInvoker.myOrder; return myOrder - theInvoker.getOrder();
} }
} }
protected static <T extends Annotation> Optional<T> findAnnotation(
AnnotatedElement theObject, Class<T> theHookClass) {
T annotation;
if (theObject instanceof Method) {
annotation = MethodUtils.getAnnotation((Method) theObject, theHookClass, true, true);
} else {
annotation = theObject.getAnnotation(theHookClass);
}
return Optional.ofNullable(annotation);
}
private static int determineOrder(Class<?> theInterceptorClass) {
return findAnnotation(theInterceptorClass, Interceptor.class)
.map(Interceptor::order)
.orElse(Interceptor.DEFAULT_ORDER);
}
private static String toErrorString(List<String> theParameterTypes) {
return theParameterTypes.stream().sorted().collect(Collectors.joining(","));
}
} }

View File

@ -64,8 +64,8 @@ public class InterceptorService extends BaseInterceptorService<Pointcut>
@Override @Override
public void registerAnonymousInterceptor(Pointcut thePointcut, int theOrder, IAnonymousInterceptor theInterceptor) { public void registerAnonymousInterceptor(Pointcut thePointcut, int theOrder, IAnonymousInterceptor theInterceptor) {
Validate.notNull(thePointcut); Validate.notNull(thePointcut, "thePointcut must not be null");
Validate.notNull(theInterceptor); Validate.notNull(theInterceptor, "theInterceptor must not be null");
BaseInvoker invoker = new AnonymousLambdaInvoker(thePointcut, theInterceptor, theOrder); BaseInvoker invoker = new AnonymousLambdaInvoker(thePointcut, theInterceptor, theOrder);
registerAnonymousInterceptor(thePointcut, theInterceptor, invoker); registerAnonymousInterceptor(thePointcut, theInterceptor, invoker);
} }
@ -81,7 +81,7 @@ public class InterceptorService extends BaseInterceptorService<Pointcut>
} }
@Override @Override
Object invoke(HookParams theParams) { public Object invoke(HookParams theParams) {
myHook.invoke(myPointcut, theParams); myHook.invoke(myPointcut, theParams);
return true; return true;
} }

View File

@ -20,6 +20,7 @@
package ca.uhn.fhir.narrative2; package ca.uhn.fhir.narrative2;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.BundleUtil;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseBundle;
@ -42,7 +43,8 @@ public class NarrativeGeneratorTemplateUtils {
* Given a Bundle as input, are any entries present with a given resource type * Given a Bundle as input, are any entries present with a given resource type
*/ */
public boolean bundleHasEntriesWithResourceType(IBaseBundle theBaseBundle, String theResourceType) { public boolean bundleHasEntriesWithResourceType(IBaseBundle theBaseBundle, String theResourceType) {
FhirContext ctx = theBaseBundle.getStructureFhirVersionEnum().newContextCached(); FhirVersionEnum fhirVersionEnum = theBaseBundle.getStructureFhirVersionEnum();
FhirContext ctx = FhirContext.forCached(fhirVersionEnum);
List<Pair<String, IBaseResource>> entryResources = List<Pair<String, IBaseResource>> entryResources =
BundleUtil.getBundleEntryUrlsAndResources(ctx, theBaseBundle); BundleUtil.getBundleEntryUrlsAndResources(ctx, theBaseBundle);
return entryResources.stream() return entryResources.stream()

View File

@ -668,7 +668,7 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
protected void parseFhirContext(CommandLine theCommandLine) throws ParseException { protected void parseFhirContext(CommandLine theCommandLine) throws ParseException {
FhirVersionEnum versionEnum = parseFhirVersion(theCommandLine); FhirVersionEnum versionEnum = parseFhirVersion(theCommandLine);
myFhirCtx = versionEnum.newContext(); myFhirCtx = FhirContext.forVersion(versionEnum);
} }
public abstract void run(CommandLine theCommandLine) throws ParseException, ExecutionException; public abstract void run(CommandLine theCommandLine) throws ParseException, ExecutionException;

View File

@ -98,7 +98,7 @@ public class VersionCanonicalizer {
private final FhirContext myContext; private final FhirContext myContext;
public VersionCanonicalizer(FhirVersionEnum theTargetVersion) { public VersionCanonicalizer(FhirVersionEnum theTargetVersion) {
this(theTargetVersion.newContextCached()); this(FhirContext.forCached(theTargetVersion));
} }
public VersionCanonicalizer(FhirContext theTargetContext) { public VersionCanonicalizer(FhirContext theTargetContext) {

View File

@ -0,0 +1,8 @@
---
type: add
issue: 6511
title: "Interceptors can be defined against the registry on the RestfulServer, or on
the registry in the JPA repository. Because these are separate registries, the order()
attribute on the Hook annotation isn't correctly processed today across the two
registries. The CompositeInterceptorRegistry has been reworked so ordering will be
respected across both registries."

View File

@ -0,0 +1,4 @@
---
type: remove
issue: 6512
title: "The methods on FhirVersionEnum which produces a FhirContext (newContext() ,and newContextCached()) have been deprecated, and will be removed."

View File

@ -27,7 +27,6 @@ import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.model.PersistentIdToForcedIdMap; import ca.uhn.fhir.jpa.api.model.PersistentIdToForcedIdMap;
import ca.uhn.fhir.jpa.api.svc.IIdHelperService; import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.jpa.bulk.export.api.IBulkExportProcessor; import ca.uhn.fhir.jpa.bulk.export.api.IBulkExportProcessor;
@ -349,10 +348,9 @@ public class JpaBulkExportProcessor implements IBulkExportProcessor<JpaPid> {
* Get a ISearchBuilder for the given resource type. * Get a ISearchBuilder for the given resource type.
*/ */
protected ISearchBuilder<JpaPid> getSearchBuilderForResourceType(String theResourceType) { protected ISearchBuilder<JpaPid> getSearchBuilderForResourceType(String theResourceType) {
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(theResourceType);
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResourceType); RuntimeResourceDefinition def = myContext.getResourceDefinition(theResourceType);
Class<? extends IBaseResource> typeClass = def.getImplementingClass(); Class<? extends IBaseResource> typeClass = def.getImplementingClass();
return mySearchBuilderFactory.newSearchBuilder(dao, theResourceType, typeClass); return mySearchBuilderFactory.newSearchBuilder(theResourceType, typeClass);
} }
protected RuntimeSearchParam getPatientSearchParamForCurrentResourceType(String theResourceType) { protected RuntimeSearchParam getPatientSearchParamForCurrentResourceType(String theResourceType) {

View File

@ -25,7 +25,6 @@ import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IDao;
import ca.uhn.fhir.jpa.api.svc.IIdHelperService; import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.dao.ISearchBuilder; import ca.uhn.fhir.jpa.dao.ISearchBuilder;
@ -158,10 +157,8 @@ public class SearchConfig {
@Bean(name = ISearchBuilder.SEARCH_BUILDER_BEAN_NAME) @Bean(name = ISearchBuilder.SEARCH_BUILDER_BEAN_NAME)
@Scope("prototype") @Scope("prototype")
public ISearchBuilder newSearchBuilder( public ISearchBuilder newSearchBuilder(String theResourceName, Class<? extends IBaseResource> theResourceType) {
IDao theDao, String theResourceName, Class<? extends IBaseResource> theResourceType) {
return new SearchBuilder( return new SearchBuilder(
theDao,
theResourceName, theResourceName,
myStorageSettings, myStorageSettings,
myEntityManagerFactory, myEntityManagerFactory,

View File

@ -1057,8 +1057,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
// Interceptor broadcast: JPA_PERFTRACE_INFO // Interceptor broadcast: JPA_PERFTRACE_INFO
if (!presenceCount.isEmpty()) { if (!presenceCount.isEmpty()) {
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequest)) { CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO)) {
StorageProcessingMessage message = new StorageProcessingMessage(); StorageProcessingMessage message = new StorageProcessingMessage();
message.setMessage( message.setMessage(
"For " + entity.getIdDt().toUnqualifiedVersionless().getValue() + " added " "For " + entity.getIdDt().toUnqualifiedVersionless().getValue() + " added "
@ -1068,8 +1069,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
.add(RequestDetails.class, theRequest) .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest) .addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, message); .add(StorageProcessingMessage.class, message);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_INFO, params);
myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
} }
} }
} }
@ -1092,8 +1092,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
// Interceptor broadcast: JPA_PERFTRACE_INFO // Interceptor broadcast: JPA_PERFTRACE_INFO
if (!searchParamAddRemoveCount.isEmpty()) { if (!searchParamAddRemoveCount.isEmpty()) {
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequest)) { CompositeInterceptorBroadcaster.newCompositeBroadcaster(
myInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO)) {
StorageProcessingMessage message = new StorageProcessingMessage(); StorageProcessingMessage message = new StorageProcessingMessage();
message.setMessage("For " message.setMessage("For "
+ entity.getIdDt().toUnqualifiedVersionless().getValue() + " added " + entity.getIdDt().toUnqualifiedVersionless().getValue() + " added "
@ -1104,8 +1106,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
.add(RequestDetails.class, theRequest) .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest) .addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, message); .add(StorageProcessingMessage.class, message);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_INFO, params);
myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
} }
} }
} }

View File

@ -227,15 +227,15 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Nullable @Nullable
public static <T extends IBaseResource> T invokeStoragePreShowResources( public static <T extends IBaseResource> T invokeStoragePreShowResources(
IInterceptorBroadcaster theInterceptorBroadcaster, RequestDetails theRequest, T retVal) { IInterceptorBroadcaster theInterceptorBroadcaster, RequestDetails theRequest, T retVal) {
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.STORAGE_PRESHOW_RESOURCES, theInterceptorBroadcaster, theRequest)) { CompositeInterceptorBroadcaster.newCompositeBroadcaster(theInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRESHOW_RESOURCES)) {
SimplePreResourceShowDetails showDetails = new SimplePreResourceShowDetails(retVal); SimplePreResourceShowDetails showDetails = new SimplePreResourceShowDetails(retVal);
HookParams params = new HookParams() HookParams params = new HookParams()
.add(IPreResourceShowDetails.class, showDetails) .add(IPreResourceShowDetails.class, showDetails)
.add(RequestDetails.class, theRequest) .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest); .addIfMatchesType(ServletRequestDetails.class, theRequest);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_PRESHOW_RESOURCES, params);
theInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
//noinspection unchecked //noinspection unchecked
retVal = (T) showDetails.getResource( retVal = (T) showDetails.getResource(
0); // TODO GGG/JA : getting resource 0 is interesting. We apparently allow null values in the list. 0); // TODO GGG/JA : getting resource 0 is interesting. We apparently allow null values in the list.
@ -251,15 +251,15 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
RequestDetails theRequest, RequestDetails theRequest,
IIdType theId, IIdType theId,
IBaseResource theResource) { IBaseResource theResource) {
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.STORAGE_PREACCESS_RESOURCES, theInterceptorBroadcaster, theRequest)) { CompositeInterceptorBroadcaster.newCompositeBroadcaster(theInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES)) {
SimplePreResourceAccessDetails accessDetails = new SimplePreResourceAccessDetails(theResource); SimplePreResourceAccessDetails accessDetails = new SimplePreResourceAccessDetails(theResource);
HookParams params = new HookParams() HookParams params = new HookParams()
.add(IPreResourceAccessDetails.class, accessDetails) .add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, theRequest) .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest); .addIfMatchesType(ServletRequestDetails.class, theRequest);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_PREACCESS_RESOURCES, params);
theInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
if (accessDetails.isDontReturnResourceAtIndex(0)) { if (accessDetails.isDontReturnResourceAtIndex(0)) {
throw new ResourceNotFoundException(Msg.code(1995) + "Resource " + theId + " is not known"); throw new ResourceNotFoundException(Msg.code(1995) + "Resource " + theId + " is not known");
} }
@ -1585,15 +1585,15 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
} }
private Optional<T> invokeStoragePreAccessResources(RequestDetails theRequest, T theResource) { private Optional<T> invokeStoragePreAccessResources(RequestDetails theRequest, T theResource) {
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.STORAGE_PREACCESS_RESOURCES, myInterceptorBroadcaster, theRequest)) { CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES)) {
SimplePreResourceAccessDetails accessDetails = new SimplePreResourceAccessDetails(theResource); SimplePreResourceAccessDetails accessDetails = new SimplePreResourceAccessDetails(theResource);
HookParams params = new HookParams() HookParams params = new HookParams()
.add(IPreResourceAccessDetails.class, accessDetails) .add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, theRequest) .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest); .addIfMatchesType(ServletRequestDetails.class, theRequest);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_PREACCESS_RESOURCES, params);
myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
if (accessDetails.isDontReturnResourceAtIndex(0)) { if (accessDetails.isDontReturnResourceAtIndex(0)) {
return Optional.empty(); return Optional.empty();
} }
@ -2103,7 +2103,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
} }
ISearchBuilder<JpaPid> builder = ISearchBuilder<JpaPid> builder =
mySearchBuilderFactory.newSearchBuilder(this, getResourceName(), getResourceType()); mySearchBuilderFactory.newSearchBuilder(getResourceName(), getResourceType());
List<JpaPid> ids = new ArrayList<>(); List<JpaPid> ids = new ArrayList<>();
@ -2136,8 +2136,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
myRequestPartitionHelperService.determineReadPartitionForRequestForSearchType( myRequestPartitionHelperService.determineReadPartitionForRequestForSearchType(
theRequest, myResourceName, theParams, theConditionalOperationTargetOrNull); theRequest, myResourceName, theParams, theConditionalOperationTargetOrNull);
ISearchBuilder<JpaPid> builder = ISearchBuilder<JpaPid> builder = mySearchBuilderFactory.newSearchBuilder(getResourceName(), getResourceType());
mySearchBuilderFactory.newSearchBuilder(this, getResourceName(), getResourceType());
String uuid = UUID.randomUUID().toString(); String uuid = UUID.randomUUID().toString();
@ -2175,7 +2174,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
.withPropagation(Propagation.REQUIRED) .withPropagation(Propagation.REQUIRED)
.searchList(() -> { .searchList(() -> {
ISearchBuilder<JpaPid> builder = ISearchBuilder<JpaPid> builder =
mySearchBuilderFactory.newSearchBuilder(this, getResourceName(), getResourceType()); mySearchBuilderFactory.newSearchBuilder(getResourceName(), getResourceType());
Stream<JpaPid> pidStream = Stream<JpaPid> pidStream =
builder.createQueryStream(theParams, searchRuntimeDetails, theRequest, requestPartitionId); builder.createQueryStream(theParams, searchRuntimeDetails, theRequest, requestPartitionId);
@ -2191,7 +2190,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Nonnull @Nonnull
private Stream<T> pidsToResource(RequestDetails theRequest, Stream<JpaPid> pidStream) { private Stream<T> pidsToResource(RequestDetails theRequest, Stream<JpaPid> pidStream) {
ISearchBuilder<JpaPid> searchBuilder = ISearchBuilder<JpaPid> searchBuilder =
mySearchBuilderFactory.newSearchBuilder(this, getResourceName(), getResourceType()); mySearchBuilderFactory.newSearchBuilder(getResourceName(), getResourceType());
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Stream<T> resourceStream = (Stream<T>) new QueryChunker<>() Stream<T> resourceStream = (Stream<T>) new QueryChunker<>()
.chunk(pidStream, SearchBuilder.getMaximumPageSize()) .chunk(pidStream, SearchBuilder.getMaximumPageSize())

View File

@ -516,8 +516,9 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private void logQuery(SearchQueryOptionsStep theQuery, RequestDetails theRequestDetails) { private void logQuery(SearchQueryOptionsStep theQuery, RequestDetails theRequestDetails) {
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequestDetails)) { CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO)) {
StorageProcessingMessage storageProcessingMessage = new StorageProcessingMessage(); StorageProcessingMessage storageProcessingMessage = new StorageProcessingMessage();
String queryString = theQuery.toQuery().queryString(); String queryString = theQuery.toQuery().queryString();
storageProcessingMessage.setMessage(queryString); storageProcessingMessage.setMessage(queryString);
@ -525,8 +526,7 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
.add(RequestDetails.class, theRequestDetails) .add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails) .addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(StorageProcessingMessage.class, storageProcessingMessage); .add(StorageProcessingMessage.class, storageProcessingMessage);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_INFO, params);
myInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_INFO, params);
} }
} }

View File

@ -273,7 +273,13 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
} }
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1); params.setLoadSynchronousUpTo(1);
params.add(StructureDefinition.SP_URL, new UriParam(theUri)); int versionSeparator = theUri.lastIndexOf('|');
if (versionSeparator != -1) {
params.add(StructureDefinition.SP_VERSION, new TokenParam(theUri.substring(versionSeparator + 1)));
params.add(StructureDefinition.SP_URL, new UriParam(theUri.substring(0, versionSeparator)));
} else {
params.add(StructureDefinition.SP_URL, new UriParam(theUri));
}
search = myDaoRegistry.getResourceDao("StructureDefinition").search(params); search = myDaoRegistry.getResourceDao("StructureDefinition").search(params);
break; break;
} }

View File

@ -124,12 +124,15 @@ public class ExpungeEverythingService implements IExpungeEverythingService {
final AtomicInteger counter = new AtomicInteger(); final AtomicInteger counter = new AtomicInteger();
// Notify Interceptors about pre-action call // Notify Interceptors about pre-action call
HookParams hooks = new HookParams() IInterceptorBroadcaster compositeBroadcaster =
.add(AtomicInteger.class, counter) CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
.add(RequestDetails.class, theRequest) if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_EVERYTHING)) {
.addIfMatchesType(ServletRequestDetails.class, theRequest); HookParams hooks = new HookParams()
CompositeInterceptorBroadcaster.doCallHooks( .add(AtomicInteger.class, counter)
myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESTORAGE_EXPUNGE_EVERYTHING, hooks); .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
compositeBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_EVERYTHING, hooks);
}
ourLog.info("BEGINNING GLOBAL $expunge"); ourLog.info("BEGINNING GLOBAL $expunge");
Propagation propagation = Propagation.REQUIRES_NEW; Propagation propagation = Propagation.REQUIRES_NEW;

View File

@ -256,8 +256,9 @@ public class JpaResourceExpungeService implements IResourceExpungeService<JpaPid
ResourceHistoryTable theVersion, ResourceHistoryTable theVersion,
IdDt theId) { IdDt theId) {
final AtomicInteger counter = new AtomicInteger(); final AtomicInteger counter = new AtomicInteger();
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, myInterceptorBroadcaster, theRequestDetails)) { CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE)) {
IBaseResource resource = myJpaStorageResourceParser.toResource(theVersion, false); IBaseResource resource = myJpaStorageResourceParser.toResource(theVersion, false);
HookParams params = new HookParams() HookParams params = new HookParams()
.add(AtomicInteger.class, counter) .add(AtomicInteger.class, counter)
@ -265,8 +266,7 @@ public class JpaResourceExpungeService implements IResourceExpungeService<JpaPid
.add(IBaseResource.class, resource) .add(IBaseResource.class, resource)
.add(RequestDetails.class, theRequestDetails) .add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); .addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, params);
myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, params);
} }
theRemainingCount.addAndGet(-1 * counter.get()); theRemainingCount.addAndGet(-1 * counter.get());
} }

View File

@ -106,13 +106,15 @@ public class DeleteConflictService {
} }
// Notify Interceptors about pre-action call // Notify Interceptors about pre-action call
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
HookParams hooks = new HookParams() HookParams hooks = new HookParams()
.add(DeleteConflictList.class, theDeleteConflicts) .add(DeleteConflictList.class, theDeleteConflicts)
.add(RequestDetails.class, theRequest) .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest) .addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(TransactionDetails.class, theTransactionDetails); .add(TransactionDetails.class, theTransactionDetails);
return (DeleteConflictOutcome) CompositeInterceptorBroadcaster.doCallHooksAndReturnObject( return (DeleteConflictOutcome)
myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESTORAGE_DELETE_CONFLICTS, hooks); compositeBroadcaster.callHooksAndReturnObject(Pointcut.STORAGE_PRESTORAGE_DELETE_CONFLICTS, hooks);
} }
private void addConflictsToList( private void addConflictsToList(

View File

@ -155,17 +155,19 @@ public class ThreadSafeResourceDeleterSvc {
TransactionDetails theTransactionDetails, TransactionDetails theTransactionDetails,
IdDt nextSource, IdDt nextSource,
IFhirResourceDao<?> dao) { IFhirResourceDao<?> dao) {
// Interceptor call: STORAGE_CASCADE_DELETE
// Remove the version so we grab the latest version to delete // Remove the version so we grab the latest version to delete
IBaseResource resource = dao.read(nextSource.toVersionless(), theRequest); IBaseResource resource = dao.read(nextSource.toVersionless(), theRequest);
// Interceptor call: STORAGE_CASCADE_DELETE
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
HookParams params = new HookParams() HookParams params = new HookParams()
.add(RequestDetails.class, theRequest) .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest) .addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(DeleteConflictList.class, theConflictList) .add(DeleteConflictList.class, theConflictList)
.add(IBaseResource.class, resource); .add(IBaseResource.class, resource);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_CASCADE_DELETE, params);
myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_CASCADE_DELETE, params);
return dao.delete(resource.getIdElement(), theConflictList, theRequest, theTransactionDetails); return dao.delete(resource.getIdElement(), theConflictList, theRequest, theTransactionDetails);
} }

View File

@ -27,7 +27,6 @@ import ca.uhn.fhir.interceptor.model.ReadPartitionIdRequestDetails;
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.dao.HistoryBuilder; import ca.uhn.fhir.jpa.dao.HistoryBuilder;
import ca.uhn.fhir.jpa.dao.HistoryBuilderFactory; import ca.uhn.fhir.jpa.dao.HistoryBuilderFactory;
@ -189,15 +188,17 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
retVal.add(myJpaStorageResourceParser.toResource(resource, true)); retVal.add(myJpaStorageResourceParser.toResource(resource, true));
} }
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, myRequest);
// Interceptor call: STORAGE_PREACCESS_RESOURCES // Interceptor call: STORAGE_PREACCESS_RESOURCES
{ if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES)) {
SimplePreResourceAccessDetails accessDetails = new SimplePreResourceAccessDetails(retVal); SimplePreResourceAccessDetails accessDetails = new SimplePreResourceAccessDetails(retVal);
HookParams params = new HookParams() HookParams params = new HookParams()
.add(IPreResourceAccessDetails.class, accessDetails) .add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, myRequest) .add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest); .addIfMatchesType(ServletRequestDetails.class, myRequest);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_PREACCESS_RESOURCES, params);
myInterceptorBroadcaster, myRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
for (int i = retVal.size() - 1; i >= 0; i--) { for (int i = retVal.size() - 1; i >= 0; i--) {
if (accessDetails.isDontReturnResourceAtIndex(i)) { if (accessDetails.isDontReturnResourceAtIndex(i)) {
@ -207,14 +208,13 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
} }
// Interceptor broadcast: STORAGE_PRESHOW_RESOURCES // Interceptor broadcast: STORAGE_PRESHOW_RESOURCES
{ if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRESHOW_RESOURCES)) {
SimplePreResourceShowDetails showDetails = new SimplePreResourceShowDetails(retVal); SimplePreResourceShowDetails showDetails = new SimplePreResourceShowDetails(retVal);
HookParams params = new HookParams() HookParams params = new HookParams()
.add(IPreResourceShowDetails.class, showDetails) .add(IPreResourceShowDetails.class, showDetails)
.add(RequestDetails.class, myRequest) .add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest); .addIfMatchesType(ServletRequestDetails.class, myRequest);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_PRESHOW_RESOURCES, params);
myInterceptorBroadcaster, myRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
retVal = showDetails.toList(); retVal = showDetails.toList();
} }
@ -254,9 +254,8 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
String resourceName = mySearchEntity.getResourceType(); String resourceName = mySearchEntity.getResourceType();
Class<? extends IBaseResource> resourceType = Class<? extends IBaseResource> resourceType =
myContext.getResourceDefinition(resourceName).getImplementingClass(); myContext.getResourceDefinition(resourceName).getImplementingClass();
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(resourceName);
final ISearchBuilder sb = mySearchBuilderFactory.newSearchBuilder(dao, resourceName, resourceType); final ISearchBuilder sb = mySearchBuilderFactory.newSearchBuilder(resourceName, resourceType);
RequestPartitionId requestPartitionId = getRequestPartitionId(); RequestPartitionId requestPartitionId = getRequestPartitionId();
// we request 1 more resource than we need // we request 1 more resource than we need

View File

@ -374,8 +374,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
Class<? extends IBaseResource> resourceTypeClass = Class<? extends IBaseResource> resourceTypeClass =
myContext.getResourceDefinition(theResourceType).getImplementingClass(); myContext.getResourceDefinition(theResourceType).getImplementingClass();
final ISearchBuilder<JpaPid> sb = final ISearchBuilder<JpaPid> sb = mySearchBuilderFactory.newSearchBuilder(theResourceType, resourceTypeClass);
mySearchBuilderFactory.newSearchBuilder(theCallingDao, theResourceType, resourceTypeClass);
sb.setFetchSize(mySyncSize); sb.setFetchSize(mySyncSize);
final Integer loadSynchronousUpTo = getLoadSynchronousUpToOrNull(theCacheControlDirective); final Integer loadSynchronousUpTo = getLoadSynchronousUpToOrNull(theCacheControlDirective);
@ -599,17 +598,18 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
.withRequest(theRequestDetails) .withRequest(theRequestDetails)
.withRequestPartitionId(theRequestPartitionId) .withRequestPartitionId(theRequestPartitionId)
.execute(() -> { .execute(() -> {
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(
myInterceptorBroadcaster, theRequestDetails);
// Interceptor call: STORAGE_PRECHECK_FOR_CACHED_SEARCH // Interceptor call: STORAGE_PRECHECK_FOR_CACHED_SEARCH
HookParams params = new HookParams() HookParams params = new HookParams()
.add(SearchParameterMap.class, theParams) .add(SearchParameterMap.class, theParams)
.add(RequestDetails.class, theRequestDetails) .add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); .addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
boolean canUseCache = CompositeInterceptorBroadcaster.doCallHooks( boolean canUseCache =
myInterceptorBroadcaster, compositeBroadcaster.callHooks(Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH, params);
theRequestDetails,
Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH,
params);
if (!canUseCache) { if (!canUseCache) {
return null; return null;
} }
@ -626,11 +626,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
.add(SearchParameterMap.class, theParams) .add(SearchParameterMap.class, theParams)
.add(RequestDetails.class, theRequestDetails) .add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); .addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_SEARCH_REUSING_CACHED, params);
myInterceptorBroadcaster,
theRequestDetails,
Pointcut.JPA_PERFTRACE_SEARCH_REUSING_CACHED,
params);
return myPersistedJpaBundleProviderFactory.newInstance(theRequestDetails, searchToUse.getUuid()); return myPersistedJpaBundleProviderFactory.newInstance(theRequestDetails, searchToUse.getUuid());
}); });

View File

@ -27,7 +27,6 @@ import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IResultIterator; import ca.uhn.fhir.jpa.dao.IResultIterator;
import ca.uhn.fhir.jpa.dao.ISearchBuilder; import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory; import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
@ -181,12 +180,16 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc {
} }
JpaPreResourceAccessDetails accessDetails = new JpaPreResourceAccessDetails(pids, () -> theSb); JpaPreResourceAccessDetails accessDetails = new JpaPreResourceAccessDetails(pids, () -> theSb);
HookParams params = new HookParams() IInterceptorBroadcaster compositeBroadcaster =
.add(IPreResourceAccessDetails.class, accessDetails) CompositeInterceptorBroadcaster.newCompositeBroadcaster(
.add(RequestDetails.class, theRequestDetails) myInterceptorBroadcaster, theRequestDetails);
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES)) {
CompositeInterceptorBroadcaster.doCallHooks( HookParams params = new HookParams()
myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PREACCESS_RESOURCES, params); .add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
compositeBroadcaster.callHooks(Pointcut.STORAGE_PREACCESS_RESOURCES, params);
}
for (int i = pids.size() - 1; i >= 0; i--) { for (int i = pids.size() - 1; i >= 0; i--) {
if (accessDetails.isDontReturnResourceAtIndex(i)) { if (accessDetails.isDontReturnResourceAtIndex(i)) {
@ -279,12 +282,9 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc {
RequestPartitionId theRequestPartitionId) { RequestPartitionId theRequestPartitionId) {
final String searchUuid = UUID.randomUUID().toString(); final String searchUuid = UUID.randomUUID().toString();
IFhirResourceDao<?> callingDao = myDaoRegistry.getResourceDao(theResourceType);
Class<? extends IBaseResource> resourceTypeClass = Class<? extends IBaseResource> resourceTypeClass =
myContext.getResourceDefinition(theResourceType).getImplementingClass(); myContext.getResourceDefinition(theResourceType).getImplementingClass();
final ISearchBuilder sb = final ISearchBuilder sb = mySearchBuilderFactory.newSearchBuilder(theResourceType, resourceTypeClass);
mySearchBuilderFactory.newSearchBuilder(callingDao, theResourceType, resourceTypeClass);
sb.setFetchSize(mySyncSize); sb.setFetchSize(mySyncSize);
return executeQuery( return executeQuery(
theSearchParameterMap, theSearchParameterMap,

View File

@ -33,7 +33,6 @@ import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IDao;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.svc.IIdHelperService; import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.jpa.api.svc.ResolveIdentityMode; import ca.uhn.fhir.jpa.api.svc.ResolveIdentityMode;
@ -192,7 +191,6 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
private final FhirContext myContext; private final FhirContext myContext;
private final IIdHelperService<JpaPid> myIdHelperService; private final IIdHelperService<JpaPid> myIdHelperService;
private final JpaStorageSettings myStorageSettings; private final JpaStorageSettings myStorageSettings;
private final IDao myCallingDao;
@PersistenceContext(type = PersistenceContextType.TRANSACTION) @PersistenceContext(type = PersistenceContextType.TRANSACTION)
protected EntityManager myEntityManager; protected EntityManager myEntityManager;
@ -220,7 +218,6 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
*/ */
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
public SearchBuilder( public SearchBuilder(
IDao theDao,
String theResourceName, String theResourceName,
JpaStorageSettings theStorageSettings, JpaStorageSettings theStorageSettings,
HapiFhirLocalContainerEntityManagerFactoryBean theEntityManagerFactory, HapiFhirLocalContainerEntityManagerFactoryBean theEntityManagerFactory,
@ -235,7 +232,6 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
FhirContext theContext, FhirContext theContext,
IIdHelperService theIdHelperService, IIdHelperService theIdHelperService,
Class<? extends IBaseResource> theResourceType) { Class<? extends IBaseResource> theResourceType) {
myCallingDao = theDao;
myResourceName = theResourceName; myResourceName = theResourceName;
myResourceType = theResourceType; myResourceType = theResourceType;
myStorageSettings = theStorageSettings; myStorageSettings = theStorageSettings;
@ -426,15 +422,15 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
if (theSearchRuntimeDetails != null) { if (theSearchRuntimeDetails != null) {
theSearchRuntimeDetails.setFoundIndexMatchesCount(resultCount); theSearchRuntimeDetails.setFoundIndexMatchesCount(resultCount);
HookParams params = new HookParams() IInterceptorBroadcaster compositeBroadcaster =
.add(RequestDetails.class, theRequest) CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
.addIfMatchesType(ServletRequestDetails.class, theRequest) if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INDEXSEARCH_QUERY_COMPLETE)) {
.add(SearchRuntimeDetails.class, theSearchRuntimeDetails); HookParams params = new HookParams()
CompositeInterceptorBroadcaster.doCallHooks( .add(RequestDetails.class, theRequest)
myInterceptorBroadcaster, .addIfMatchesType(ServletRequestDetails.class, theRequest)
theRequest, .add(SearchRuntimeDetails.class, theSearchRuntimeDetails);
Pointcut.JPA_PERFTRACE_INDEXSEARCH_QUERY_COMPLETE, compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_INDEXSEARCH_QUERY_COMPLETE, params);
params); }
} }
// can we skip the database entirely and return the pid list from here? // can we skip the database entirely and return the pid list from here?
@ -1393,8 +1389,10 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
String searchIdOrDescription = theParameters.getSearchIdOrDescription(); String searchIdOrDescription = theParameters.getSearchIdOrDescription();
List<String> desiredResourceTypes = theParameters.getDesiredResourceTypes(); List<String> desiredResourceTypes = theParameters.getDesiredResourceTypes();
boolean hasDesiredResourceTypes = desiredResourceTypes != null && !desiredResourceTypes.isEmpty(); boolean hasDesiredResourceTypes = desiredResourceTypes != null && !desiredResourceTypes.isEmpty();
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.JPA_PERFTRACE_RAW_SQL, myInterceptorBroadcaster, theParameters.getRequestDetails())) { CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, request);
if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_RAW_SQL)) {
CurrentThreadCaptureQueriesListener.startCapturing(); CurrentThreadCaptureQueriesListener.startCapturing();
} }
if (matches.isEmpty()) { if (matches.isEmpty()) {
@ -1498,17 +1496,16 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
w.getMillisAndRestart(), w.getMillisAndRestart(),
searchIdOrDescription); searchIdOrDescription);
if (CompositeInterceptorBroadcaster.hasHooks( if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_RAW_SQL)) {
Pointcut.JPA_PERFTRACE_RAW_SQL, myInterceptorBroadcaster, request)) { callRawSqlHookWithCurrentThreadQueries(request, compositeBroadcaster);
callRawSqlHookWithCurrentThreadQueries(request);
} }
// Interceptor call: STORAGE_PREACCESS_RESOURCES // Interceptor call: STORAGE_PREACCESS_RESOURCES
// This can be used to remove results from the search result details before // This can be used to remove results from the search result details before
// the user has a chance to know that they were in the results // the user has a chance to know that they were in the results
if (!allAdded.isEmpty()) { if (!allAdded.isEmpty()) {
if (CompositeInterceptorBroadcaster.hasHooks( if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES)) {
Pointcut.STORAGE_PREACCESS_RESOURCES, myInterceptorBroadcaster, request)) {
List<JpaPid> includedPidList = new ArrayList<>(allAdded); List<JpaPid> includedPidList = new ArrayList<>(allAdded);
JpaPreResourceAccessDetails accessDetails = JpaPreResourceAccessDetails accessDetails =
new JpaPreResourceAccessDetails(includedPidList, () -> this); new JpaPreResourceAccessDetails(includedPidList, () -> this);
@ -1516,8 +1513,7 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
.add(IPreResourceAccessDetails.class, accessDetails) .add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, request) .add(RequestDetails.class, request)
.addIfMatchesType(ServletRequestDetails.class, request); .addIfMatchesType(ServletRequestDetails.class, request);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_PREACCESS_RESOURCES, params);
myInterceptorBroadcaster, request, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
for (int i = includedPidList.size() - 1; i >= 0; i--) { for (int i = includedPidList.size() - 1; i >= 0; i--) {
if (accessDetails.isDontReturnResourceAtIndex(i)) { if (accessDetails.isDontReturnResourceAtIndex(i)) {
@ -1813,17 +1809,18 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
/** /**
* Calls Performance Trace Hook * Calls Performance Trace Hook
* @param request the request deatils *
* Sends a raw SQL query to the Pointcut for raw SQL queries. * @param request the request deatils
* Sends a raw SQL query to the Pointcut for raw SQL queries.
*/ */
private void callRawSqlHookWithCurrentThreadQueries(RequestDetails request) { private void callRawSqlHookWithCurrentThreadQueries(
RequestDetails request, IInterceptorBroadcaster theCompositeBroadcaster) {
SqlQueryList capturedQueries = CurrentThreadCaptureQueriesListener.getCurrentQueueAndStopCapturing(); SqlQueryList capturedQueries = CurrentThreadCaptureQueriesListener.getCurrentQueueAndStopCapturing();
HookParams params = new HookParams() HookParams params = new HookParams()
.add(RequestDetails.class, request) .add(RequestDetails.class, request)
.addIfMatchesType(ServletRequestDetails.class, request) .addIfMatchesType(ServletRequestDetails.class, request)
.add(SqlQueryList.class, capturedQueries); .add(SqlQueryList.class, capturedQueries);
CompositeInterceptorBroadcaster.doCallHooks( theCompositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_RAW_SQL, params);
myInterceptorBroadcaster, request, Pointcut.JPA_PERFTRACE_RAW_SQL, params);
} }
@Nullable @Nullable
@ -2095,16 +2092,19 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
indexStrings.sort(Comparator.naturalOrder()); indexStrings.sort(Comparator.naturalOrder());
// Interceptor broadcast: JPA_PERFTRACE_INFO // Interceptor broadcast: JPA_PERFTRACE_INFO
String indexStringForLog = indexStrings.size() > 1 ? indexStrings.toString() : indexStrings.get(0); IInterceptorBroadcaster compositeBroadcaster =
StorageProcessingMessage msg = new StorageProcessingMessage() CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
.setMessage("Using " + theComboParam.getComboSearchParamType() + " index(es) for query for search: " if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO)) {
+ indexStringForLog); String indexStringForLog = indexStrings.size() > 1 ? indexStrings.toString() : indexStrings.get(0);
HookParams params = new HookParams() StorageProcessingMessage msg = new StorageProcessingMessage()
.add(RequestDetails.class, theRequest) .setMessage("Using " + theComboParam.getComboSearchParamType() + " index(es) for query for search: "
.addIfMatchesType(ServletRequestDetails.class, theRequest) + indexStringForLog);
.add(StorageProcessingMessage.class, msg); HookParams params = new HookParams()
CompositeInterceptorBroadcaster.doCallHooks( .add(RequestDetails.class, theRequest)
myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params); .addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, msg);
compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_INFO, params);
}
switch (requireNonNull(theComboParam.getComboSearchParamType())) { switch (requireNonNull(theComboParam.getComboSearchParamType())) {
case UNIQUE: case UNIQUE:
@ -2330,6 +2330,7 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
private final boolean myHavePerfTraceFoundIdHook; private final boolean myHavePerfTraceFoundIdHook;
private final SortSpec mySort; private final SortSpec mySort;
private final Integer myOffset; private final Integer myOffset;
private final IInterceptorBroadcaster myCompositeBroadcaster;
private boolean myFirst = true; private boolean myFirst = true;
private IncludesIterator myIncludesIterator; private IncludesIterator myIncludesIterator;
/** /**
@ -2368,16 +2369,16 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
mySort = myParams.getSort(); mySort = myParams.getSort();
myOffset = myParams.getOffset(); myOffset = myParams.getOffset();
myRequest = theRequest; myRequest = theRequest;
myCompositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
// everything requires fetching recursively all related resources // everything requires fetching recursively all related resources
if (myParams.getEverythingMode() != null) { if (myParams.getEverythingMode() != null) {
myFetchIncludesForEverythingOperation = true; myFetchIncludesForEverythingOperation = true;
} }
myHavePerfTraceFoundIdHook = CompositeInterceptorBroadcaster.hasHooks( myHavePerfTraceFoundIdHook = myCompositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID);
Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, myInterceptorBroadcaster, myRequest); myHaveRawSqlHooks = myCompositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_RAW_SQL);
myHaveRawSqlHooks = CompositeInterceptorBroadcaster.hasHooks(
Pointcut.JPA_PERFTRACE_RAW_SQL, myInterceptorBroadcaster, myRequest);
} }
private void fetchNext() { private void fetchNext() {
@ -2479,7 +2480,7 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
} finally { } finally {
// search finished - fire hooks // search finished - fire hooks
if (myHaveRawSqlHooks) { if (myHaveRawSqlHooks) {
callRawSqlHookWithCurrentThreadQueries(myRequest); callRawSqlHookWithCurrentThreadQueries(myRequest, myCompositeBroadcaster);
} }
} }
@ -2488,8 +2489,7 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
.add(RequestDetails.class, myRequest) .add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest) .addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(SearchRuntimeDetails.class, mySearchRuntimeDetails); .add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
CompositeInterceptorBroadcaster.doCallHooks( myCompositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED, params);
myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED, params);
myFirst = false; myFirst = false;
} }
@ -2498,8 +2498,7 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
.add(RequestDetails.class, myRequest) .add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest) .addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(SearchRuntimeDetails.class, mySearchRuntimeDetails); .add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
CompositeInterceptorBroadcaster.doCallHooks( myCompositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE, params);
myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE, params);
} }
} }
@ -2523,8 +2522,7 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
HookParams params = new HookParams() HookParams params = new HookParams()
.add(Integer.class, System.identityHashCode(this)) .add(Integer.class, System.identityHashCode(this))
.add(Object.class, theNextLong); .add(Object.class, theNextLong);
CompositeInterceptorBroadcaster.doCallHooks( myCompositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, params);
myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, params);
} }
private void sendProcessingMsgAndFirePerformanceHook() { private void sendProcessingMsgAndFirePerformanceHook() {
@ -2627,14 +2625,18 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
firePerformanceMessage(theRequest, theMessage, Pointcut.JPA_PERFTRACE_WARNING); firePerformanceMessage(theRequest, theMessage, Pointcut.JPA_PERFTRACE_WARNING);
} }
private void firePerformanceMessage(RequestDetails theRequest, String theMessage, Pointcut pointcut) { private void firePerformanceMessage(RequestDetails theRequest, String theMessage, Pointcut thePointcut) {
StorageProcessingMessage message = new StorageProcessingMessage(); IInterceptorBroadcaster compositeBroadcaster =
message.setMessage(theMessage); CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
HookParams params = new HookParams() if (compositeBroadcaster.hasHooks(thePointcut)) {
.add(RequestDetails.class, theRequest) StorageProcessingMessage message = new StorageProcessingMessage();
.addIfMatchesType(ServletRequestDetails.class, theRequest) message.setMessage(theMessage);
.add(StorageProcessingMessage.class, message); HookParams params = new HookParams()
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, pointcut, params); .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, message);
compositeBroadcaster.callHooks(thePointcut, params);
}
} }
public static int getMaximumPageSize() { public static int getMaximumPageSize() {

View File

@ -53,14 +53,17 @@ public class StorageInterceptorHooksFacade {
SearchParameterMap theParams, SearchParameterMap theParams,
Search search, Search search,
RequestPartitionId theRequestPartitionId) { RequestPartitionId theRequestPartitionId) {
HookParams params = new HookParams() IInterceptorBroadcaster compositeBroadcaster =
.add(ICachedSearchDetails.class, search) CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
.add(RequestDetails.class, theRequestDetails) if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRESEARCH_REGISTERED)) {
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails) HookParams params = new HookParams()
.add(SearchParameterMap.class, theParams) .add(ICachedSearchDetails.class, search)
.add(RequestPartitionId.class, theRequestPartitionId); .add(RequestDetails.class, theRequestDetails)
CompositeInterceptorBroadcaster.doCallHooks( .addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRESEARCH_REGISTERED, params); .add(SearchParameterMap.class, theParams)
.add(RequestPartitionId.class, theRequestPartitionId);
compositeBroadcaster.callHooks(Pointcut.STORAGE_PRESEARCH_REGISTERED, params);
}
} }
// private IInterceptorBroadcaster myInterceptorBroadcaster; // private IInterceptorBroadcaster myInterceptorBroadcaster;
} }

View File

@ -407,12 +407,16 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder im
} }
String message = builder.toString(); String message = builder.toString();
StorageProcessingMessage msg = new StorageProcessingMessage().setMessage(message); StorageProcessingMessage msg = new StorageProcessingMessage().setMessage(message);
HookParams params = new HookParams()
.add(RequestDetails.class, theRequest) IInterceptorBroadcaster compositeBroadcaster =
.addIfMatchesType(ServletRequestDetails.class, theRequest) CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
.add(StorageProcessingMessage.class, msg); if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_WARNING)) {
CompositeInterceptorBroadcaster.doCallHooks( HookParams params = new HookParams()
myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_WARNING, params); .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, msg);
compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_WARNING, params);
}
} }
/** /**

View File

@ -109,15 +109,18 @@ public class UriPredicateBuilder extends BaseSearchParamPredicateBuilder {
+ "] param[" + theParamName + "]"; + "] param[" + theParamName + "]";
ourLog.info(msg); ourLog.info(msg);
StorageProcessingMessage message = new StorageProcessingMessage(); IInterceptorBroadcaster compositeBroadcaster =
ourLog.warn(msg); CompositeInterceptorBroadcaster.newCompositeBroadcaster(
message.setMessage(msg); myInterceptorBroadcaster, theRequestDetails);
HookParams params = new HookParams() if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_WARNING)) {
.add(RequestDetails.class, theRequestDetails) StorageProcessingMessage message = new StorageProcessingMessage();
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails) message.setMessage(msg);
.add(StorageProcessingMessage.class, message); HookParams params = new HookParams()
CompositeInterceptorBroadcaster.doCallHooks( .add(RequestDetails.class, theRequestDetails)
myInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_WARNING, params); .addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(StorageProcessingMessage.class, message);
compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_WARNING, params);
}
long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity( long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(
getPartitionSettings(), getRequestPartitionId(), getResourceType(), theParamName); getPartitionSettings(), getRequestPartitionId(), getResourceType(), theParamName);

View File

@ -26,7 +26,6 @@ import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.api.dao.IDao;
import ca.uhn.fhir.jpa.dao.IResultIterator; import ca.uhn.fhir.jpa.dao.IResultIterator;
import ca.uhn.fhir.jpa.dao.ISearchBuilder; import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory; import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
@ -95,7 +94,6 @@ public class SearchTask implements Callable<Void> {
protected final FhirContext myContext; protected final FhirContext myContext;
protected final ISearchResultCacheSvc mySearchResultCacheSvc; protected final ISearchResultCacheSvc mySearchResultCacheSvc;
private final SearchParameterMap myParams; private final SearchParameterMap myParams;
private final IDao myCallingDao;
private final String myResourceType; private final String myResourceType;
private final ArrayList<JpaPid> mySyncedPids = new ArrayList<>(); private final ArrayList<JpaPid> mySyncedPids = new ArrayList<>();
private final CountDownLatch myInitialCollectionLatch = new CountDownLatch(1); private final CountDownLatch myInitialCollectionLatch = new CountDownLatch(1);
@ -113,6 +111,7 @@ public class SearchTask implements Callable<Void> {
private final JpaStorageSettings myStorageSettings; private final JpaStorageSettings myStorageSettings;
private final ISearchCacheSvc mySearchCacheSvc; private final ISearchCacheSvc mySearchCacheSvc;
private final IPagingProvider myPagingProvider; private final IPagingProvider myPagingProvider;
private final IInterceptorBroadcaster myCompositeBroadcaster;
private Search mySearch; private Search mySearch;
private boolean myAbortRequested; private boolean myAbortRequested;
private int myCountSavedTotal = 0; private int myCountSavedTotal = 0;
@ -149,7 +148,6 @@ public class SearchTask implements Callable<Void> {
// values // values
myOnRemove = theCreationParams.OnRemove; myOnRemove = theCreationParams.OnRemove;
mySearch = theCreationParams.Search; mySearch = theCreationParams.Search;
myCallingDao = theCreationParams.CallingDao;
myParams = theCreationParams.Params; myParams = theCreationParams.Params;
myResourceType = theCreationParams.ResourceType; myResourceType = theCreationParams.ResourceType;
myRequest = theCreationParams.Request; myRequest = theCreationParams.Request;
@ -158,9 +156,11 @@ public class SearchTask implements Callable<Void> {
myLoadingThrottleForUnitTests = theCreationParams.getLoadingThrottleForUnitTests(); myLoadingThrottleForUnitTests = theCreationParams.getLoadingThrottleForUnitTests();
mySearchRuntimeDetails = new SearchRuntimeDetails(myRequest, mySearch.getUuid()); mySearchRuntimeDetails = new SearchRuntimeDetails(myRequest, mySearch.getUuid());
mySearchRuntimeDetails.setQueryString(myParams.toNormalizedQueryString(myCallingDao.getContext())); mySearchRuntimeDetails.setQueryString(myParams.toNormalizedQueryString(myContext));
myRequestPartitionId = theCreationParams.RequestPartitionId; myRequestPartitionId = theCreationParams.RequestPartitionId;
myParentTransaction = ElasticApm.currentTransaction(); myParentTransaction = ElasticApm.currentTransaction();
myCompositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, myRequest);
} }
protected RequestPartitionId getRequestPartitionId() { protected RequestPartitionId getRequestPartitionId() {
@ -203,7 +203,7 @@ public class SearchTask implements Callable<Void> {
private ISearchBuilder newSearchBuilder() { private ISearchBuilder newSearchBuilder() {
Class<? extends IBaseResource> resourceTypeClass = Class<? extends IBaseResource> resourceTypeClass =
myContext.getResourceDefinition(myResourceType).getImplementingClass(); myContext.getResourceDefinition(myResourceType).getImplementingClass();
return mySearchBuilderFactory.newSearchBuilder(myCallingDao, myResourceType, resourceTypeClass); return mySearchBuilderFactory.newSearchBuilder(myResourceType, resourceTypeClass);
} }
@Nonnull @Nonnull
@ -280,7 +280,7 @@ public class SearchTask implements Callable<Void> {
.withRequest(myRequest) .withRequest(myRequest)
.withRequestPartitionId(myRequestPartitionId) .withRequestPartitionId(myRequestPartitionId)
.withPropagation(Propagation.REQUIRES_NEW) .withPropagation(Propagation.REQUIRES_NEW)
.execute(() -> doSaveSearch()); .execute(this::doSaveSearch);
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@ -307,8 +307,7 @@ public class SearchTask implements Callable<Void> {
.add(RequestDetails.class, mySearchRuntimeDetails.getRequestDetails()) .add(RequestDetails.class, mySearchRuntimeDetails.getRequestDetails())
.addIfMatchesType( .addIfMatchesType(
ServletRequestDetails.class, mySearchRuntimeDetails.getRequestDetails()); ServletRequestDetails.class, mySearchRuntimeDetails.getRequestDetails());
CompositeInterceptorBroadcaster.doCallHooks( myCompositeBroadcaster.callHooks(Pointcut.STORAGE_PREACCESS_RESOURCES, params);
myInterceptorBroadcaster, myRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
for (int i = unsyncedPids.size() - 1; i >= 0; i--) { for (int i = unsyncedPids.size() - 1; i >= 0; i--) {
if (accessDetails.isDontReturnResourceAtIndex(i)) { if (accessDetails.isDontReturnResourceAtIndex(i)) {
@ -454,15 +453,13 @@ public class SearchTask implements Callable<Void> {
.add(RequestDetails.class, myRequest) .add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest) .addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(SearchRuntimeDetails.class, mySearchRuntimeDetails); .add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
CompositeInterceptorBroadcaster.doCallHooks( myCompositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_SEARCH_COMPLETE, params);
myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_COMPLETE, params);
} else { } else {
HookParams params = new HookParams() HookParams params = new HookParams()
.add(RequestDetails.class, myRequest) .add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest) .addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(SearchRuntimeDetails.class, mySearchRuntimeDetails); .add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
CompositeInterceptorBroadcaster.doCallHooks( myCompositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_SEARCH_PASS_COMPLETE, params);
myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_PASS_COMPLETE, params);
} }
ourLog.trace( ourLog.trace(
@ -516,8 +513,7 @@ public class SearchTask implements Callable<Void> {
.add(RequestDetails.class, myRequest) .add(RequestDetails.class, myRequest)
.addIfMatchesType(ServletRequestDetails.class, myRequest) .addIfMatchesType(ServletRequestDetails.class, myRequest)
.add(SearchRuntimeDetails.class, mySearchRuntimeDetails); .add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
CompositeInterceptorBroadcaster.doCallHooks( myCompositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_SEARCH_FAILED, params);
myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FAILED, params);
saveSearch(); saveSearch();
span.captureException(t); span.captureException(t);

View File

@ -214,9 +214,7 @@ public class JpaBulkExportProcessorTest {
when(myBulkExportHelperService.createSearchParameterMapsForResourceType(any(RuntimeResourceDefinition.class), eq(parameters), any(boolean.class))) when(myBulkExportHelperService.createSearchParameterMapsForResourceType(any(RuntimeResourceDefinition.class), eq(parameters), any(boolean.class)))
.thenReturn(maps); .thenReturn(maps);
// from getSearchBuilderForLocalResourceType // from getSearchBuilderForLocalResourceType
when(myDaoRegistry.getResourceDao(anyString())) when(mySearchBuilderFactory.newSearchBuilder(eq(parameters.getResourceType()), any()))
.thenReturn(mockDao);
when(mySearchBuilderFactory.newSearchBuilder(eq(mockDao), eq(parameters.getResourceType()), any()))
.thenReturn(searchBuilder); .thenReturn(searchBuilder);
// ret // ret
when(searchBuilder.createQuery( when(searchBuilder.createQuery(
@ -304,9 +302,7 @@ public class JpaBulkExportProcessorTest {
when(myBulkExportHelperService.createSearchParameterMapsForResourceType(any(RuntimeResourceDefinition.class), eq(parameters), any(boolean.class))) when(myBulkExportHelperService.createSearchParameterMapsForResourceType(any(RuntimeResourceDefinition.class), eq(parameters), any(boolean.class)))
.thenReturn(Collections.singletonList(new SearchParameterMap())); .thenReturn(Collections.singletonList(new SearchParameterMap()));
// from getSearchBuilderForLocalResourceType // from getSearchBuilderForLocalResourceType
when(myDaoRegistry.getResourceDao(not(eq("Group")))) when(mySearchBuilderFactory.newSearchBuilder(eq(parameters.getResourceType()), any()))
.thenReturn(mockDao);
when(mySearchBuilderFactory.newSearchBuilder(eq(mockDao), eq(parameters.getResourceType()), any()))
.thenReturn(searchBuilder); .thenReturn(searchBuilder);
// ret // ret
when(searchBuilder.createQuery( when(searchBuilder.createQuery(
@ -432,9 +428,7 @@ public class JpaBulkExportProcessorTest {
when(myIdHelperService.getPidOrNull(eq(getPartitionIdFromParams(thePartitioned)), eq(groupResource))) when(myIdHelperService.getPidOrNull(eq(getPartitionIdFromParams(thePartitioned)), eq(groupResource)))
.thenReturn(groupId); .thenReturn(groupId);
// getMembersFromGroupWithFilter // getMembersFromGroupWithFilter
when(myDaoRegistry.getResourceDao(eq("Patient"))) when(mySearchBuilderFactory.newSearchBuilder(eq("Patient"), eq(Patient.class)))
.thenReturn(patientDao);
when(mySearchBuilderFactory.newSearchBuilder(eq(patientDao), eq("Patient"), eq(Patient.class)))
.thenReturn(patientSearchBuilder); .thenReturn(patientSearchBuilder);
RuntimeResourceDefinition patientDef = myFhirContext.getResourceDefinition("Patient"); RuntimeResourceDefinition patientDef = myFhirContext.getResourceDefinition("Patient");
SearchParameterMap patientSpMap = new SearchParameterMap(); SearchParameterMap patientSpMap = new SearchParameterMap();
@ -447,9 +441,7 @@ public class JpaBulkExportProcessorTest {
RuntimeResourceDefinition observationDef = myFhirContext.getResourceDefinition("Observation"); RuntimeResourceDefinition observationDef = myFhirContext.getResourceDefinition("Observation");
when(myBulkExportHelperService.createSearchParameterMapsForResourceType(eq(observationDef), eq(parameters), any(boolean.class))) when(myBulkExportHelperService.createSearchParameterMapsForResourceType(eq(observationDef), eq(parameters), any(boolean.class)))
.thenReturn(Collections.singletonList(observationSpMap)); .thenReturn(Collections.singletonList(observationSpMap));
when(myDaoRegistry.getResourceDao((eq("Observation")))) when(mySearchBuilderFactory.newSearchBuilder(eq("Observation"), eq(Observation.class)))
.thenReturn(observationDao);
when(mySearchBuilderFactory.newSearchBuilder(eq(observationDao), eq("Observation"), eq(Observation.class)))
.thenReturn(observationSearchBuilder); .thenReturn(observationSearchBuilder);
when(observationSearchBuilder.loadIncludes( when(observationSearchBuilder.loadIncludes(
any(SearchBuilderLoadIncludesParameters.class) any(SearchBuilderLoadIncludesParameters.class)
@ -520,10 +512,7 @@ public class JpaBulkExportProcessorTest {
any(ExportPIDIteratorParameters.class), any(ExportPIDIteratorParameters.class),
any(boolean.class) any(boolean.class)
)).thenReturn(Collections.singletonList(new SearchParameterMap())); )).thenReturn(Collections.singletonList(new SearchParameterMap()));
when(myDaoRegistry.getResourceDao(eq("Patient")))
.thenReturn(dao);
when(mySearchBuilderFactory.newSearchBuilder( when(mySearchBuilderFactory.newSearchBuilder(
any(IFhirResourceDao.class),
anyString(), anyString(),
any() any()
)).thenReturn(searchBuilder); )).thenReturn(searchBuilder);

View File

@ -941,8 +941,9 @@ public class SearchParamExtractorService {
if (myPartitionSettings.getAllowReferencesAcrossPartitions() == ALLOWED_UNQUALIFIED) { if (myPartitionSettings.getAllowReferencesAcrossPartitions() == ALLOWED_UNQUALIFIED) {
// Interceptor: Pointcut.JPA_CROSS_PARTITION_REFERENCE_DETECTED // Interceptor: Pointcut.JPA_CROSS_PARTITION_REFERENCE_DETECTED
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.JPA_RESOLVE_CROSS_PARTITION_REFERENCE, myInterceptorBroadcaster, theRequest)) { CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.JPA_RESOLVE_CROSS_PARTITION_REFERENCE)) {
CrossPartitionReferenceDetails referenceDetails = new CrossPartitionReferenceDetails( CrossPartitionReferenceDetails referenceDetails = new CrossPartitionReferenceDetails(
theRequestPartitionId, theRequestPartitionId,
theSourceResourceName, theSourceResourceName,
@ -950,12 +951,8 @@ public class SearchParamExtractorService {
theRequest, theRequest,
theTransactionDetails); theTransactionDetails);
HookParams params = new HookParams(referenceDetails); HookParams params = new HookParams(referenceDetails);
targetResource = targetResource = (IResourceLookup<JpaPid>) compositeBroadcaster.callHooksAndReturnObject(
(IResourceLookup<JpaPid>) CompositeInterceptorBroadcaster.doCallHooksAndReturnObject( Pointcut.JPA_RESOLVE_CROSS_PARTITION_REFERENCE, params);
myInterceptorBroadcaster,
theRequest,
Pointcut.JPA_RESOLVE_CROSS_PARTITION_REFERENCE,
params);
} else { } else {
targetResource = myResourceLinkResolver.findTargetResource( targetResource = myResourceLinkResolver.findTargetResource(
RequestPartitionId.allPartitions(), RequestPartitionId.allPartitions(),
@ -1089,8 +1086,9 @@ public class SearchParamExtractorService {
} }
// If extraction generated any warnings, broadcast an error // If extraction generated any warnings, broadcast an error
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.JPA_PERFTRACE_WARNING, theInterceptorBroadcaster, theRequestDetails)) { CompositeInterceptorBroadcaster.newCompositeBroadcaster(theInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_WARNING)) {
for (String next : theSearchParamSet.getWarnings()) { for (String next : theSearchParamSet.getWarnings()) {
StorageProcessingMessage messageHolder = new StorageProcessingMessage(); StorageProcessingMessage messageHolder = new StorageProcessingMessage();
messageHolder.setMessage(next); messageHolder.setMessage(next);
@ -1098,8 +1096,7 @@ public class SearchParamExtractorService {
.add(RequestDetails.class, theRequestDetails) .add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails) .addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(StorageProcessingMessage.class, messageHolder); .add(StorageProcessingMessage.class, messageHolder);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_WARNING, params);
theInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_WARNING, params);
} }
} }
} }

View File

@ -1,14 +1,20 @@
package ca.uhn.fhir.jpa.searchparam.extractor; package ca.uhn.fhir.jpa.searchparam.extractor;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.test.utilities.MockInvoker;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
@ -36,14 +42,17 @@ public class SearchParamExtractorServiceTest {
searchParamSet.addWarning("help i'm a bug"); searchParamSet.addWarning("help i'm a bug");
searchParamSet.addWarning("Spiff"); searchParamSet.addWarning("Spiff");
when(myJpaInterceptorBroadcaster.hasHooks(any())).thenReturn(true); AtomicInteger counter = new AtomicInteger();
when(myJpaInterceptorBroadcaster.callHooks(any(), any())).thenReturn(true);
when(myJpaInterceptorBroadcaster.hasHooks(eq(Pointcut.JPA_PERFTRACE_WARNING))).thenReturn(true);
when(myJpaInterceptorBroadcaster.getInvokersForPointcut(eq(Pointcut.JPA_PERFTRACE_WARNING))).thenReturn(MockInvoker.list((Consumer<HookParams>) params->counter.incrementAndGet()));
ServletRequestDetails requestDetails = new ServletRequestDetails(myRequestInterceptorBroadcaster); ServletRequestDetails requestDetails = new ServletRequestDetails(myRequestInterceptorBroadcaster);
SearchParamExtractorService.handleWarnings(requestDetails, myJpaInterceptorBroadcaster, searchParamSet); SearchParamExtractorService.handleWarnings(requestDetails, myJpaInterceptorBroadcaster, searchParamSet);
verify(myJpaInterceptorBroadcaster, times(2)).callHooks(eq(Pointcut.JPA_PERFTRACE_WARNING), any()); verify(myJpaInterceptorBroadcaster, times(3)).hasHooks(eq(Pointcut.JPA_PERFTRACE_WARNING));
verify(myRequestInterceptorBroadcaster, times(2)).callHooks(eq(Pointcut.JPA_PERFTRACE_WARNING), any()); verify(myRequestInterceptorBroadcaster, times(2)).hasHooks(eq(Pointcut.JPA_PERFTRACE_WARNING));
assertEquals(2, counter.get());
} }
} }

View File

@ -118,12 +118,15 @@ public class SubscriptionMatcherInterceptor {
ResourceModifiedMessage msg = createResourceModifiedMessage(theNewResource, theOperationType, theRequest); ResourceModifiedMessage msg = createResourceModifiedMessage(theNewResource, theOperationType, theRequest);
// Interceptor call: SUBSCRIPTION_RESOURCE_MODIFIED // Interceptor call: SUBSCRIPTION_RESOURCE_MODIFIED
HookParams params = new HookParams().add(ResourceModifiedMessage.class, msg); IInterceptorBroadcaster compositeBroadcaster =
boolean outcome = CompositeInterceptorBroadcaster.doCallHooks( CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
myInterceptorBroadcaster, theRequest, Pointcut.SUBSCRIPTION_RESOURCE_MODIFIED, params); if (compositeBroadcaster.hasHooks(Pointcut.SUBSCRIPTION_RESOURCE_MODIFIED)) {
HookParams params = new HookParams().add(ResourceModifiedMessage.class, msg);
boolean outcome = compositeBroadcaster.callHooks(Pointcut.SUBSCRIPTION_RESOURCE_MODIFIED, params);
if (!outcome) { if (!outcome) {
return; return;
}
} }
processResourceModifiedMessage(msg); processResourceModifiedMessage(msg);

View File

@ -383,8 +383,8 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc
String resourceType = myFhirContext.getResourceType(theJobDetails.getCurrentSearchResourceType()); String resourceType = myFhirContext.getResourceType(theJobDetails.getCurrentSearchResourceType());
RuntimeResourceDefinition resourceDef = RuntimeResourceDefinition resourceDef =
myFhirContext.getResourceDefinition(theJobDetails.getCurrentSearchResourceType()); myFhirContext.getResourceDefinition(theJobDetails.getCurrentSearchResourceType());
ISearchBuilder searchBuilder = mySearchBuilderFactory.newSearchBuilder( ISearchBuilder searchBuilder =
resourceDao, resourceType, resourceDef.getImplementingClass()); mySearchBuilderFactory.newSearchBuilder(resourceType, resourceDef.getImplementingClass());
List<IBaseResource> listToPopulate = new ArrayList<>(); List<IBaseResource> listToPopulate = new ArrayList<>();
myTransactionService.withRequest(null).execute(() -> { myTransactionService.withRequest(null).execute(() -> {

View File

@ -113,7 +113,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
myClient.create().resource(subs).execute(); myClient.create().resource(subs).execute();
fail(""); fail("");
} catch (UnprocessableEntityException e) { } catch (UnprocessableEntityException e) {
assertThat(e.getMessage()).contains("Unknown SubscriptionStatus code 'aaaaa'"); assertThat(e.getMessage()).containsAnyOf("invalid value aaaaa", "Unknown SubscriptionStatus code 'aaaaa'");
} }
} }

View File

@ -52,7 +52,7 @@ public class BaseSearchSvc {
protected static final FhirContext ourCtx = FhirContext.forDstu3Cached(); protected static final FhirContext ourCtx = FhirContext.forDstu3Cached();
public void after() { public void after() {
verify(mySearchBuilderFactory, atMost(myExpectedNumberOfSearchBuildersCreated)).newSearchBuilder(any(), any(), any()); verify(mySearchBuilderFactory, atMost(myExpectedNumberOfSearchBuildersCreated)).newSearchBuilder(any(), any());
} }
protected List<JpaPid> createPidSequence(int to) { protected List<JpaPid> createPidSequence(int to) {

View File

@ -76,6 +76,7 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
@ -91,7 +92,7 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
@Mock @Mock
private ISearchResultCacheSvc mySearchResultCacheSvc; private ISearchResultCacheSvc mySearchResultCacheSvc;
private Search myCurrentSearch; private Search myCurrentSearch;
@Mock @Mock(strictness = Mock.Strictness.STRICT_STUBS)
private IInterceptorBroadcaster myInterceptorBroadcaster; private IInterceptorBroadcaster myInterceptorBroadcaster;
@Mock @Mock
private SearchBuilderFactory<JpaPid> mySearchBuilderFactory; private SearchBuilderFactory<JpaPid> mySearchBuilderFactory;
@ -289,7 +290,7 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
} }
private void initSearches() { private void initSearches() {
when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())).thenReturn(mySearchBuilder); when(mySearchBuilderFactory.newSearchBuilder(any(), any())).thenReturn(mySearchBuilder);
} }
private void initAsyncSearches() { private void initAsyncSearches() {
@ -318,8 +319,8 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
SlowIterator iter = new SlowIterator(pids.iterator(), 500); SlowIterator iter = new SlowIterator(pids.iterator(), 500);
when(mySearchBuilder.createQuery(same(params), any(), any(), nullable(RequestPartitionId.class))).thenReturn(iter); when(mySearchBuilder.createQuery(same(params), any(), any(), nullable(RequestPartitionId.class))).thenReturn(iter);
mockSearchTask(); mockSearchTask();
when(myInterceptorBroadcaster.callHooks(any(), any())) when(myInterceptorBroadcaster.hasHooks(any())).thenReturn(true);
.thenReturn(true); when(myInterceptorBroadcaster.getInvokersForPointcut(any())).thenReturn(List.of());
ourLog.info("Registering the first search"); ourLog.info("Registering the first search");
new Thread(() -> mySvc.registerSearch(myCallingDao, params, "Patient", new CacheControlDirective(), null, RequestPartitionId.allPartitions())).start(); new Thread(() -> mySvc.registerSearch(myCallingDao, params, "Patient", new CacheControlDirective(), null, RequestPartitionId.allPartitions())).start();
@ -437,7 +438,7 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
@Test @Test
public void testLoadSearchResultsFromDifferentCoordinator() { public void testLoadSearchResultsFromDifferentCoordinator() {
when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())).thenReturn(mySearchBuilder); when(mySearchBuilderFactory.newSearchBuilder(any(), any())).thenReturn(mySearchBuilder);
final String uuid = UUID.randomUUID().toString(); final String uuid = UUID.randomUUID().toString();
@ -517,7 +518,7 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
@Test @Test
public void testSynchronousSearch() { public void testSynchronousSearch() {
when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())).thenReturn(mySearchBuilder); when(mySearchBuilderFactory.newSearchBuilder(any(), any())).thenReturn(mySearchBuilder);
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronous(true); params.setLoadSynchronous(true);
@ -531,7 +532,7 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
@Test @Test
public void testSynchronousSearchWithOffset() { public void testSynchronousSearchWithOffset() {
when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())).thenReturn(mySearchBuilder); when(mySearchBuilderFactory.newSearchBuilder(any(), any())).thenReturn(mySearchBuilder);
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
params.setOffset(10); params.setOffset(10);
@ -544,7 +545,7 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
@Test @Test
public void testSynchronousSearchUpTo() { public void testSynchronousSearchUpTo() {
when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())).thenReturn(mySearchBuilder); when(mySearchBuilderFactory.newSearchBuilder(any(), any())).thenReturn(mySearchBuilder);
int loadUpto = 30; int loadUpto = 30;
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
@ -584,7 +585,6 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
@Test @Test
public void testFetchAllResultsReturnsNull() { public void testFetchAllResultsReturnsNull() {
when(myDaoRegistry.getResourceDao(anyString())).thenReturn(myCallingDao); when(myDaoRegistry.getResourceDao(anyString())).thenReturn(myCallingDao);
when(myCallingDao.getContext()).thenReturn(ourCtx);
Search search = new Search(); Search search = new Search();
search.setUuid("0000-1111"); search.setUuid("0000-1111");

View File

@ -41,7 +41,7 @@ public class SynchronousSearchSvcImplTest extends BaseSearchSvc {
@Test @Test
public void testSynchronousSearch() { public void testSynchronousSearch() {
when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())) when(mySearchBuilderFactory.newSearchBuilder(any(), any()))
.thenReturn(mySearchBuilder); .thenReturn(mySearchBuilder);
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
@ -65,7 +65,7 @@ public class SynchronousSearchSvcImplTest extends BaseSearchSvc {
@Test @Test
public void testSynchronousSearchWithOffset() { public void testSynchronousSearchWithOffset() {
when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())).thenReturn(mySearchBuilder); when(mySearchBuilderFactory.newSearchBuilder(any(), any())).thenReturn(mySearchBuilder);
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
params.setCount(10); params.setCount(10);
@ -87,7 +87,7 @@ public class SynchronousSearchSvcImplTest extends BaseSearchSvc {
@Test @Test
public void testSynchronousSearchUpTo() { public void testSynchronousSearchUpTo() {
when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())).thenReturn(mySearchBuilder); when(mySearchBuilderFactory.newSearchBuilder(any(), any())).thenReturn(mySearchBuilder);
when(myStorageSettings.getDefaultTotalMode()).thenReturn(null); when(myStorageSettings.getDefaultTotalMode()).thenReturn(null);
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();

View File

@ -15,6 +15,7 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
public class TerminologySvcImplDstu2Test extends BaseJpaDstu2Test { public class TerminologySvcImplDstu2Test extends BaseJpaDstu2Test {
@ -29,6 +30,8 @@ public class TerminologySvcImplDstu2Test extends BaseJpaDstu2Test {
List<FhirVersionIndependentConcept> concepts; List<FhirVersionIndependentConcept> concepts;
Set<String> codes; Set<String> codes;
when(mySrd.getInterceptorBroadcaster()).thenReturn(null);
ValueSet upload = new ValueSet(); ValueSet upload = new ValueSet();
upload.setId(new IdDt("testVs")); upload.setId(new IdDt("testVs"));
upload.setUrl("http://myVs"); upload.setUrl("http://myVs");
@ -61,6 +64,8 @@ public class TerminologySvcImplDstu2Test extends BaseJpaDstu2Test {
List<FhirVersionIndependentConcept> concepts; List<FhirVersionIndependentConcept> concepts;
Set<String> codes; Set<String> codes;
when(mySrd.getInterceptorBroadcaster()).thenReturn(null);
ValueSet upload = new ValueSet(); ValueSet upload = new ValueSet();
upload.setId(new IdDt("testVs")); upload.setId(new IdDt("testVs"));
upload.setUrl("http://myVs"); upload.setUrl("http://myVs");

View File

@ -33,6 +33,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy;
import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.HttpClientExtension;
import ca.uhn.fhir.test.utilities.MockInvoker;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import ca.uhn.fhir.util.JsonUtil; import ca.uhn.fhir.util.JsonUtil;
import ca.uhn.fhir.util.SearchParameterUtil; import ca.uhn.fhir.util.SearchParameterUtil;
@ -1054,18 +1055,18 @@ public class BulkDataExportProviderR4Test {
AtomicBoolean initiateCalled = new AtomicBoolean(false); AtomicBoolean initiateCalled = new AtomicBoolean(false);
// when // when
when(myInterceptorBroadcaster.callHooks(eq(Pointcut.STORAGE_PRE_INITIATE_BULK_EXPORT), any(HookParams.class))) when(myInterceptorBroadcaster.hasHooks(eq(Pointcut.STORAGE_PRE_INITIATE_BULK_EXPORT))).thenReturn(true);
.thenAnswer((args) -> { when(myInterceptorBroadcaster.hasHooks(eq(Pointcut.STORAGE_INITIATE_BULK_EXPORT))).thenReturn(true);
when(myInterceptorBroadcaster.getInvokersForPointcut(eq(Pointcut.STORAGE_PRE_INITIATE_BULK_EXPORT))).thenReturn(MockInvoker.list(params->{
assertFalse(initiateCalled.get()); assertFalse(initiateCalled.get());
assertFalse(preInitiateCalled.getAndSet(true)); assertFalse(preInitiateCalled.getAndSet(true));
return true; return true;
}); }));
when(myInterceptorBroadcaster.callHooks(eq(Pointcut.STORAGE_INITIATE_BULK_EXPORT), any(HookParams.class))) when(myInterceptorBroadcaster.getInvokersForPointcut(eq(Pointcut.STORAGE_INITIATE_BULK_EXPORT))).thenReturn(MockInvoker.list(params->{
.thenAnswer((args) -> {
assertTrue(preInitiateCalled.get()); assertTrue(preInitiateCalled.get());
assertFalse(initiateCalled.getAndSet(true)); assertFalse(initiateCalled.getAndSet(true));
return true; return true;
}); }));
when(myJobCoordinator.startInstance(isNotNull(), any())) when(myJobCoordinator.startInstance(isNotNull(), any()))
.thenReturn(createJobStartResponse()); .thenReturn(createJobStartResponse());

View File

@ -30,6 +30,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy;
import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.HttpClientExtension;
import ca.uhn.fhir.test.utilities.MockInvoker;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import ca.uhn.fhir.util.JsonUtil; import ca.uhn.fhir.util.JsonUtil;
import ca.uhn.fhir.util.SearchParameterUtil; import ca.uhn.fhir.util.SearchParameterUtil;
@ -1057,18 +1058,18 @@ public class BulkDataExportProviderR5Test {
AtomicBoolean initiateCalled = new AtomicBoolean(false); AtomicBoolean initiateCalled = new AtomicBoolean(false);
// when // when
when(myInterceptorBroadcaster.callHooks(eq(Pointcut.STORAGE_PRE_INITIATE_BULK_EXPORT), any(HookParams.class))) when(myInterceptorBroadcaster.hasHooks(eq(Pointcut.STORAGE_PRE_INITIATE_BULK_EXPORT))).thenReturn(true);
.thenAnswer((args) -> { when(myInterceptorBroadcaster.getInvokersForPointcut(eq(Pointcut.STORAGE_PRE_INITIATE_BULK_EXPORT))).thenReturn(MockInvoker.list(params -> {
assertFalse(initiateCalled.get()); assertFalse(initiateCalled.get());
assertFalse(preInitiateCalled.getAndSet(true)); assertFalse(preInitiateCalled.getAndSet(true));
return true; return true;
}); }));
when(myInterceptorBroadcaster.callHooks(eq(Pointcut.STORAGE_INITIATE_BULK_EXPORT), any(HookParams.class))) when(myInterceptorBroadcaster.hasHooks(eq(Pointcut.STORAGE_INITIATE_BULK_EXPORT))).thenReturn(true);
.thenAnswer((args) -> { when(myInterceptorBroadcaster.getInvokersForPointcut(eq(Pointcut.STORAGE_INITIATE_BULK_EXPORT))).thenReturn(MockInvoker.list(params -> {
assertTrue(preInitiateCalled.get()); assertTrue(preInitiateCalled.get());
assertFalse(initiateCalled.getAndSet(true)); assertFalse(initiateCalled.getAndSet(true));
return true; return true;
}); }));
when(myJobCoordinator.startInstance(isNotNull(), any())) when(myJobCoordinator.startInstance(isNotNull(), any()))
.thenReturn(createJobStartResponse()); .thenReturn(createJobStartResponse());

View File

@ -320,7 +320,7 @@ class BaseHapiFhirResourceDaoTest {
mySvc.setTransactionService(myTransactionService); mySvc.setTransactionService(myTransactionService);
when(myRequestPartitionHelperSvc.determineReadPartitionForRequestForSearchType(any(), any(), any(), any())).thenReturn(mock(RequestPartitionId.class)); when(myRequestPartitionHelperSvc.determineReadPartitionForRequestForSearchType(any(), any(), any(), any())).thenReturn(mock(RequestPartitionId.class));
when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())).thenReturn(myISearchBuilder); when(mySearchBuilderFactory.newSearchBuilder(any(), any())).thenReturn(myISearchBuilder);
when(myISearchBuilder.createQuery(any(), any(), any(), any())).thenReturn(mock(IResultIterator.class)); when(myISearchBuilder.createQuery(any(), any(), any(), any())).thenReturn(mock(IResultIterator.class));
lenient().when(myStorageSettings.getInternalSynchronousSearchSize()).thenReturn(5000); lenient().when(myStorageSettings.getInternalSynchronousSearchSize()).thenReturn(5000);

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.jpa.dao; package ca.uhn.fhir.jpa.dao;
import static org.junit.jupiter.api.Assertions.assertNull;
/*- /*-
* #%L * #%L
* HAPI FHIR JPA Server * HAPI FHIR JPA Server
@ -25,15 +24,23 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.sl.cache.Cache; import ca.uhn.fhir.sl.cache.Cache;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
@ -41,13 +48,18 @@ import org.springframework.test.util.ReflectionTestUtils;
import java.util.function.Function; import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.hl7.fhir.common.hapi.validation.support.ValidationConstants.LOINC_LOW; import static org.hl7.fhir.common.hapi.validation.support.ValidationConstants.LOINC_LOW;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.isA; import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class JpaPersistedResourceValidationSupportTest { class JpaPersistedResourceValidationSupportTest {
@ -115,5 +127,68 @@ class JpaPersistedResourceValidationSupportTest {
} }
@Nested
class FetchStructureDefinitionTests {
@Mock
private DaoRegistry myDaoRegistry;
@InjectMocks
private final JpaPersistedResourceValidationSupport testClass = new JpaPersistedResourceValidationSupport(theFhirContext);
@Captor
ArgumentCaptor<SearchParameterMap> searchParameterMapCaptor;
@Test
@DisplayName("fetch StructureDefinition by version less url")
void fetchStructureDefinitionForUrl() {
final String profileUrl = "http://example.com/fhir/StructureDefinition/exampleProfile";
IFhirResourceDao mockDao = mock(IFhirResourceDao.class);
when(mockDao.search(any())).thenReturn(mock(IBundleProvider.class));
when(myDaoRegistry.getResourceDao(anyString())).thenReturn(mockDao);
testClass.fetchResource(StructureDefinition.class, profileUrl);
verify(mockDao).search(searchParameterMapCaptor.capture());
SearchParameterMap searchParams = searchParameterMapCaptor.getValue();
String uriParam = searchParams.get(StructureDefinition.SP_URL)
.get(0)
.stream()
.map(UriParam.class::cast)
.map(UriParam::getValue)
.findFirst()
.orElse(null);
assertThat(uriParam).isEqualTo(profileUrl);
}
@Test
@DisplayName("fetch StructureDefinition by versioned url")
void fetchStructureDefinitionForVersionedUrl() {
final String profileUrl = "http://example.com/fhir/StructureDefinition/exampleProfile|1.1.0";
IFhirResourceDao mockDao = mock(IFhirResourceDao.class);
when(mockDao.search(any())).thenReturn(mock(IBundleProvider.class));
when(myDaoRegistry.getResourceDao(anyString())).thenReturn(mockDao);
testClass.fetchResource(StructureDefinition.class, profileUrl);
verify(mockDao).search(searchParameterMapCaptor.capture());
SearchParameterMap searchParams = searchParameterMapCaptor.getValue();
String uriParam = searchParams.get(StructureDefinition.SP_URL)
.get(0)
.stream()
.map(UriParam.class::cast)
.map(UriParam::getValue)
.findFirst()
.orElse(null);
assertThat(uriParam).isEqualTo("http://example.com/fhir/StructureDefinition/exampleProfile");
String versionParam = searchParams.get(StructureDefinition.SP_VERSION)
.get(0)
.stream()
.map(TokenParam.class::cast)
.map(TokenParam::getValue)
.findFirst()
.orElse(null);
assertThat(versionParam).isEqualTo("1.1.0");
}
}
} }

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.jpa.dao.r4; package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
@ -10,9 +9,9 @@ import ca.uhn.fhir.jpa.search.reindex.ResourceReindexingSvcImpl;
import ca.uhn.fhir.jpa.test.BaseJpaR4Test; import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
import ca.uhn.fhir.jpa.util.SpringObjectCaster; import ca.uhn.fhir.jpa.util.SpringObjectCaster;
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
import ca.uhn.fhir.test.utilities.MockInvoker;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.mockito.ArgumentMatchers;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -47,24 +46,22 @@ public abstract class BaseComboParamsR4Test extends BaseJpaR4Test {
when(myInterceptorBroadcaster.hasHooks(eq(Pointcut.JPA_PERFTRACE_WARNING))).thenReturn(true); when(myInterceptorBroadcaster.hasHooks(eq(Pointcut.JPA_PERFTRACE_WARNING))).thenReturn(true);
when(myInterceptorBroadcaster.hasHooks(eq(Pointcut.JPA_PERFTRACE_INFO))).thenReturn(true); when(myInterceptorBroadcaster.hasHooks(eq(Pointcut.JPA_PERFTRACE_INFO))).thenReturn(true);
when(myInterceptorBroadcaster.callHooks(eq(Pointcut.JPA_PERFTRACE_INFO), ArgumentMatchers.any(HookParams.class))).thenAnswer(t -> { when(myInterceptorBroadcaster.hasHooks(eq(Pointcut.JPA_PERFTRACE_SEARCH_REUSING_CACHED))).thenReturn(true);
HookParams params = t.getArgument(1, HookParams.class); when(myInterceptorBroadcaster.hasHooks(eq(Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH))).thenReturn(true);
when(myInterceptorBroadcaster.getInvokersForPointcut(eq(Pointcut.JPA_PERFTRACE_INFO))).thenReturn(MockInvoker.list(params->{
myMessages.add("INFO " + params.get(StorageProcessingMessage.class).getMessage()); myMessages.add("INFO " + params.get(StorageProcessingMessage.class).getMessage());
return null; }));
});
when(myInterceptorBroadcaster.callHooks(eq(Pointcut.JPA_PERFTRACE_WARNING), ArgumentMatchers.any(HookParams.class))).thenAnswer(t -> {
HookParams params = t.getArgument(1, HookParams.class); when(myInterceptorBroadcaster.getInvokersForPointcut(eq(Pointcut.JPA_PERFTRACE_WARNING))).thenReturn(MockInvoker.list(params->{
myMessages.add("WARN " + params.get(StorageProcessingMessage.class).getMessage()); myMessages.add("WARN " + params.get(StorageProcessingMessage.class).getMessage());
return null; }));
}); when(myInterceptorBroadcaster.getInvokersForPointcut(eq(Pointcut.JPA_PERFTRACE_SEARCH_REUSING_CACHED))).thenReturn(MockInvoker.list(params->{
when(myInterceptorBroadcaster.callHooks(eq(Pointcut.JPA_PERFTRACE_SEARCH_REUSING_CACHED), ArgumentMatchers.any(HookParams.class))).thenAnswer(t -> {
HookParams params = t.getArgument(1, HookParams.class);
myMessages.add("REUSING CACHED SEARCH"); myMessages.add("REUSING CACHED SEARCH");
return null; }));
});
// allow searches to use cached results // allow searches to use cached results
when(myInterceptorBroadcaster.callHooks(eq(Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH), ArgumentMatchers.any(HookParams.class))).thenReturn(true); when(myInterceptorBroadcaster.getInvokersForPointcut(eq(Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH))).thenReturn(MockInvoker.list(params->true));
} }
@AfterEach @AfterEach
@ -83,4 +80,5 @@ public abstract class BaseComboParamsR4Test extends BaseJpaR4Test {
ourLog.info("Messages:\n {}", String.join("\n ", myMessages)); ourLog.info("Messages:\n {}", String.join("\n ", myMessages));
} }
} }

View File

@ -94,7 +94,7 @@ public class ValidateWithRemoteTerminologyTest extends BaseResourceProviderR4Tes
final String classSystem = "http://terminology.hl7.org/CodeSystem/v3-ActCode"; final String classSystem = "http://terminology.hl7.org/CodeSystem/v3-ActCode";
final String identifierTypeSystem = "http://terminology.hl7.org/CodeSystem/v2-0203"; final String identifierTypeSystem = "http://terminology.hl7.org/CodeSystem/v2-0203";
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/encounter-status", "http://hl7.org/fhir/encounter-status", statusCode, "validation/encounter/validateCode-ValueSet-encounter-status.json"); setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/encounter-status", "4.0.1", "http://hl7.org/fhir/encounter-status", statusCode, "validation/encounter/validateCode-ValueSet-encounter-status.json");
setupValueSetValidateCode("http://terminology.hl7.org/ValueSet/v3-ActEncounterCode", "http://terminology.hl7.org/CodeSystem/v3-ActCode", classCode, "validation/encounter/validateCode-ValueSet-v3-ActEncounterCode.json"); setupValueSetValidateCode("http://terminology.hl7.org/ValueSet/v3-ActEncounterCode", "http://terminology.hl7.org/CodeSystem/v3-ActCode", classCode, "validation/encounter/validateCode-ValueSet-v3-ActEncounterCode.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/identifier-type", "http://hl7.org/fhir/identifier-type", identifierTypeCode, "validation/encounter/validateCode-ValueSet-identifier-type.json"); setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/identifier-type", "http://hl7.org/fhir/identifier-type", identifierTypeCode, "validation/encounter/validateCode-ValueSet-identifier-type.json");
@ -138,7 +138,7 @@ public class ValidateWithRemoteTerminologyTest extends BaseResourceProviderR4Tes
final String loincSystem = "http://loinc.org"; final String loincSystem = "http://loinc.org";
final String system = "http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM"; final String system = "http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM";
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/observation-status", statusSystem, statusCode, "validation/observation/validateCode-ValueSet-observation-status.json"); setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/observation-status", "4.0.1", statusSystem, statusCode, "validation/observation/validateCode-ValueSet-observation-status.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/observation-codes", loincSystem, statusCode, "validation/observation/validateCode-ValueSet-codes.json"); setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/observation-codes", loincSystem, statusCode, "validation/observation/validateCode-ValueSet-codes.json");
setupCodeSystemValidateCode(statusSystem, statusCode, "validation/observation/validateCode-CodeSystem-observation-status.json"); setupCodeSystemValidateCode(statusSystem, statusCode, "validation/observation/validateCode-CodeSystem-observation-status.json");
@ -171,7 +171,7 @@ public class ValidateWithRemoteTerminologyTest extends BaseResourceProviderR4Tes
final String statusSystem = "http://hl7.org/fhir/event-status"; final String statusSystem = "http://hl7.org/fhir/event-status";
final String snomedSystem = "http://snomed.info/sct"; final String snomedSystem = "http://snomed.info/sct";
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/event-status", statusSystem, statusCode, "validation/procedure/validateCode-ValueSet-event-status.json"); setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/event-status", "4.0.1", statusSystem, statusCode, "validation/procedure/validateCode-ValueSet-event-status.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode1, "validation/procedure/validateCode-ValueSet-procedure-code-valid.json"); setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode1, "validation/procedure/validateCode-ValueSet-procedure-code-valid.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode2, "validation/procedure/validateCode-ValueSet-procedure-code-invalid.json"); setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode2, "validation/procedure/validateCode-ValueSet-procedure-code-invalid.json");
@ -213,7 +213,7 @@ public class ValidateWithRemoteTerminologyTest extends BaseResourceProviderR4Tes
final String snomedSystem = "http://snomed.info/sct"; final String snomedSystem = "http://snomed.info/sct";
final String absentUnknownSystem = "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips"; final String absentUnknownSystem = "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips";
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/event-status", statusSystem, statusCode, "validation/procedure/validateCode-ValueSet-event-status.json"); setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/event-status", "4.0.1", statusSystem, statusCode, "validation/procedure/validateCode-ValueSet-event-status.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode, "validation/procedure/validateCode-ValueSet-procedure-code-invalid-slice.json"); setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode, "validation/procedure/validateCode-ValueSet-procedure-code-invalid-slice.json");
setupValueSetValidateCode("http://hl7.org/fhir/uv/ips/ValueSet/absent-or-unknown-procedures-uv-ips", absentUnknownSystem, procedureCode, "validation/procedure/validateCode-ValueSet-absent-or-unknown-procedure.json"); setupValueSetValidateCode("http://hl7.org/fhir/uv/ips/ValueSet/absent-or-unknown-procedures-uv-ips", absentUnknownSystem, procedureCode, "validation/procedure/validateCode-ValueSet-absent-or-unknown-procedure.json");
@ -245,6 +245,15 @@ public class ValidateWithRemoteTerminologyTest extends BaseResourceProviderR4Tes
// which also attempts a validateCode against the CodeSystem after the validateCode against the ValueSet // which also attempts a validateCode against the CodeSystem after the validateCode against the ValueSet
} }
private void setupValueSetValidateCode(String theUrl, String theVersion, String theSystem, String theCode, String theTerminologyResponseFile) {
ValueSet valueSet = myValueSetProvider.addTerminologyResource(theUrl, theVersion);
myValueSetProvider.addTerminologyResource(theSystem, theVersion);
myValueSetProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, valueSet.getUrl(), theCode, ourCtx, theTerminologyResponseFile);
valueSet.getCompose().addInclude().setSystem(theSystem);
}
private void setupCodeSystemValidateCode(String theUrl, String theCode, String theTerminologyResponseFile) { private void setupCodeSystemValidateCode(String theUrl, String theCode, String theTerminologyResponseFile) {
CodeSystem codeSystem = myCodeSystemProvider.addTerminologyResource(theUrl); CodeSystem codeSystem = myCodeSystemProvider.addTerminologyResource(theUrl);
myCodeSystemProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, codeSystem.getUrl(), theCode, ourCtx, theTerminologyResponseFile); myCodeSystemProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, codeSystem.getUrl(), theCode, ourCtx, theTerminologyResponseFile);

View File

@ -90,7 +90,7 @@ public class MdmReadVirtualizationInterceptor<P extends IResourcePersistentId<?>
@Hook( @Hook(
value = Pointcut.STORAGE_PRESEARCH_REGISTERED, value = Pointcut.STORAGE_PRESEARCH_REGISTERED,
order = MdmConstants.ORDER_PRESEARCH_REGISTERED_MDM_READ_VIRTUALIZATION_INTERCEPTOR) order = MdmConstants.ORDER_PRESEARCH_REGISTERED_MDM_READ_VIRTUALIZATION_INTERCEPTOR)
public void hook(RequestDetails theRequestDetails, SearchParameterMap theSearchParameterMap) { public void preSearchRegistered(RequestDetails theRequestDetails, SearchParameterMap theSearchParameterMap) {
ourMdmTroubleshootingLog ourMdmTroubleshootingLog
.atTrace() .atTrace()
.setMessage("MDM virtualization original search: {}{}") .setMessage("MDM virtualization original search: {}{}")

View File

@ -85,14 +85,15 @@ public class MdmSearchExpansionSvc {
// Try to detect if the RequestDetails is being reused across multiple different queries, which // Try to detect if the RequestDetails is being reused across multiple different queries, which
// can happen during CQL measure evaluation // can happen during CQL measure evaluation
String resourceName = theRequestDetails.getResourceName(); {
String queryString = theSearchParameterMap.toNormalizedQueryString(myFhirContext); String resourceName = theRequestDetails.getResourceName();
if (!Objects.equals(resourceName, theRequestDetails.getUserData().get(RESOURCE_NAME)) String queryString = theSearchParameterMap.toNormalizedQueryString(myFhirContext);
|| !Objects.equals(queryString, theRequestDetails.getUserData().get(QUERY_STRING))) { if (!Objects.equals(resourceName, theRequestDetails.getUserData().get(RESOURCE_NAME))
theRequestDetails.getUserData().remove(EXPANSION_RESULTS); || !Objects.equals(
queryString, theRequestDetails.getUserData().get(QUERY_STRING))) {
theRequestDetails.getUserData().remove(EXPANSION_RESULTS);
}
} }
theRequestDetails.getUserData().put(RESOURCE_NAME, resourceName);
theRequestDetails.getUserData().put(QUERY_STRING, queryString);
MdmSearchExpansionResults expansionResults = getCachedExpansionResults(theRequestDetails); MdmSearchExpansionResults expansionResults = getCachedExpansionResults(theRequestDetails);
if (expansionResults != null) { if (expansionResults != null) {
@ -123,6 +124,15 @@ public class MdmSearchExpansionSvc {
theRequestDetails.getUserData().put(EXPANSION_RESULTS, expansionResults); theRequestDetails.getUserData().put(EXPANSION_RESULTS, expansionResults);
/*
* Note: Do this at the end so that the query string reflects the post-translated
* query string
*/
String resourceName = theRequestDetails.getResourceName();
String queryString = theSearchParameterMap.toNormalizedQueryString(myFhirContext);
theRequestDetails.getUserData().put(RESOURCE_NAME, resourceName);
theRequestDetails.getUserData().put(QUERY_STRING, queryString);
return expansionResults; return expansionResults;
} }

View File

@ -92,7 +92,7 @@ public class MdmSearchParamSvc {
public ISearchBuilder generateSearchBuilderForType(String theSourceType) { public ISearchBuilder generateSearchBuilderForType(String theSourceType) {
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theSourceType); IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theSourceType);
return mySearchBuilderFactory.newSearchBuilder(resourceDao, theSourceType, resourceDao.getResourceType()); return mySearchBuilderFactory.newSearchBuilder(theSourceType, resourceDao.getResourceType());
} }
/** /**

View File

@ -41,6 +41,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List; import java.util.List;
import static java.util.Objects.nonNull; import static java.util.Objects.nonNull;
@ -241,6 +242,11 @@ public class SystemRequestDetails extends RequestDetails {
public boolean hasHooks(Pointcut thePointcut) { public boolean hasHooks(Pointcut thePointcut) {
return false; return false;
} }
@Override
public List<IInvoker> getInvokersForPointcut(Pointcut thePointcut) {
return Collections.emptyList();
}
} }
public static SystemRequestDetails forAllPartitions() { public static SystemRequestDetails forAllPartitions() {

View File

@ -686,7 +686,7 @@ public class RestfulServerUtils {
if (context.getVersion().getVersion() != theForVersion) { if (context.getVersion().getVersion() != theForVersion) {
context = myFhirContextMap.get(theForVersion); context = myFhirContextMap.get(theForVersion);
if (context == null) { if (context == null) {
context = theForVersion.newContext(); context = FhirContext.forVersion(theForVersion);
myFhirContextMap.put(theForVersion, context); myFhirContextMap.put(theForVersion, context);
} }
} }

View File

@ -54,17 +54,20 @@ public class ServerInterceptorUtil {
// Interceptor call: STORAGE_PRESHOW_RESOURCE // Interceptor call: STORAGE_PRESHOW_RESOURCE
// This can be used to remove results from the search result details before // This can be used to remove results from the search result details before
// the user has a chance to know that they were in the results // the user has a chance to know that they were in the results
if (retVal.size() > 0) { if (!retVal.isEmpty()) {
SimplePreResourceShowDetails accessDetails = new SimplePreResourceShowDetails(retVal); IInterceptorBroadcaster compositeBroadcaster =
HookParams params = new HookParams() CompositeInterceptorBroadcaster.newCompositeBroadcaster(theInterceptorBroadcaster, theRequest);
.add(IPreResourceShowDetails.class, accessDetails) if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRESHOW_RESOURCES)) {
.add(RequestDetails.class, theRequest) SimplePreResourceShowDetails accessDetails = new SimplePreResourceShowDetails(retVal);
.addIfMatchesType(ServletRequestDetails.class, theRequest); HookParams params = new HookParams()
CompositeInterceptorBroadcaster.doCallHooks( .add(IPreResourceShowDetails.class, accessDetails)
theInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params); .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
compositeBroadcaster.callHooks(Pointcut.STORAGE_PRESHOW_RESOURCES, params);
retVal = accessDetails.toList(); retVal = accessDetails.toList();
retVal.removeIf(Objects::isNull); retVal.removeIf(Objects::isNull);
}
} }
return retVal; return retVal;

View File

@ -19,97 +19,105 @@
*/ */
package ca.uhn.fhir.rest.server.util; package ca.uhn.fhir.rest.server.util;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.executor.BaseInterceptorService;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable; import jakarta.annotation.Nullable;
public class CompositeInterceptorBroadcaster { import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
/**
* This is an {@link IInterceptorBroadcaster} which combines multiple interceptor
* broadcasters. Hook methods are called across all broadcasters, respecting
* the {@link Hook#order()} across all broadcasters.
*/
public class CompositeInterceptorBroadcaster implements IInterceptorBroadcaster {
private final List<IInterceptorBroadcaster> myServices;
/** /**
* Non instantiable * Constructor
*/ */
private CompositeInterceptorBroadcaster() { private CompositeInterceptorBroadcaster(Collection<IInterceptorBroadcaster> theServices) {
// nothing myServices = theServices.stream().filter(t -> t != null).collect(Collectors.toList());
}
@Override
public boolean callHooks(Pointcut thePointcut, HookParams theParams) {
assert BaseInterceptorService.haveAppropriateParams(thePointcut, theParams);
assert thePointcut.getReturnType() == void.class
|| thePointcut.getReturnType() == thePointcut.getBooleanReturnTypeForEnum();
List<IInvoker> invokers = getInvokersForPointcut(thePointcut);
Object retVal = BaseInterceptorService.callInvokers(thePointcut, theParams, invokers);
retVal = defaultIfNull(retVal, true);
return (Boolean) retVal;
}
@Override
public Object callHooksAndReturnObject(Pointcut thePointcut, HookParams theParams) {
assert BaseInterceptorService.haveAppropriateParams(thePointcut, theParams);
assert thePointcut.getReturnType() != void.class;
List<IInvoker> invokers = getInvokersForPointcut(thePointcut);
return BaseInterceptorService.callInvokers(thePointcut, theParams, invokers);
}
@Override
@Nonnull
public List<IInvoker> getInvokersForPointcut(Pointcut thePointcut) {
List<IInvoker> invokers = new ArrayList<>();
for (IInterceptorBroadcaster services : myServices) {
if (services.hasHooks(thePointcut)) {
List<IInvoker> serviceInvokers = services.getInvokersForPointcut(thePointcut);
assert serviceInvokers != null;
invokers.addAll(serviceInvokers);
}
}
invokers.sort(Comparator.naturalOrder());
return invokers;
}
@Override
public boolean hasHooks(Pointcut thePointcut) {
for (IInterceptorBroadcaster service : myServices) {
if (service.hasHooks(thePointcut)) {
return true;
}
}
return false;
} }
/** /**
* Broadcast hooks to both the interceptor service associated with the request, as well * @since 8.0.0
* as the one associated with the JPA module.
*/ */
public static boolean doCallHooks( public static IInterceptorBroadcaster newCompositeBroadcaster(IInterceptorBroadcaster... theServices) {
IInterceptorBroadcaster theInterceptorBroadcaster, return new CompositeInterceptorBroadcaster(Arrays.asList(theServices));
@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);
}
// TODO: JA - Refactor to make thePointcut the last argument in order to be consistent with thr other methods here
public static boolean hasHooks(
Pointcut thePointcut, IInterceptorBroadcaster theInterceptorBroadcaster, RequestDetails theRequestDetails) {
return newCompositeBroadcaster(theInterceptorBroadcaster, theRequestDetails)
.hasHooks(thePointcut);
} }
/** /**
* @since 5.5.0 * @since 5.5.0
*/ */
public static IInterceptorBroadcaster newCompositeBroadcaster( public static IInterceptorBroadcaster newCompositeBroadcaster(
IInterceptorBroadcaster theInterceptorBroadcaster, RequestDetails theRequestDetails) { @Nonnull IInterceptorBroadcaster theInterceptorBroadcaster, @Nullable RequestDetails theRequestDetails) {
return new IInterceptorBroadcaster() { if (theRequestDetails != null) {
@Override IInterceptorBroadcaster requestBroadcaster = theRequestDetails.getInterceptorBroadcaster();
public boolean callHooks(Pointcut thePointcut, HookParams theParams) { if (requestBroadcaster != null) {
boolean retVal = true; return newCompositeBroadcaster(theInterceptorBroadcaster, requestBroadcaster);
if (theInterceptorBroadcaster != null) {
retVal = theInterceptorBroadcaster.callHooks(thePointcut, theParams);
}
if (theRequestDetails != null && theRequestDetails.getInterceptorBroadcaster() != null && retVal) {
IInterceptorBroadcaster interceptorBroadcaster = theRequestDetails.getInterceptorBroadcaster();
retVal = interceptorBroadcaster.callHooks(thePointcut, theParams);
}
return retVal;
} }
}
@Override return newCompositeBroadcaster(theInterceptorBroadcaster);
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);
}
};
} }
} }

View File

@ -1,161 +1,314 @@
package ca.uhn.fhir.rest.server.util; package ca.uhn.fhir.rest.server.util;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IBaseInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.executor.InterceptorService;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import com.google.common.collect.MultimapBuilder;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@SuppressWarnings("unchecked")
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class CompositeInterceptorBroadcasterTest { class CompositeInterceptorBroadcasterTest {
@SuppressWarnings("rawtypes")
private static final Class BOOLEAN_CLASS = boolean.class;
private final List<Integer> myOrders = new ArrayList<>();
@Mock @Mock
private IInterceptorBroadcaster myModuleBroadcasterMock; private IInterceptorBroadcaster myModuleBroadcasterMock;
@Mock @Mock
private IInterceptorBroadcaster myReqDetailsBroadcasterMock; private IInterceptorBroadcaster myReqDetailsBroadcasterMock;
@Mock @Mock
private IBaseInterceptorBroadcaster.IInvoker myModuleBroadcasterInvokerMock;
@Mock
private IBaseInterceptorBroadcaster.IInvoker myReqDetailsInvokerMock;
@Mock
private Pointcut myPointcutMock; private Pointcut myPointcutMock;
@Mock @Mock
private HookParams myHookParamsMock; private HookParams myHookParamsMock;
@Mock @Mock
private RequestDetails myRequestDetailsMock; private RequestDetails myRequestDetailsMock;
@Test @Test
void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_RequestDetailsBroadcasterReturnsTrue_ThenReturnsTrue() { void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_RequestDetailsBroadcasterReturnsTrue_ThenReturnsTrue() {
when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock); when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock);
when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true); when(myPointcutMock.getReturnType()).thenReturn(BOOLEAN_CLASS);
when(myReqDetailsBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true); when(myPointcutMock.getBooleanReturnTypeForEnum()).thenReturn(BOOLEAN_CLASS);
when(myModuleBroadcasterMock.hasHooks(eq(myPointcutMock))).thenReturn(true);
when(myModuleBroadcasterMock.getInvokersForPointcut(eq(myPointcutMock))).thenReturn(List.of(myModuleBroadcasterInvokerMock));
when(myReqDetailsBroadcasterMock.hasHooks(eq(myPointcutMock))).thenReturn(true);
when(myReqDetailsBroadcasterMock.getInvokersForPointcut(eq(myPointcutMock))).thenReturn(List.of(myReqDetailsInvokerMock));
when(myPointcutMock.getParameterTypes()).thenReturn(List.of());
when(myHookParamsMock.getParamsForType()).thenReturn(MultimapBuilder.hashKeys().arrayListValues().build());
when(myModuleBroadcasterInvokerMock.invoke(eq(myHookParamsMock))).thenReturn(true);
when(myReqDetailsInvokerMock.invoke(eq(myHookParamsMock))).thenReturn(true);
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock, IInterceptorBroadcaster interceptorBroadcaster = CompositeInterceptorBroadcaster
myPointcutMock, myHookParamsMock); .newCompositeBroadcaster(myModuleBroadcasterMock, myRequestDetailsMock);
boolean retVal = interceptorBroadcaster.callHooks(myPointcutMock, myHookParamsMock);
assertThat(retVal).isTrue(); assertThat(retVal).isTrue();
verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock); verify(myModuleBroadcasterInvokerMock, times(1)).invoke(eq(myHookParamsMock));
verify(myReqDetailsBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock); verify(myReqDetailsInvokerMock, times(1)).invoke(eq(myHookParamsMock));
} }
@SuppressWarnings("unchecked")
@Test @Test
void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_RequestDetailsBroadcasterReturnsFalse_ThenReturnsFalse() { void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_RequestDetailsBroadcasterReturnsFalse_ThenReturnsFalse() {
when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock); when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock);
when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true); when(myPointcutMock.getReturnType()).thenReturn(BOOLEAN_CLASS);
when(myReqDetailsBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false); when(myPointcutMock.getBooleanReturnTypeForEnum()).thenReturn(BOOLEAN_CLASS);
when(myModuleBroadcasterMock.hasHooks(eq(myPointcutMock))).thenReturn(true);
when(myModuleBroadcasterMock.getInvokersForPointcut(eq(myPointcutMock))).thenReturn(List.of(myModuleBroadcasterInvokerMock));
when(myReqDetailsBroadcasterMock.hasHooks(eq(myPointcutMock))).thenReturn(true);
when(myReqDetailsBroadcasterMock.getInvokersForPointcut(eq(myPointcutMock))).thenReturn(List.of(myReqDetailsInvokerMock));
when(myPointcutMock.getParameterTypes()).thenReturn(List.of());
when(myHookParamsMock.getParamsForType()).thenReturn(MultimapBuilder.hashKeys().arrayListValues().build());
when(myModuleBroadcasterInvokerMock.invoke(eq(myHookParamsMock))).thenReturn(true);
when(myReqDetailsInvokerMock.invoke(eq(myHookParamsMock))).thenReturn(false);
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock, IInterceptorBroadcaster interceptorBroadcaster = CompositeInterceptorBroadcaster
myPointcutMock, myHookParamsMock); .newCompositeBroadcaster(myModuleBroadcasterMock, myRequestDetailsMock);
boolean retVal = interceptorBroadcaster.callHooks(myPointcutMock, myHookParamsMock);
assertThat(retVal).isFalse(); assertThat(retVal).isFalse();
verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock); verify(myModuleBroadcasterInvokerMock).invoke(eq(myHookParamsMock));
verify(myReqDetailsBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock); verify(myReqDetailsInvokerMock).invoke(eq(myHookParamsMock));
} }
@Test @Test
void doCallHooks_WhenModuleBroadcasterReturnsFalse_ThenSkipsBroadcasterInRequestDetails_And_ReturnsFalse() { void doCallHooks_WhenModuleBroadcasterReturnsFalse_ThenSkipsBroadcasterInRequestDetails_And_ReturnsFalse() {
when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock); when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock);
when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false); when(myPointcutMock.getReturnType()).thenReturn(BOOLEAN_CLASS);
when(myPointcutMock.getBooleanReturnTypeForEnum()).thenReturn(BOOLEAN_CLASS);
when(myModuleBroadcasterMock.hasHooks(eq(myPointcutMock))).thenReturn(true);
when(myModuleBroadcasterMock.getInvokersForPointcut(eq(myPointcutMock))).thenReturn(List.of(myModuleBroadcasterInvokerMock));
when(myReqDetailsBroadcasterMock.hasHooks(eq(myPointcutMock))).thenReturn(true);
when(myReqDetailsBroadcasterMock.getInvokersForPointcut(eq(myPointcutMock))).thenReturn(List.of(myReqDetailsInvokerMock));
when(myPointcutMock.getParameterTypes()).thenReturn(List.of());
when(myHookParamsMock.getParamsForType()).thenReturn(MultimapBuilder.hashKeys().arrayListValues().build());
when(myModuleBroadcasterInvokerMock.invoke(eq(myHookParamsMock))).thenReturn(false);
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock, IInterceptorBroadcaster interceptorBroadcaster = CompositeInterceptorBroadcaster
myPointcutMock, myHookParamsMock); .newCompositeBroadcaster(myModuleBroadcasterMock, myRequestDetailsMock);
boolean retVal = interceptorBroadcaster.callHooks(myPointcutMock, myHookParamsMock);
assertThat(retVal).isFalse(); assertThat(retVal).isFalse();
verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock); verify(myModuleBroadcasterInvokerMock, times(1)).invoke(eq(myHookParamsMock));
verify(myReqDetailsBroadcasterMock, never()).callHooks(myPointcutMock, myHookParamsMock); verify(myReqDetailsInvokerMock, never()).invoke(eq(myHookParamsMock));
} }
@Test @Test
void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_NullRequestDetailsBroadcaster_ThenReturnsTrue() { void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_NullRequestDetailsBroadcaster_ThenReturnsTrue() {
when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true); when(myPointcutMock.getReturnType()).thenReturn(BOOLEAN_CLASS);
when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(null); when(myPointcutMock.getBooleanReturnTypeForEnum()).thenReturn(BOOLEAN_CLASS);
when(myModuleBroadcasterMock.hasHooks(eq(myPointcutMock))).thenReturn(true);
when(myModuleBroadcasterMock.getInvokersForPointcut(eq(myPointcutMock))).thenReturn(List.of(myModuleBroadcasterInvokerMock));
when(myPointcutMock.getParameterTypes()).thenReturn(List.of());
when(myHookParamsMock.getParamsForType()).thenReturn(MultimapBuilder.hashKeys().arrayListValues().build());
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock, myPointcutMock, IInterceptorBroadcaster interceptorBroadcaster = CompositeInterceptorBroadcaster
myHookParamsMock); .newCompositeBroadcaster(myModuleBroadcasterMock, myRequestDetailsMock);
boolean retVal = interceptorBroadcaster.callHooks(myPointcutMock, myHookParamsMock);
assertThat(retVal).isTrue(); assertThat(retVal).isTrue();
verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock); verify(myModuleBroadcasterInvokerMock, times(1)).invoke(eq(myHookParamsMock));
verify(myReqDetailsInvokerMock, never()).invoke(eq(myHookParamsMock));
} }
@SuppressWarnings("unchecked")
@Test @Test
void doCallHooks_WhenModuleBroadcasterReturnsFalse_And_NullRequestDetailsBroadcaster_ThenReturnsFalse() { void doCallHooks_WhenModuleBroadcasterReturnsFalse_And_NullRequestDetailsBroadcaster_ThenReturnsFalse() {
when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false); when(myPointcutMock.getReturnType()).thenReturn(BOOLEAN_CLASS);
when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(null); when(myPointcutMock.getBooleanReturnTypeForEnum()).thenReturn(BOOLEAN_CLASS);
when(myModuleBroadcasterMock.hasHooks(eq(myPointcutMock))).thenReturn(true);
when(myModuleBroadcasterMock.getInvokersForPointcut(eq(myPointcutMock))).thenReturn(List.of(myModuleBroadcasterInvokerMock));
when(myPointcutMock.getParameterTypes()).thenReturn(List.of());
when(myHookParamsMock.getParamsForType()).thenReturn(MultimapBuilder.hashKeys().arrayListValues().build());
when(myModuleBroadcasterInvokerMock.invoke(eq(myHookParamsMock))).thenReturn(false);
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock, myPointcutMock, IInterceptorBroadcaster interceptorBroadcaster = CompositeInterceptorBroadcaster
myHookParamsMock); .newCompositeBroadcaster(myModuleBroadcasterMock, myRequestDetailsMock);
boolean retVal = interceptorBroadcaster.callHooks(myPointcutMock, myHookParamsMock);
assertThat(retVal).isFalse(); assertThat(retVal).isFalse();
verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock); verify(myModuleBroadcasterInvokerMock, times(1)).invoke(eq(myHookParamsMock));
} }
@SuppressWarnings("unchecked")
@Test @Test
void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_NullRequestDetails_ThenReturnsTrue() { void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_NullRequestDetails_ThenReturnsTrue() {
when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true); when(myPointcutMock.getReturnType()).thenReturn(BOOLEAN_CLASS);
when(myPointcutMock.getBooleanReturnTypeForEnum()).thenReturn(BOOLEAN_CLASS);
when(myModuleBroadcasterMock.hasHooks(eq(myPointcutMock))).thenReturn(true);
when(myModuleBroadcasterMock.getInvokersForPointcut(eq(myPointcutMock))).thenReturn(List.of(myModuleBroadcasterInvokerMock));
when(myPointcutMock.getParameterTypes()).thenReturn(List.of());
when(myHookParamsMock.getParamsForType()).thenReturn(MultimapBuilder.hashKeys().arrayListValues().build());
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, null, myPointcutMock, myHookParamsMock); IInterceptorBroadcaster interceptorBroadcaster = CompositeInterceptorBroadcaster
.newCompositeBroadcaster(myModuleBroadcasterMock, null);
boolean retVal = interceptorBroadcaster.callHooks(myPointcutMock, myHookParamsMock);
assertThat(retVal).isTrue(); assertThat(retVal).isTrue();
verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock); verify(myModuleBroadcasterInvokerMock, times(1)).invoke(eq(myHookParamsMock));
verify(myReqDetailsInvokerMock, never()).invoke(eq(myHookParamsMock));
} }
@SuppressWarnings("unchecked")
@Test @Test
void doCallHooks_WhenModuleBroadcasterReturnsFalse_And_NullRequestDetails_ThenReturnsFalse() { void doCallHooks_WhenModuleBroadcasterReturnsFalse_And_NullRequestDetails_ThenReturnsFalse() {
when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false); when(myPointcutMock.getReturnType()).thenReturn(BOOLEAN_CLASS);
when(myPointcutMock.getBooleanReturnTypeForEnum()).thenReturn(BOOLEAN_CLASS);
when(myPointcutMock.getParameterTypes()).thenReturn(List.of());
when(myHookParamsMock.getParamsForType()).thenReturn(MultimapBuilder.hashKeys().arrayListValues().build());
when(myModuleBroadcasterMock.hasHooks(eq(myPointcutMock))).thenReturn(true);
when(myModuleBroadcasterMock.getInvokersForPointcut(eq(myPointcutMock))).thenReturn(List.of(myModuleBroadcasterInvokerMock));
when(myModuleBroadcasterInvokerMock.invoke(eq(myHookParamsMock))).thenReturn(false);
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, null, myPointcutMock, myHookParamsMock); IInterceptorBroadcaster interceptorBroadcaster = CompositeInterceptorBroadcaster
.newCompositeBroadcaster(myModuleBroadcasterMock, null);
boolean retVal = interceptorBroadcaster.callHooks(myPointcutMock, myHookParamsMock);
assertThat(retVal).isFalse(); assertThat(retVal).isFalse();
verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock); verify(myModuleBroadcasterInvokerMock).invoke(eq(myHookParamsMock));
} }
@Test @Test
void doCallHooks_WhenNullModuleBroadcaster_And_NullRequestDetails_ThenReturnsTrue() { void doCallHooks_WhenNullModuleBroadcaster_And_NullRequestDetails_ThenReturnsTrue() {
when(myPointcutMock.getParameterTypes()).thenReturn(List.of());
when(myHookParamsMock.getParamsForType()).thenReturn(MultimapBuilder.hashKeys().arrayListValues().build());
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(null, null, myPointcutMock, myHookParamsMock); IInterceptorBroadcaster interceptorBroadcaster = CompositeInterceptorBroadcaster
.newCompositeBroadcaster();
boolean retVal = interceptorBroadcaster.callHooks(myPointcutMock, myHookParamsMock);
assertThat(retVal).isTrue(); assertThat(retVal).isTrue();
} }
@Test @ParameterizedTest
void doCallHooks_WhenNullModuleBroadcaster_And_RequestDetailsBroadcasterReturnsTrue_ThenReturnsTrue() { @CsvSource({
when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock); "0, 1, 2",
when(myReqDetailsBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true); "1, 0, 2",
"2, 0, 1"
})
public void testCompositeBroadcasterBroadcastsInOrder(int theIndex0, int theIndex1, int theIndex2) {
// Setup
InterceptorService svc0 = new InterceptorService();
svc0.registerInterceptor(new Interceptor0());
InterceptorService svc1 = new InterceptorService();
svc1.registerInterceptor(new Interceptor1());
InterceptorService svc2 = new InterceptorService();
svc2.registerInterceptor(new Interceptor2());
InterceptorService[] services = new InterceptorService[]{svc0, svc1, svc2};
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(null, myRequestDetailsMock, myPointcutMock, myHookParamsMock); List<IInterceptorBroadcaster> serviceList = List.of(
services[theIndex0], services[theIndex1], services[theIndex2]
);
IInterceptorBroadcaster compositeBroadcaster = CompositeInterceptorBroadcaster.newCompositeBroadcaster(serviceList.toArray(new IInterceptorBroadcaster[0]));
assertThat(retVal).isTrue(); assertTrue(compositeBroadcaster.hasHooks(Pointcut.TEST_RO));
verify(myReqDetailsBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock); assertFalse(compositeBroadcaster.hasHooks(Pointcut.TEST_RB));
// Test
HookParams hookParams = new HookParams()
.add(String.class, "PARAM_A")
.add(String.class, "PARAM_B");
Object outcome = compositeBroadcaster.callHooksAndReturnObject(Pointcut.TEST_RO, hookParams);
// Verify
assertNull(outcome);
assertThat(myOrders).asList().containsExactly(
-2, -1, 0, 1, 2, 3
);
} }
@Test @Interceptor
void doCallHooks_WhenNullModuleBroadcaster_And_RequestDetailsBroadcasterReturnsFalse_ThenReturnsFalse() { private class Interceptor0 {
when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock);
when(myReqDetailsBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false);
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(null, myRequestDetailsMock, myPointcutMock, myHookParamsMock); @Hook(value = Pointcut.TEST_RO, order = 0)
public BaseServerResponseException hook0() {
myOrders.add(0);
return null;
}
@Hook(value = Pointcut.TEST_RO, order = 2)
public BaseServerResponseException hook2() {
myOrders.add(2);
return null;
}
assertThat(retVal).isFalse();
verify(myReqDetailsBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
} }
@Interceptor
private class Interceptor1 {
@Hook(value = Pointcut.TEST_RO, order = 1)
public BaseServerResponseException hook1() {
myOrders.add(1);
return null;
}
@Hook(value = Pointcut.TEST_RO, order = 3)
public BaseServerResponseException hook3() {
myOrders.add(3);
return null;
}
}
@Interceptor
private class Interceptor2 {
@Hook(value = Pointcut.TEST_RO, order = -1)
public BaseServerResponseException hookMinus1() {
myOrders.add(-1);
return null;
}
@Hook(value = Pointcut.TEST_RO, order = -2)
public BaseServerResponseException hookMinus2() {
myOrders.add(-2);
return null;
}
}
} }

View File

@ -173,15 +173,15 @@ public class BulkDataExportProvider {
expandParameters(theRequestDetails, theOptions); expandParameters(theRequestDetails, theOptions);
// permission check // permission check
HookParams initiateBulkExportHookParams = (new HookParams()) IInterceptorBroadcaster compositeBroadcaster =
.add(BulkExportJobParameters.class, theOptions) CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
.add(RequestDetails.class, theRequestDetails) if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_INITIATE_BULK_EXPORT)) {
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); HookParams initiateBulkExportHookParams = (new HookParams())
CompositeInterceptorBroadcaster.doCallHooks( .add(BulkExportJobParameters.class, theOptions)
this.myInterceptorBroadcaster, .add(RequestDetails.class, theRequestDetails)
theRequestDetails, .addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
Pointcut.STORAGE_INITIATE_BULK_EXPORT, compositeBroadcaster.callHooks(Pointcut.STORAGE_INITIATE_BULK_EXPORT, initiateBulkExportHookParams);
initiateBulkExportHookParams); }
// get cache boolean // get cache boolean
boolean useCache = shouldUseCache(theRequestDetails); boolean useCache = shouldUseCache(theRequestDetails);
@ -220,15 +220,15 @@ public class BulkDataExportProvider {
theOptions.setPartitionId(partitionId); theOptions.setPartitionId(partitionId);
// call hook so any other parameter manipulation can be done // call hook so any other parameter manipulation can be done
HookParams preInitiateBulkExportHookParams = new HookParams(); IInterceptorBroadcaster compositeBroadcaster =
preInitiateBulkExportHookParams.add(BulkExportJobParameters.class, theOptions); CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
preInitiateBulkExportHookParams.add(RequestDetails.class, theRequestDetails); if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRE_INITIATE_BULK_EXPORT)) {
preInitiateBulkExportHookParams.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); HookParams preInitiateBulkExportHookParams = new HookParams();
CompositeInterceptorBroadcaster.doCallHooks( preInitiateBulkExportHookParams.add(BulkExportJobParameters.class, theOptions);
myInterceptorBroadcaster, preInitiateBulkExportHookParams.add(RequestDetails.class, theRequestDetails);
theRequestDetails, preInitiateBulkExportHookParams.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
Pointcut.STORAGE_PRE_INITIATE_BULK_EXPORT, compositeBroadcaster.callHooks(Pointcut.STORAGE_PRE_INITIATE_BULK_EXPORT, preInitiateBulkExportHookParams);
preInitiateBulkExportHookParams); }
} }
private boolean shouldUseCache(ServletRequestDetails theRequestDetails) { private boolean shouldUseCache(ServletRequestDetails theRequestDetails) {

View File

@ -78,13 +78,16 @@ public class DeleteExpungeJobSubmitterImpl implements IDeleteExpungeJobSubmitter
Msg.code(820) + "Delete Expunge not allowed: " + myStorageSettings.cannotDeleteExpungeReason()); Msg.code(820) + "Delete Expunge not allowed: " + myStorageSettings.cannotDeleteExpungeReason());
} }
for (String url : theUrlsToDeleteExpunge) { IInterceptorBroadcaster compositeBroadcaster =
HookParams params = new HookParams() CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
.add(RequestDetails.class, theRequestDetails) if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRE_DELETE_EXPUNGE)) {
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails) for (String url : theUrlsToDeleteExpunge) {
.add(String.class, url); HookParams params = new HookParams()
CompositeInterceptorBroadcaster.doCallHooks( .add(RequestDetails.class, theRequestDetails)
myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRE_DELETE_EXPUNGE, params); .addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(String.class, url);
compositeBroadcaster.callHooks(Pointcut.STORAGE_PRE_DELETE_EXPUNGE, params);
}
} }
DeleteExpungeJobParameters deleteExpungeJobParameters = new DeleteExpungeJobParameters(); DeleteExpungeJobParameters deleteExpungeJobParameters = new DeleteExpungeJobParameters();

View File

@ -274,12 +274,15 @@ public class BinaryStorageInterceptor<T extends IPrimitiveType<byte[]>> {
* @return A string, which will be used to prefix the blob ID. May be null. * @return A string, which will be used to prefix the blob ID. May be null.
*/ */
private String invokeAssignBinaryContentPrefix(RequestDetails theRequest, IBaseResource theResource) { private String invokeAssignBinaryContentPrefix(RequestDetails theRequest, IBaseResource theResource) {
// TODO: to be removed when pointcut STORAGE_BINARY_ASSIGN_BLOB_ID_PREFIX has exceeded the grace period IInterceptorBroadcaster compositeBroadcaster =
boolean hasStorageBinaryAssignBlobIdPrefixHooks = CompositeInterceptorBroadcaster.hasHooks( CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
Pointcut.STORAGE_BINARY_ASSIGN_BLOB_ID_PREFIX, myInterceptorBroadcaster, theRequest);
boolean hasStorageBinaryAssignBinaryContentIdPrefixHooks = CompositeInterceptorBroadcaster.hasHooks( // TODO: to be removed when pointcut STORAGE_BINARY_ASSIGN_BLOB_ID_PREFIX has exceeded the grace period
Pointcut.STORAGE_BINARY_ASSIGN_BINARY_CONTENT_ID_PREFIX, myInterceptorBroadcaster, theRequest); boolean hasStorageBinaryAssignBlobIdPrefixHooks =
compositeBroadcaster.hasHooks(Pointcut.STORAGE_BINARY_ASSIGN_BLOB_ID_PREFIX);
boolean hasStorageBinaryAssignBinaryContentIdPrefixHooks =
compositeBroadcaster.hasHooks(Pointcut.STORAGE_BINARY_ASSIGN_BINARY_CONTENT_ID_PREFIX);
if (!(hasStorageBinaryAssignBlobIdPrefixHooks || hasStorageBinaryAssignBinaryContentIdPrefixHooks)) { if (!(hasStorageBinaryAssignBlobIdPrefixHooks || hasStorageBinaryAssignBinaryContentIdPrefixHooks)) {
return null; return null;
@ -297,8 +300,7 @@ public class BinaryStorageInterceptor<T extends IPrimitiveType<byte[]>> {
pointcutToInvoke = Pointcut.STORAGE_BINARY_ASSIGN_BLOB_ID_PREFIX; pointcutToInvoke = Pointcut.STORAGE_BINARY_ASSIGN_BLOB_ID_PREFIX;
} }
return (String) CompositeInterceptorBroadcaster.doCallHooksAndReturnObject( return (String) compositeBroadcaster.callHooksAndReturnObject(pointcutToInvoke, params);
myInterceptorBroadcaster, theRequest, pointcutToInvoke, params);
} }
@Nonnull @Nonnull

View File

@ -174,13 +174,16 @@ public abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
@Nullable @Nullable
private String callBinaryContentIdPointcut( private String callBinaryContentIdPointcut(
byte[] theBytes, RequestDetails theRequestDetails, String theContentType) { byte[] theBytes, RequestDetails theRequestDetails, String theContentType) {
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
// TODO: to be removed when pointcut STORAGE_BINARY_ASSIGN_BLOB_ID_PREFIX has exceeded the grace period. // TODO: to be removed when pointcut STORAGE_BINARY_ASSIGN_BLOB_ID_PREFIX has exceeded the grace period.
// Deprecated in 7.2.0. // Deprecated in 7.2.0.
boolean hasStorageBinaryAssignBlobIdPrefixHooks = CompositeInterceptorBroadcaster.hasHooks( boolean hasStorageBinaryAssignBlobIdPrefixHooks =
Pointcut.STORAGE_BINARY_ASSIGN_BLOB_ID_PREFIX, myInterceptorBroadcaster, theRequestDetails); compositeBroadcaster.hasHooks(Pointcut.STORAGE_BINARY_ASSIGN_BLOB_ID_PREFIX);
boolean hasStorageBinaryAssignBinaryContentIdPrefixHooks = CompositeInterceptorBroadcaster.hasHooks( boolean hasStorageBinaryAssignBinaryContentIdPrefixHooks =
Pointcut.STORAGE_BINARY_ASSIGN_BINARY_CONTENT_ID_PREFIX, myInterceptorBroadcaster, theRequestDetails); compositeBroadcaster.hasHooks(Pointcut.STORAGE_BINARY_ASSIGN_BINARY_CONTENT_ID_PREFIX);
if (!(hasStorageBinaryAssignBlobIdPrefixHooks || hasStorageBinaryAssignBinaryContentIdPrefixHooks)) { if (!(hasStorageBinaryAssignBlobIdPrefixHooks || hasStorageBinaryAssignBinaryContentIdPrefixHooks)) {
return null; return null;
@ -201,8 +204,7 @@ public abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
pointcutToInvoke = Pointcut.STORAGE_BINARY_ASSIGN_BLOB_ID_PREFIX; pointcutToInvoke = Pointcut.STORAGE_BINARY_ASSIGN_BLOB_ID_PREFIX;
} }
return (String) CompositeInterceptorBroadcaster.doCallHooksAndReturnObject( return (String) compositeBroadcaster.callHooksAndReturnObject(pointcutToInvoke, hookParams);
myInterceptorBroadcaster, theRequestDetails, pointcutToInvoke, hookParams);
} }
@Override @Override

View File

@ -335,14 +335,17 @@ public abstract class BaseStorageDao {
// Interceptor broadcast: STORAGE_PREACCESS_RESOURCES // Interceptor broadcast: STORAGE_PREACCESS_RESOURCES
if (outcome.getResource() != null) { if (outcome.getResource() != null) {
SimplePreResourceAccessDetails accessDetails = new SimplePreResourceAccessDetails(outcome.getResource()); SimplePreResourceAccessDetails accessDetails = new SimplePreResourceAccessDetails(outcome.getResource());
HookParams params = new HookParams() IInterceptorBroadcaster compositeBroadcaster =
.add(IPreResourceAccessDetails.class, accessDetails) CompositeInterceptorBroadcaster.newCompositeBroadcaster(getInterceptorBroadcaster(), theRequest);
.add(RequestDetails.class, theRequest) if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES)) {
.addIfMatchesType(ServletRequestDetails.class, theRequest); HookParams params = new HookParams()
CompositeInterceptorBroadcaster.doCallHooks( .add(IPreResourceAccessDetails.class, accessDetails)
getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params); .add(RequestDetails.class, theRequest)
if (accessDetails.isDontReturnResourceAtIndex(0)) { .addIfMatchesType(ServletRequestDetails.class, theRequest);
outcome.setResource(null); compositeBroadcaster.callHooks(Pointcut.STORAGE_PREACCESS_RESOURCES, params);
if (accessDetails.isDontReturnResourceAtIndex(0)) {
outcome.setResource(null);
}
} }
} }
@ -352,14 +355,17 @@ public abstract class BaseStorageDao {
// outcome.fireResourceViewCallback()) // outcome.fireResourceViewCallback())
outcome.registerResourceViewCallback(() -> { outcome.registerResourceViewCallback(() -> {
if (outcome.getResource() != null) { if (outcome.getResource() != null) {
SimplePreResourceShowDetails showDetails = new SimplePreResourceShowDetails(outcome.getResource()); IInterceptorBroadcaster compositeBroadcaster = CompositeInterceptorBroadcaster.newCompositeBroadcaster(
HookParams params = new HookParams() getInterceptorBroadcaster(), theRequest);
.add(IPreResourceShowDetails.class, showDetails) if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRESHOW_RESOURCES)) {
.add(RequestDetails.class, theRequest) SimplePreResourceShowDetails showDetails = new SimplePreResourceShowDetails(outcome.getResource());
.addIfMatchesType(ServletRequestDetails.class, theRequest); HookParams params = new HookParams()
CompositeInterceptorBroadcaster.doCallHooks( .add(IPreResourceShowDetails.class, showDetails)
getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params); .add(RequestDetails.class, theRequest)
outcome.setResource(showDetails.getResource(0)); .addIfMatchesType(ServletRequestDetails.class, theRequest);
compositeBroadcaster.callHooks(Pointcut.STORAGE_PRESHOW_RESOURCES, params);
outcome.setResource(showDetails.getResource(0));
}
} }
}); });
@ -378,16 +384,19 @@ public abstract class BaseStorageDao {
outcome.setEntitySupplierUseCallback(() -> { outcome.setEntitySupplierUseCallback(() -> {
// Interceptor broadcast: STORAGE_PREACCESS_RESOURCES // Interceptor broadcast: STORAGE_PREACCESS_RESOURCES
if (outcome.getResource() != null) { if (outcome.getResource() != null) {
SimplePreResourceAccessDetails accessDetails = IInterceptorBroadcaster compositeBroadcaster = CompositeInterceptorBroadcaster.newCompositeBroadcaster(
new SimplePreResourceAccessDetails(outcome.getResource()); getInterceptorBroadcaster(), theRequest);
HookParams params = new HookParams() if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES)) {
.add(IPreResourceAccessDetails.class, accessDetails) SimplePreResourceAccessDetails accessDetails =
.add(RequestDetails.class, theRequest) new SimplePreResourceAccessDetails(outcome.getResource());
.addIfMatchesType(ServletRequestDetails.class, theRequest); HookParams params = new HookParams()
CompositeInterceptorBroadcaster.doCallHooks( .add(IPreResourceAccessDetails.class, accessDetails)
getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params); .add(RequestDetails.class, theRequest)
if (accessDetails.isDontReturnResourceAtIndex(0)) { .addIfMatchesType(ServletRequestDetails.class, theRequest);
outcome.setResource(null); compositeBroadcaster.callHooks(Pointcut.STORAGE_PREACCESS_RESOURCES, params);
if (accessDetails.isDontReturnResourceAtIndex(0)) {
outcome.setResource(null);
}
} }
} }
@ -397,14 +406,19 @@ public abstract class BaseStorageDao {
// outcome.fireResourceViewCallback()) // outcome.fireResourceViewCallback())
outcome.registerResourceViewCallback(() -> { outcome.registerResourceViewCallback(() -> {
if (outcome.getResource() != null) { if (outcome.getResource() != null) {
SimplePreResourceShowDetails showDetails = new SimplePreResourceShowDetails(outcome.getResource()); IInterceptorBroadcaster compositeBroadcaster =
HookParams params = new HookParams() CompositeInterceptorBroadcaster.newCompositeBroadcaster(
.add(IPreResourceShowDetails.class, showDetails) getInterceptorBroadcaster(), theRequest);
.add(RequestDetails.class, theRequest) if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRESHOW_RESOURCES)) {
.addIfMatchesType(ServletRequestDetails.class, theRequest); SimplePreResourceShowDetails showDetails =
CompositeInterceptorBroadcaster.doCallHooks( new SimplePreResourceShowDetails(outcome.getResource());
getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params); HookParams params = new HookParams()
outcome.setResource(showDetails.getResource(0)); .add(IPreResourceShowDetails.class, showDetails)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
compositeBroadcaster.callHooks(Pointcut.STORAGE_PRESHOW_RESOURCES, params);
outcome.setResource(showDetails.getResource(0));
}
} }
}); });
}); });
@ -420,8 +434,9 @@ public abstract class BaseStorageDao {
if (theTransactionDetails.isAcceptingDeferredInterceptorBroadcasts(thePointcut)) { if (theTransactionDetails.isAcceptingDeferredInterceptorBroadcasts(thePointcut)) {
theTransactionDetails.addDeferredInterceptorBroadcast(thePointcut, theParams); theTransactionDetails.addDeferredInterceptorBroadcast(thePointcut, theParams);
} else { } else {
CompositeInterceptorBroadcaster.doCallHooks( IInterceptorBroadcaster compositeBroadcaster = CompositeInterceptorBroadcaster.newCompositeBroadcaster(
getInterceptorBroadcaster(), theRequestDetails, thePointcut, theParams); getInterceptorBroadcaster(), theRequestDetails);
compositeBroadcaster.callHooks(thePointcut, theParams);
} }
} }

View File

@ -359,14 +359,14 @@ public abstract class BaseTransactionProcessor {
try { try {
// Interceptor call: STORAGE_TRANSACTION_PROCESSING // Interceptor call: STORAGE_TRANSACTION_PROCESSING
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster = CompositeInterceptorBroadcaster.newCompositeBroadcaster(
Pointcut.STORAGE_TRANSACTION_PROCESSING, myInterceptorBroadcaster, theRequestDetails)) { myInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_TRANSACTION_PROCESSING)) {
HookParams params = new HookParams() HookParams params = new HookParams()
.add(RequestDetails.class, theRequestDetails) .add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequest) .addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(IBaseBundle.class, theRequest); .add(IBaseBundle.class, theRequest);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_TRANSACTION_PROCESSING, params);
myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_TRANSACTION_PROCESSING, params);
} }
return processTransaction(theRequestDetails, theRequest, theActionName, theNestedMode); return processTransaction(theRequestDetails, theRequest, theActionName, theNestedMode);
@ -561,8 +561,9 @@ public abstract class BaseTransactionProcessor {
theRequestDetails, response, getEntries, originalRequestOrder, transactionStopWatch, theNestedMode); theRequestDetails, response, getEntries, originalRequestOrder, transactionStopWatch, theNestedMode);
// Interceptor broadcast: JPA_PERFTRACE_INFO // Interceptor broadcast: JPA_PERFTRACE_INFO
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequestDetails)) { CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO)) {
String taskDurations = transactionStopWatch.formatTaskDurations(); String taskDurations = transactionStopWatch.formatTaskDurations();
StorageProcessingMessage message = new StorageProcessingMessage(); StorageProcessingMessage message = new StorageProcessingMessage();
message.setMessage("Transaction timing:\n" + taskDurations); message.setMessage("Transaction timing:\n" + taskDurations);
@ -570,8 +571,7 @@ public abstract class BaseTransactionProcessor {
.add(RequestDetails.class, theRequestDetails) .add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails) .addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(StorageProcessingMessage.class, message); .add(StorageProcessingMessage.class, message);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_INFO, params);
myInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_INFO, params);
} }
return response; return response;
@ -821,12 +821,10 @@ public abstract class BaseTransactionProcessor {
} }
private boolean haveWriteOperationsHooks(RequestDetails theRequestDetails) { private boolean haveWriteOperationsHooks(RequestDetails theRequestDetails) {
return CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.STORAGE_TRANSACTION_WRITE_OPERATIONS_PRE, myInterceptorBroadcaster, theRequestDetails) CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
|| CompositeInterceptorBroadcaster.hasHooks( return compositeBroadcaster.hasHooks(Pointcut.STORAGE_TRANSACTION_WRITE_OPERATIONS_PRE)
Pointcut.STORAGE_TRANSACTION_WRITE_OPERATIONS_POST, || compositeBroadcaster.hasHooks(Pointcut.STORAGE_TRANSACTION_WRITE_OPERATIONS_POST);
myInterceptorBroadcaster,
theRequestDetails);
} }
private void callWriteOperationsHook( private void callWriteOperationsHook(
@ -834,10 +832,14 @@ public abstract class BaseTransactionProcessor {
RequestDetails theRequestDetails, RequestDetails theRequestDetails,
TransactionDetails theTransactionDetails, TransactionDetails theTransactionDetails,
TransactionWriteOperationsDetails theWriteOperationsDetails) { TransactionWriteOperationsDetails theWriteOperationsDetails) {
HookParams params = new HookParams() IInterceptorBroadcaster compositeBroadcaster =
.add(TransactionDetails.class, theTransactionDetails) CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
.add(TransactionWriteOperationsDetails.class, theWriteOperationsDetails); if (compositeBroadcaster.hasHooks(thePointcut)) {
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, thePointcut, params); HookParams params = new HookParams()
.add(TransactionDetails.class, theTransactionDetails)
.add(TransactionWriteOperationsDetails.class, theWriteOperationsDetails);
compositeBroadcaster.callHooks(thePointcut, params);
}
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -967,18 +969,16 @@ public abstract class BaseTransactionProcessor {
+ " as it contained a duplicate conditional " + verb; + " as it contained a duplicate conditional " + verb;
ourLog.info(msg); ourLog.info(msg);
// Interceptor broadcast: JPA_PERFTRACE_INFO // Interceptor broadcast: JPA_PERFTRACE_INFO
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.JPA_PERFTRACE_WARNING, myInterceptorBroadcaster, theRequestDetails)) { CompositeInterceptorBroadcaster.newCompositeBroadcaster(
myInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_WARNING)) {
StorageProcessingMessage message = new StorageProcessingMessage().setMessage(msg); StorageProcessingMessage message = new StorageProcessingMessage().setMessage(msg);
HookParams params = new HookParams() HookParams params = new HookParams()
.add(RequestDetails.class, theRequestDetails) .add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails) .addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(StorageProcessingMessage.class, message); .add(StorageProcessingMessage.class, message);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_INFO, params);
myInterceptorBroadcaster,
theRequestDetails,
Pointcut.JPA_PERFTRACE_INFO,
params);
} }
theEntries.remove(index); theEntries.remove(index);
@ -1475,13 +1475,14 @@ public abstract class BaseTransactionProcessor {
} }
} }
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
ListMultimap<Pointcut, HookParams> deferredBroadcastEvents = ListMultimap<Pointcut, HookParams> deferredBroadcastEvents =
theTransactionDetails.endAcceptingDeferredInterceptorBroadcasts(); theTransactionDetails.endAcceptingDeferredInterceptorBroadcasts();
for (Map.Entry<Pointcut, HookParams> nextEntry : deferredBroadcastEvents.entries()) { for (Map.Entry<Pointcut, HookParams> nextEntry : deferredBroadcastEvents.entries()) {
Pointcut nextPointcut = nextEntry.getKey(); Pointcut nextPointcut = nextEntry.getKey();
HookParams nextParams = nextEntry.getValue(); HookParams nextParams = nextEntry.getValue();
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(nextPointcut, nextParams);
myInterceptorBroadcaster, theRequest, nextPointcut, nextParams);
} }
DeferredInterceptorBroadcasts deferredInterceptorBroadcasts = DeferredInterceptorBroadcasts deferredInterceptorBroadcasts =
@ -1492,8 +1493,7 @@ public abstract class BaseTransactionProcessor {
.add(DeferredInterceptorBroadcasts.class, deferredInterceptorBroadcasts) .add(DeferredInterceptorBroadcasts.class, deferredInterceptorBroadcasts)
.add(TransactionDetails.class, theTransactionDetails) .add(TransactionDetails.class, theTransactionDetails)
.add(IBaseBundle.class, theResponse); .add(IBaseBundle.class, theResponse);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_TRANSACTION_PROCESSED, params);
myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_TRANSACTION_PROCESSED, params);
theTransactionDetails.deferredBroadcastProcessingFinished(); theTransactionDetails.deferredBroadcastProcessingFinished();

View File

@ -135,8 +135,9 @@ public class MatchResourceUrlService<T extends IResourcePersistentId> {
} }
// Interceptor broadcast: STORAGE_PRESHOW_RESOURCES // Interceptor broadcast: STORAGE_PRESHOW_RESOURCES
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.STORAGE_PRESHOW_RESOURCES, myInterceptorBroadcaster, theRequest)) { CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRESHOW_RESOURCES)) {
Map<IBaseResource, T> resourceToPidMap = new HashMap<>(); Map<IBaseResource, T> resourceToPidMap = new HashMap<>();
IFhirResourceDao<R> dao = getResourceDao(theResourceType); IFhirResourceDao<R> dao = getResourceDao(theResourceType);
@ -152,8 +153,7 @@ public class MatchResourceUrlService<T extends IResourcePersistentId> {
.addIfMatchesType(ServletRequestDetails.class, theRequest); .addIfMatchesType(ServletRequestDetails.class, theRequest);
try { try {
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_PRESHOW_RESOURCES, params);
myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
retVal = accessDetails.toList().stream() retVal = accessDetails.toList().stream()
.map(resourceToPidMap::get) .map(resourceToPidMap::get)
@ -222,16 +222,16 @@ public class MatchResourceUrlService<T extends IResourcePersistentId> {
List<T> retVal = dao.searchForIds(theParamMap, theRequest, theConditionalOperationTargetOrNull); List<T> retVal = dao.searchForIds(theParamMap, theRequest, theConditionalOperationTargetOrNull);
// Interceptor broadcast: JPA_PERFTRACE_INFO // Interceptor broadcast: JPA_PERFTRACE_INFO
if (CompositeInterceptorBroadcaster.hasHooks( IInterceptorBroadcaster compositeBroadcaster =
Pointcut.JPA_PERFTRACE_INFO, myInterceptorBroadcaster, theRequest)) { CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO)) {
StorageProcessingMessage message = new StorageProcessingMessage(); StorageProcessingMessage message = new StorageProcessingMessage();
message.setMessage("Processed conditional resource URL with " + retVal.size() + " result(s) in " + sw); message.setMessage("Processed conditional resource URL with " + retVal.size() + " result(s) in " + sw);
HookParams params = new HookParams() HookParams params = new HookParams()
.add(RequestDetails.class, theRequest) .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest) .addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, message); .add(StorageProcessingMessage.class, message);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_INFO, params);
myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
} }
return new HashSet<>(retVal); return new HashSet<>(retVal);

View File

@ -19,7 +19,6 @@
*/ */
package ca.uhn.fhir.jpa.dao; package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.jpa.api.dao.IDao;
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -32,9 +31,8 @@ public class SearchBuilderFactory<T extends IResourcePersistentId<?>> {
public SearchBuilderFactory() {} public SearchBuilderFactory() {}
public ISearchBuilder<T> newSearchBuilder( public ISearchBuilder<T> newSearchBuilder(String theResourceName, Class<? extends IBaseResource> theResourceType) {
IDao theDao, String theResourceName, Class<? extends IBaseResource> theResourceType) { return (ISearchBuilder<T>)
return (ISearchBuilder<T>) myApplicationContext.getBean( myApplicationContext.getBean(ISearchBuilder.SEARCH_BUILDER_BEAN_NAME, theResourceName, theResourceType);
ISearchBuilder.SEARCH_BUILDER_BEAN_NAME, theDao, theResourceName, theResourceType);
} }
} }

View File

@ -364,19 +364,21 @@ public class HapiTransactionService implements IHapiTransactionService {
} }
if (maxRetries == 0) { if (maxRetries == 0) {
HookParams params = new HookParams() IInterceptorBroadcaster compositeBroadcaster =
.add(RequestDetails.class, theExecutionBuilder.myRequestDetails) CompositeInterceptorBroadcaster.newCompositeBroadcaster(
.addIfMatchesType( myInterceptorBroadcaster, theExecutionBuilder.myRequestDetails);
ServletRequestDetails.class, theExecutionBuilder.myRequestDetails); if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_VERSION_CONFLICT)) {
ResourceVersionConflictResolutionStrategy conflictResolutionStrategy = HookParams params = new HookParams()
(ResourceVersionConflictResolutionStrategy) .add(RequestDetails.class, theExecutionBuilder.myRequestDetails)
CompositeInterceptorBroadcaster.doCallHooksAndReturnObject( .addIfMatchesType(
myInterceptorBroadcaster, ServletRequestDetails.class, theExecutionBuilder.myRequestDetails);
theExecutionBuilder.myRequestDetails, ResourceVersionConflictResolutionStrategy conflictResolutionStrategy =
Pointcut.STORAGE_VERSION_CONFLICT, (ResourceVersionConflictResolutionStrategy)
params); compositeBroadcaster.callHooksAndReturnObject(
if (conflictResolutionStrategy != null && conflictResolutionStrategy.isRetry()) { Pointcut.STORAGE_VERSION_CONFLICT, params);
maxRetries = conflictResolutionStrategy.getMaxRetries(); if (conflictResolutionStrategy != null && conflictResolutionStrategy.isRetry()) {
maxRetries = conflictResolutionStrategy.getMaxRetries();
}
} }
} }

View File

@ -34,12 +34,11 @@ import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import jakarta.annotation.Nonnull; import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable; import jakarta.annotation.Nullable;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import java.util.Arrays; import java.util.Arrays;
@ -48,12 +47,7 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
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;
public abstract class BaseRequestPartitionHelperSvc implements IRequestPartitionHelperSvc { public abstract class BaseRequestPartitionHelperSvc implements IRequestPartitionHelperSvc {
private static final Logger ourLog = LoggerFactory.getLogger(BaseRequestPartitionHelperSvc.class);
private final HashSet<Object> myNonPartitionableResourceNames; private final HashSet<Object> myNonPartitionableResourceNames;
@ -124,21 +118,25 @@ public abstract class BaseRequestPartitionHelperSvc implements IRequestPartition
requestPartitionId = getSystemRequestPartitionId((SystemRequestDetails) requestDetails, false); requestPartitionId = getSystemRequestPartitionId((SystemRequestDetails) requestDetails, false);
} else if ((requestDetails instanceof SystemRequestDetails) && nonPartitionableResource) { } else if ((requestDetails instanceof SystemRequestDetails) && nonPartitionableResource) {
requestPartitionId = RequestPartitionId.fromPartitionId(myPartitionSettings.getDefaultPartitionId()); requestPartitionId = RequestPartitionId.fromPartitionId(myPartitionSettings.getDefaultPartitionId());
} else if (hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, myInterceptorBroadcaster, requestDetails)) { } else {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY IInterceptorBroadcaster compositeBroadcaster =
HookParams params = new HookParams() CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, requestDetails);
.add(RequestDetails.class, requestDetails) if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY)) {
.addIfMatchesType(ServletRequestDetails.class, requestDetails); // Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY
requestPartitionId = (RequestPartitionId) doCallHooksAndReturnObject( HookParams params = new HookParams()
myInterceptorBroadcaster, requestDetails, Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params); .add(RequestDetails.class, requestDetails)
} else if (hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_READ, myInterceptorBroadcaster, requestDetails)) { .addIfMatchesType(ServletRequestDetails.class, requestDetails);
// Interceptor call: STORAGE_PARTITION_IDENTIFY_READ requestPartitionId = (RequestPartitionId)
HookParams params = new HookParams() compositeBroadcaster.callHooksAndReturnObject(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params);
.add(RequestDetails.class, requestDetails) } else if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_READ)) {
.addIfMatchesType(ServletRequestDetails.class, requestDetails) // Interceptor call: STORAGE_PARTITION_IDENTIFY_READ
.add(ReadPartitionIdRequestDetails.class, theDetails); HookParams params = new HookParams()
requestPartitionId = (RequestPartitionId) doCallHooksAndReturnObject( .add(RequestDetails.class, requestDetails)
myInterceptorBroadcaster, requestDetails, Pointcut.STORAGE_PARTITION_IDENTIFY_READ, params); .addIfMatchesType(ServletRequestDetails.class, requestDetails)
.add(ReadPartitionIdRequestDetails.class, theDetails);
requestPartitionId = (RequestPartitionId)
compositeBroadcaster.callHooksAndReturnObject(Pointcut.STORAGE_PARTITION_IDENTIFY_READ, params);
}
} }
validateRequestPartitionNotNull( validateRequestPartitionNotNull(
@ -158,13 +156,17 @@ public abstract class BaseRequestPartitionHelperSvc implements IRequestPartition
if (theRequestDetails instanceof SystemRequestDetails if (theRequestDetails instanceof SystemRequestDetails
&& systemRequestHasExplicitPartition((SystemRequestDetails) theRequestDetails)) { && systemRequestHasExplicitPartition((SystemRequestDetails) theRequestDetails)) {
requestPartitionId = getSystemRequestPartitionId((SystemRequestDetails) theRequestDetails); requestPartitionId = getSystemRequestPartitionId((SystemRequestDetails) theRequestDetails);
} else if (hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, myInterceptorBroadcaster, theRequestDetails)) { } else {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY IInterceptorBroadcaster compositeBroadcaster = CompositeInterceptorBroadcaster.newCompositeBroadcaster(
HookParams params = new HookParams() myInterceptorBroadcaster, theRequestDetails);
.add(RequestDetails.class, theRequestDetails) if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY)) {
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); // Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY
requestPartitionId = (RequestPartitionId) doCallHooksAndReturnObject( HookParams params = new HookParams()
myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params); .add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
requestPartitionId = (RequestPartitionId)
compositeBroadcaster.callHooksAndReturnObject(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params);
}
} }
// TODO MM: at the moment it is ok for this method to return null // TODO MM: at the moment it is ok for this method to return null
@ -244,21 +246,25 @@ public abstract class BaseRequestPartitionHelperSvc implements IRequestPartition
&& systemRequestHasExplicitPartition((SystemRequestDetails) theRequest)) { && systemRequestHasExplicitPartition((SystemRequestDetails) theRequest)) {
requestPartitionId = requestPartitionId =
getSystemRequestPartitionId((SystemRequestDetails) theRequest, nonPartitionableResource); getSystemRequestPartitionId((SystemRequestDetails) theRequest, nonPartitionableResource);
} else if (hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, myInterceptorBroadcaster, requestDetails)) { } else {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY IInterceptorBroadcaster compositeBroadcaster =
HookParams params = new HookParams() CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, requestDetails);
.add(RequestDetails.class, requestDetails) if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY)) {
.addIfMatchesType(ServletRequestDetails.class, requestDetails); // Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY
requestPartitionId = (RequestPartitionId) doCallHooksAndReturnObject( HookParams params = new HookParams()
myInterceptorBroadcaster, requestDetails, Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params); .add(RequestDetails.class, requestDetails)
} else if (hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE, myInterceptorBroadcaster, requestDetails)) { .addIfMatchesType(ServletRequestDetails.class, requestDetails);
// Interceptor call: STORAGE_PARTITION_IDENTIFY_CREATE requestPartitionId = (RequestPartitionId)
HookParams params = new HookParams() compositeBroadcaster.callHooksAndReturnObject(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params);
.add(IBaseResource.class, theResource) } else if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE)) {
.add(RequestDetails.class, requestDetails) // Interceptor call: STORAGE_PARTITION_IDENTIFY_CREATE
.addIfMatchesType(ServletRequestDetails.class, requestDetails); HookParams params = new HookParams()
requestPartitionId = (RequestPartitionId) doCallHooksAndReturnObject( .add(IBaseResource.class, theResource)
myInterceptorBroadcaster, requestDetails, Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE, params); .add(RequestDetails.class, requestDetails)
.addIfMatchesType(ServletRequestDetails.class, requestDetails);
requestPartitionId = (RequestPartitionId) compositeBroadcaster.callHooksAndReturnObject(
Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE, params);
}
} }
// If the interceptors haven't selected a partition, and its a non-partitionable resource anyhow, send // If the interceptors haven't selected a partition, and its a non-partitionable resource anyhow, send
@ -322,7 +328,9 @@ public abstract class BaseRequestPartitionHelperSvc implements IRequestPartition
@Override @Override
public void validateHasPartitionPermissions( public void validateHasPartitionPermissions(
@Nonnull RequestDetails theRequest, String theResourceType, RequestPartitionId theRequestPartitionId) { @Nonnull RequestDetails theRequest, String theResourceType, RequestPartitionId theRequestPartitionId) {
if (myInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_SELECTED)) { IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_SELECTED)) {
RuntimeResourceDefinition runtimeResourceDefinition = null; RuntimeResourceDefinition runtimeResourceDefinition = null;
if (theResourceType != null) { if (theResourceType != null) {
runtimeResourceDefinition = myFhirContext.getResourceDefinition(theResourceType); runtimeResourceDefinition = myFhirContext.getResourceDefinition(theResourceType);
@ -332,7 +340,7 @@ public abstract class BaseRequestPartitionHelperSvc implements IRequestPartition
.add(RequestDetails.class, theRequest) .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest) .addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(RuntimeResourceDefinition.class, runtimeResourceDefinition); .add(RuntimeResourceDefinition.class, runtimeResourceDefinition);
doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PARTITION_SELECTED, params); compositeBroadcaster.callHooks(Pointcut.STORAGE_PARTITION_SELECTED, params);
} }
} }

View File

@ -11,6 +11,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.test.utilities.MockInvoker;
import ca.uhn.fhir.util.SleepUtil; import ca.uhn.fhir.util.SleepUtil;
import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.ConstraintViolationException;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -80,17 +81,15 @@ class HapiTransactionServiceTest {
} }
private void mockInterceptorBroadcaster() { private void mockInterceptorBroadcaster() {
lenient().when(myInterceptorBroadcasterMock.callHooksAndReturnObject(eq(Pointcut.STORAGE_VERSION_CONFLICT), lenient().when(myInterceptorBroadcasterMock.hasHooks(eq(Pointcut.STORAGE_VERSION_CONFLICT))).thenReturn(true);
isA(HookParams.class))) lenient().when(myInterceptorBroadcasterMock.getInvokersForPointcut(eq(Pointcut.STORAGE_VERSION_CONFLICT))).thenReturn(MockInvoker.list(hookParams->{
.thenAnswer(invocationOnMock -> {
HookParams hookParams = (HookParams) invocationOnMock.getArguments()[1];
//answer with whatever retry settings passed in as HookParam //answer with whatever retry settings passed in as HookParam
RequestDetails requestDetails = hookParams.get(RequestDetails.class); RequestDetails requestDetails = hookParams.get(RequestDetails.class);
ResourceVersionConflictResolutionStrategy answer = new ResourceVersionConflictResolutionStrategy(); ResourceVersionConflictResolutionStrategy answer = new ResourceVersionConflictResolutionStrategy();
answer.setRetry(requestDetails.isRetry()); answer.setRetry(requestDetails.isRetry());
answer.setMaxRetries(requestDetails.getMaxRetries()); answer.setMaxRetries(requestDetails.getMaxRetries());
return answer; return answer;
}); }));
} }

View File

@ -0,0 +1,51 @@
package ca.uhn.fhir.test.utilities;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IBaseInterceptorBroadcaster;
import jakarta.annotation.Nonnull;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
public class MockInvoker implements IBaseInterceptorBroadcaster.IInvoker {
private final Function<HookParams, Object> myFunction;
private MockInvoker(Consumer<HookParams> theRunnable) {
this(param -> { theRunnable.accept(param); return null; });
}
private MockInvoker(Function<HookParams, Object> theFunction) {
myFunction = theFunction;
}
@Override
public Object invoke(HookParams theParams) {
return myFunction.apply(theParams);
}
@Override
public int getOrder() {
return 0;
}
@Override
public Object getInterceptor() {
return new Object();
}
@Override
public int compareTo(@Nonnull IBaseInterceptorBroadcaster.IInvoker o) {
return 0;
}
public static List<IBaseInterceptorBroadcaster.IInvoker> list(Consumer<HookParams> theRunnable) {
return List.of(new MockInvoker(theRunnable));
}
public static List<IBaseInterceptorBroadcaster.IInvoker> list(Function<HookParams, Object> theRunnable) {
return List.of(new MockInvoker(theRunnable));
}
}

View File

@ -84,9 +84,14 @@ public interface IValidationProviders {
protected void addTerminologyResource(String theUrl, T theResource) { protected void addTerminologyResource(String theUrl, T theResource) {
myTerminologyResourceMap.put(theUrl, theResource); myTerminologyResourceMap.put(theUrl, theResource);
} }
protected void addVersionedTerminologyResource(String theUrl, String theVersion, T theResource) {
myTerminologyResourceMap.put(theUrl + "|" + theVersion, theResource);
}
public abstract T addTerminologyResource(String theUrl); public abstract T addTerminologyResource(String theUrl);
public abstract T addTerminologyResource(String theUrl, String theVersion);
protected IBaseParameters getTerminologyResponse(String theOperation, String theUrl, String theCode) throws Exception { protected IBaseParameters getTerminologyResponse(String theOperation, String theUrl, String theCode) throws Exception {
String inputKey = getInputKey(theOperation, theUrl, theCode); String inputKey = getInputKey(theOperation, theUrl, theCode);
if (myExceptionMap.containsKey(inputKey)) { if (myExceptionMap.containsKey(inputKey)) {

View File

@ -95,6 +95,11 @@ public interface IValidationProvidersDstu3 {
addTerminologyResource(theUrl, codeSystem); addTerminologyResource(theUrl, codeSystem);
return codeSystem; return codeSystem;
} }
@Override
public CodeSystem addTerminologyResource(String theUrl, String theVersion) {
return addTerminologyResource(theUrl);
}
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -133,5 +138,10 @@ public interface IValidationProvidersDstu3 {
addTerminologyResource(theUrl, valueSet); addTerminologyResource(theUrl, valueSet);
return valueSet; return valueSet;
} }
@Override
public ValueSet addTerminologyResource(String theUrl, String theVersion) {
return addTerminologyResource(theUrl);
}
} }
} }

View File

@ -99,6 +99,14 @@ public interface IValidationProvidersR4 {
addTerminologyResource(theUrl, codeSystem); addTerminologyResource(theUrl, codeSystem);
return codeSystem; return codeSystem;
} }
@Override
public CodeSystem addTerminologyResource(String theUrl, String theVersion) {
CodeSystem codeSystem = addTerminologyResource(theUrl);
codeSystem.setVersion(theVersion);
addVersionedTerminologyResource(theUrl, theVersion, codeSystem);
return codeSystem;
}
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -130,6 +138,7 @@ public interface IValidationProvidersR4 {
Class<Parameters> getParameterType() { Class<Parameters> getParameterType() {
return Parameters.class; return Parameters.class;
} }
@Override @Override
public ValueSet addTerminologyResource(String theUrl) { public ValueSet addTerminologyResource(String theUrl) {
ValueSet valueSet = new ValueSet(); ValueSet valueSet = new ValueSet();
@ -138,5 +147,12 @@ public interface IValidationProvidersR4 {
addTerminologyResource(theUrl, valueSet); addTerminologyResource(theUrl, valueSet);
return valueSet; return valueSet;
} }
@Override
public ValueSet addTerminologyResource(String theUrl, String theVersion) {
ValueSet valueSet = addTerminologyResource(theUrl);
addVersionedTerminologyResource(theUrl, theVersion, valueSet);
return valueSet;
}
} }
} }

View File

@ -297,7 +297,7 @@ public class BaseController {
FhirVersionEnum version = theRequest.getFhirVersion(myConfig); FhirVersionEnum version = theRequest.getFhirVersion(myConfig);
VersionCanonicalizer retVal = myCanonicalizers.get(version); VersionCanonicalizer retVal = myCanonicalizers.get(version);
if (retVal == null) { if (retVal == null) {
retVal = new VersionCanonicalizer(version.newContext()); retVal = new VersionCanonicalizer(FhirContext.forVersion(version));
myCanonicalizers.put(version, retVal); myCanonicalizers.put(version, retVal);
} }
return retVal; return retVal;

View File

@ -12,6 +12,7 @@ import ca.uhn.fhir.system.HapiSystemProperties;
import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
import jakarta.annotation.Nonnull; import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable; import jakarta.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;
@ -466,18 +467,11 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
return null; return null;
} }
String uri = theUri; if (StringUtils.countMatches(theUri, "|") > 1) {
// handle profile version, if present ourLog.warn("Unrecognized profile uri: {}", theUri);
if (theUri.contains("|")) {
String[] parts = theUri.split("\\|");
if (parts.length == 2) {
uri = parts[0];
} else {
ourLog.warn("Unrecognized profile uri: {}", theUri);
}
} }
ResourceKey key = new ResourceKey(class_.getSimpleName(), uri); ResourceKey key = new ResourceKey(class_.getSimpleName(), theUri);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
T retVal = (T) myFetchResourceCache.get(key); T retVal = (T) myFetchResourceCache.get(key);