Compare commits

...

5 Commits

Author SHA1 Message Date
Mark Iantorno 1148eadddb
Merge a38065dafe into 061390d76b 2024-11-27 08:24:33 -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
JasonRoberts-smile 77fa7f7819
adapt template for reuse in CDA (#6500)
* adapt template for reuse in CDA

* use coerced onset date time value

* fix broken test
2024-11-26 09:14:18 -05:00
Mark Iantorno a38065dafe
Update stale.yml
Pipelines are not triggering, I need to test why.
2024-10-28 13:23:19 -04:00
68 changed files with 1068 additions and 720 deletions

3
.github/stale.yml vendored
View File

@ -15,3 +15,6 @@ markComment: >
for your contributions. for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable # Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false closeComment: false
CHANGE TO TEST IF THE PIPELINES ARE BROKEN, PLEASE IGNORE AND DON'T MERGE

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

@ -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
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_EVERYTHING)) {
HookParams hooks = new HookParams() HookParams hooks = new HookParams()
.add(AtomicInteger.class, counter) .add(AtomicInteger.class, counter)
.add(RequestDetails.class, theRequest) .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest); .addIfMatchesType(ServletRequestDetails.class, theRequest);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_EVERYTHING, hooks);
myInterceptorBroadcaster, theRequest, 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);
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(
myInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES)) {
HookParams params = new HookParams() HookParams params = new HookParams()
.add(IPreResourceAccessDetails.class, accessDetails) .add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, theRequestDetails) .add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); .addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_PREACCESS_RESOURCES, params);
myInterceptorBroadcaster, theRequestDetails, 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);
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INDEXSEARCH_QUERY_COMPLETE)) {
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(SearchRuntimeDetails.class, theSearchRuntimeDetails); .add(SearchRuntimeDetails.class, theSearchRuntimeDetails);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_INDEXSEARCH_QUERY_COMPLETE, params);
myInterceptorBroadcaster, }
theRequest,
Pointcut.JPA_PERFTRACE_INDEXSEARCH_QUERY_COMPLETE,
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 * @param request the request deatils
* Sends a raw SQL query to the Pointcut for raw SQL queries. * 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,6 +2092,9 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
indexStrings.sort(Comparator.naturalOrder()); indexStrings.sort(Comparator.naturalOrder());
// Interceptor broadcast: JPA_PERFTRACE_INFO // Interceptor broadcast: JPA_PERFTRACE_INFO
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_INFO)) {
String indexStringForLog = indexStrings.size() > 1 ? indexStrings.toString() : indexStrings.get(0); String indexStringForLog = indexStrings.size() > 1 ? indexStrings.toString() : indexStrings.get(0);
StorageProcessingMessage msg = new StorageProcessingMessage() StorageProcessingMessage msg = new StorageProcessingMessage()
.setMessage("Using " + theComboParam.getComboSearchParamType() + " index(es) for query for search: " .setMessage("Using " + theComboParam.getComboSearchParamType() + " index(es) for query for search: "
@ -2103,8 +2103,8 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
.add(RequestDetails.class, theRequest) .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest) .addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, msg); .add(StorageProcessingMessage.class, msg);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_INFO, params);
myInterceptorBroadcaster, theRequest, 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) {
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(thePointcut)) {
StorageProcessingMessage message = new StorageProcessingMessage(); StorageProcessingMessage message = new StorageProcessingMessage();
message.setMessage(theMessage); message.setMessage(theMessage);
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(myInterceptorBroadcaster, theRequest, pointcut, params); 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) {
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRESEARCH_REGISTERED)) {
HookParams params = new HookParams() HookParams params = new HookParams()
.add(ICachedSearchDetails.class, search) .add(ICachedSearchDetails.class, search)
.add(RequestDetails.class, theRequestDetails) .add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails) .addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(SearchParameterMap.class, theParams) .add(SearchParameterMap.class, theParams)
.add(RequestPartitionId.class, theRequestPartitionId); .add(RequestPartitionId.class, theRequestPartitionId);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_PRESEARCH_REGISTERED, params);
myInterceptorBroadcaster, theRequestDetails, 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);
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_WARNING)) {
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, msg); .add(StorageProcessingMessage.class, msg);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_WARNING, params);
myInterceptorBroadcaster, theRequest, 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);
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(
myInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_WARNING)) {
StorageProcessingMessage message = new StorageProcessingMessage(); StorageProcessingMessage message = new StorageProcessingMessage();
ourLog.warn(msg);
message.setMessage(msg); message.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_WARNING, params);
myInterceptorBroadcaster, theRequestDetails, 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

@ -23,8 +23,8 @@ Comments: AllergyIntolerance.note[x].text (separated by <br />)
</thead> </thead>
<tbody> <tbody>
<th:block th:each="entry : ${resource.entry}" th:object="${entry.getResource()}"> <th:block th:each="entry : ${resource.entry}" th:object="${entry.getResource()}">
<th:block th:with="extension=${entry.getResource().getExtensionByUrl('http://hl7.org/fhir/StructureDefinition/narrativeLink').getValue().getValue()}"> <th:block th:with="extension=${entry.getResource().getExtensionByUrl('http://hl7.org/fhir/StructureDefinition/narrativeLink')}">
<tr th:id="${#strings.arraySplit(extension, '#')[1]}"> <tr th:id="${extension != null} ? ${#strings.arraySplit(extension.getValue().getValue(), '#')[1]} : ''">
<td th:insert="IpsUtilityFragments :: codeableConcept (cc=*{getCode()},attr='display')">Allergen</td> <td th:insert="IpsUtilityFragments :: codeableConcept (cc=*{getCode()},attr='display')">Allergen</td>
<td th:insert="~{IpsUtilityFragments :: codeableConcept (cc=*{getClinicalStatus()},attr='code')}">Status</td> <td th:insert="~{IpsUtilityFragments :: codeableConcept (cc=*{getClinicalStatus()},attr='code')}">Status</td>
<td th:insert="~{IpsUtilityFragments :: concat (list=*{getCategory()},attr='value')}">Category</td> <td th:insert="~{IpsUtilityFragments :: concat (list=*{getCategory()},attr='value')}">Category</td>
@ -33,7 +33,7 @@ Comments: AllergyIntolerance.note[x].text (separated by <br />)
<td th:insert="~{IpsUtilityFragments :: concat (list=*{getNote()},attr='text')}">Comments</td> <td th:insert="~{IpsUtilityFragments :: concat (list=*{getNote()},attr='text')}">Comments</td>
<th:block th:if="*{hasOnsetDateTimeType()}"> <th:block th:if="*{hasOnsetDateTimeType()}">
<td th:text="*{getOnsetDateTimeType().getValue()}">Onset</td> <td th:text="*{getOnsetDateTimeType().getValueAsString()}">Onset</td>
</th:block> </th:block>
<th:block th:if="*{hasOnsetStringType()}"> <th:block th:if="*{hasOnsetStringType()}">
<td th:text="*{getOnsetStringType().getValue()}">Onset</td> <td th:text="*{getOnsetStringType().getValue()}">Onset</td>

View File

@ -220,7 +220,7 @@ public class IpsGeneratorSvcImplTest {
HtmlTable table = (HtmlTable) tables.get(0); HtmlTable table = (HtmlTable) tables.get(0);
int onsetIndex = 6; int onsetIndex = 6;
assertEquals("Onset", table.getHeader().getRows().get(0).getCell(onsetIndex).asNormalizedText()); assertEquals("Onset", table.getHeader().getRows().get(0).getCell(onsetIndex).asNormalizedText());
assertEquals(new DateTimeType("2020-02-03T11:22:33Z").getValue().toString(), table.getBodies().get(0).getRows().get(0).getCell(onsetIndex).asNormalizedText()); assertEquals(new DateTimeType("2020-02-03T11:22:33Z").getValueAsString(), table.getBodies().get(0).getRows().get(0).getCell(onsetIndex).asNormalizedText());
assertEquals("Some Onset", table.getBodies().get(0).getRows().get(1).getCell(onsetIndex).asNormalizedText()); assertEquals("Some Onset", table.getBodies().get(0).getRows().get(1).getCell(onsetIndex).asNormalizedText());
assertEquals("", table.getBodies().get(0).getRows().get(2).getCell(onsetIndex).asNormalizedText()); assertEquals("", table.getBodies().get(0).getRows().get(2).getCell(onsetIndex).asNormalizedText());
} }

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,13 +118,16 @@ 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
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.SUBSCRIPTION_RESOURCE_MODIFIED)) {
HookParams params = new HookParams().add(ResourceModifiedMessage.class, msg); HookParams params = new HookParams().add(ResourceModifiedMessage.class, msg);
boolean outcome = CompositeInterceptorBroadcaster.doCallHooks( boolean outcome = compositeBroadcaster.callHooks(Pointcut.SUBSCRIPTION_RESOURCE_MODIFIED, params);
myInterceptorBroadcaster, theRequest, 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.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

@ -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 resourceName = theRequestDetails.getResourceName();
String queryString = theSearchParameterMap.toNormalizedQueryString(myFhirContext); String queryString = theSearchParameterMap.toNormalizedQueryString(myFhirContext);
if (!Objects.equals(resourceName, theRequestDetails.getUserData().get(RESOURCE_NAME)) if (!Objects.equals(resourceName, theRequestDetails.getUserData().get(RESOURCE_NAME))
|| !Objects.equals(queryString, theRequestDetails.getUserData().get(QUERY_STRING))) { || !Objects.equals(
queryString, theRequestDetails.getUserData().get(QUERY_STRING))) {
theRequestDetails.getUserData().remove(EXPANSION_RESULTS); 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,18 +54,21 @@ 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()) {
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(theInterceptorBroadcaster, theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRESHOW_RESOURCES)) {
SimplePreResourceShowDetails accessDetails = new SimplePreResourceShowDetails(retVal); SimplePreResourceShowDetails accessDetails = new SimplePreResourceShowDetails(retVal);
HookParams params = new HookParams() HookParams params = new HookParams()
.add(IPreResourceShowDetails.class, accessDetails) .add(IPreResourceShowDetails.class, accessDetails)
.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);
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() {
assertThat(retVal).isFalse(); myOrders.add(0);
verify(myReqDetailsBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock); return null;
} }
@Hook(value = Pointcut.TEST_RO, order = 2)
public BaseServerResponseException hook2() {
myOrders.add(2);
return null;
}
}
@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
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_INITIATE_BULK_EXPORT)) {
HookParams initiateBulkExportHookParams = (new HookParams()) HookParams initiateBulkExportHookParams = (new HookParams())
.add(BulkExportJobParameters.class, theOptions) .add(BulkExportJobParameters.class, theOptions)
.add(RequestDetails.class, theRequestDetails) .add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); .addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_INITIATE_BULK_EXPORT, initiateBulkExportHookParams);
this.myInterceptorBroadcaster, }
theRequestDetails,
Pointcut.STORAGE_INITIATE_BULK_EXPORT,
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
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRE_INITIATE_BULK_EXPORT)) {
HookParams preInitiateBulkExportHookParams = new HookParams(); HookParams preInitiateBulkExportHookParams = new HookParams();
preInitiateBulkExportHookParams.add(BulkExportJobParameters.class, theOptions); preInitiateBulkExportHookParams.add(BulkExportJobParameters.class, theOptions);
preInitiateBulkExportHookParams.add(RequestDetails.class, theRequestDetails); preInitiateBulkExportHookParams.add(RequestDetails.class, theRequestDetails);
preInitiateBulkExportHookParams.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); preInitiateBulkExportHookParams.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_PRE_INITIATE_BULK_EXPORT, preInitiateBulkExportHookParams);
myInterceptorBroadcaster, }
theRequestDetails,
Pointcut.STORAGE_PRE_INITIATE_BULK_EXPORT,
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());
} }
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRE_DELETE_EXPUNGE)) {
for (String url : theUrlsToDeleteExpunge) { for (String url : theUrlsToDeleteExpunge) {
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(String.class, url); .add(String.class, url);
CompositeInterceptorBroadcaster.doCallHooks( compositeBroadcaster.callHooks(Pointcut.STORAGE_PRE_DELETE_EXPUNGE, params);
myInterceptorBroadcaster, theRequestDetails, 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,16 +335,19 @@ 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());
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(getInterceptorBroadcaster(), theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES)) {
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);
getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
if (accessDetails.isDontReturnResourceAtIndex(0)) { if (accessDetails.isDontReturnResourceAtIndex(0)) {
outcome.setResource(null); outcome.setResource(null);
} }
} }
}
// Interceptor broadcast: STORAGE_PRESHOW_RESOURCES // Interceptor broadcast: STORAGE_PRESHOW_RESOURCES
// Note that this will only fire if someone actually goes to use the // Note that this will only fire if someone actually goes to use the
@ -352,15 +355,18 @@ public abstract class BaseStorageDao {
// outcome.fireResourceViewCallback()) // outcome.fireResourceViewCallback())
outcome.registerResourceViewCallback(() -> { outcome.registerResourceViewCallback(() -> {
if (outcome.getResource() != null) { if (outcome.getResource() != null) {
IInterceptorBroadcaster compositeBroadcaster = CompositeInterceptorBroadcaster.newCompositeBroadcaster(
getInterceptorBroadcaster(), theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRESHOW_RESOURCES)) {
SimplePreResourceShowDetails showDetails = new SimplePreResourceShowDetails(outcome.getResource()); SimplePreResourceShowDetails showDetails = new SimplePreResourceShowDetails(outcome.getResource());
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);
getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
outcome.setResource(showDetails.getResource(0)); outcome.setResource(showDetails.getResource(0));
} }
}
}); });
return outcome; return outcome;
@ -378,18 +384,21 @@ 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) {
IInterceptorBroadcaster compositeBroadcaster = CompositeInterceptorBroadcaster.newCompositeBroadcaster(
getInterceptorBroadcaster(), theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES)) {
SimplePreResourceAccessDetails accessDetails = SimplePreResourceAccessDetails accessDetails =
new SimplePreResourceAccessDetails(outcome.getResource()); new SimplePreResourceAccessDetails(outcome.getResource());
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);
getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
if (accessDetails.isDontReturnResourceAtIndex(0)) { if (accessDetails.isDontReturnResourceAtIndex(0)) {
outcome.setResource(null); outcome.setResource(null);
} }
} }
}
// Interceptor broadcast: STORAGE_PRESHOW_RESOURCES // Interceptor broadcast: STORAGE_PRESHOW_RESOURCES
// Note that this will only fire if someone actually goes to use the // Note that this will only fire if someone actually goes to use the
@ -397,15 +406,20 @@ 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(
getInterceptorBroadcaster(), theRequest);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PRESHOW_RESOURCES)) {
SimplePreResourceShowDetails showDetails =
new SimplePreResourceShowDetails(outcome.getResource());
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);
getInterceptorBroadcaster(), theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
outcome.setResource(showDetails.getResource(0)); 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) {
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(thePointcut)) {
HookParams params = new HookParams() HookParams params = new HookParams()
.add(TransactionDetails.class, theTransactionDetails) .add(TransactionDetails.class, theTransactionDetails)
.add(TransactionWriteOperationsDetails.class, theWriteOperationsDetails); .add(TransactionWriteOperationsDetails.class, theWriteOperationsDetails);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, thePointcut, params); 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,21 +364,23 @@ public class HapiTransactionService implements IHapiTransactionService {
} }
if (maxRetries == 0) { if (maxRetries == 0) {
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(
myInterceptorBroadcaster, theExecutionBuilder.myRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_VERSION_CONFLICT)) {
HookParams params = new HookParams() HookParams params = new HookParams()
.add(RequestDetails.class, theExecutionBuilder.myRequestDetails) .add(RequestDetails.class, theExecutionBuilder.myRequestDetails)
.addIfMatchesType( .addIfMatchesType(
ServletRequestDetails.class, theExecutionBuilder.myRequestDetails); ServletRequestDetails.class, theExecutionBuilder.myRequestDetails);
ResourceVersionConflictResolutionStrategy conflictResolutionStrategy = ResourceVersionConflictResolutionStrategy conflictResolutionStrategy =
(ResourceVersionConflictResolutionStrategy) (ResourceVersionConflictResolutionStrategy)
CompositeInterceptorBroadcaster.doCallHooksAndReturnObject( compositeBroadcaster.callHooksAndReturnObject(
myInterceptorBroadcaster, Pointcut.STORAGE_VERSION_CONFLICT, params);
theExecutionBuilder.myRequestDetails,
Pointcut.STORAGE_VERSION_CONFLICT,
params);
if (conflictResolutionStrategy != null && conflictResolutionStrategy.isRetry()) { if (conflictResolutionStrategy != null && conflictResolutionStrategy.isRetry()) {
maxRetries = conflictResolutionStrategy.getMaxRetries(); maxRetries = conflictResolutionStrategy.getMaxRetries();
} }
} }
}
if (i < maxRetries) { if (i < maxRetries) {
if (theExecutionBuilder.myTransactionDetails != null) { if (theExecutionBuilder.myTransactionDetails != null) {

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 {
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, requestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY)) {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY // Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY
HookParams params = new HookParams() HookParams params = new HookParams()
.add(RequestDetails.class, requestDetails) .add(RequestDetails.class, requestDetails)
.addIfMatchesType(ServletRequestDetails.class, requestDetails); .addIfMatchesType(ServletRequestDetails.class, requestDetails);
requestPartitionId = (RequestPartitionId) doCallHooksAndReturnObject( requestPartitionId = (RequestPartitionId)
myInterceptorBroadcaster, requestDetails, Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params); compositeBroadcaster.callHooksAndReturnObject(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params);
} else if (hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_READ, myInterceptorBroadcaster, requestDetails)) { } else if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_READ)) {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_READ // Interceptor call: STORAGE_PARTITION_IDENTIFY_READ
HookParams params = new HookParams() HookParams params = new HookParams()
.add(RequestDetails.class, requestDetails) .add(RequestDetails.class, requestDetails)
.addIfMatchesType(ServletRequestDetails.class, requestDetails) .addIfMatchesType(ServletRequestDetails.class, requestDetails)
.add(ReadPartitionIdRequestDetails.class, theDetails); .add(ReadPartitionIdRequestDetails.class, theDetails);
requestPartitionId = (RequestPartitionId) doCallHooksAndReturnObject( requestPartitionId = (RequestPartitionId)
myInterceptorBroadcaster, requestDetails, Pointcut.STORAGE_PARTITION_IDENTIFY_READ, params); 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 {
IInterceptorBroadcaster compositeBroadcaster = CompositeInterceptorBroadcaster.newCompositeBroadcaster(
myInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY)) {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY // Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY
HookParams params = new HookParams() HookParams params = new HookParams()
.add(RequestDetails.class, theRequestDetails) .add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); .addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
requestPartitionId = (RequestPartitionId) doCallHooksAndReturnObject( requestPartitionId = (RequestPartitionId)
myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params); 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 {
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, requestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY)) {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY // Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY
HookParams params = new HookParams() HookParams params = new HookParams()
.add(RequestDetails.class, requestDetails) .add(RequestDetails.class, requestDetails)
.addIfMatchesType(ServletRequestDetails.class, requestDetails); .addIfMatchesType(ServletRequestDetails.class, requestDetails);
requestPartitionId = (RequestPartitionId) doCallHooksAndReturnObject( requestPartitionId = (RequestPartitionId)
myInterceptorBroadcaster, requestDetails, Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params); compositeBroadcaster.callHooksAndReturnObject(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params);
} else if (hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE, myInterceptorBroadcaster, requestDetails)) { } else if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE)) {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_CREATE // Interceptor call: STORAGE_PARTITION_IDENTIFY_CREATE
HookParams params = new HookParams() HookParams params = new HookParams()
.add(IBaseResource.class, theResource) .add(IBaseResource.class, theResource)
.add(RequestDetails.class, requestDetails) .add(RequestDetails.class, requestDetails)
.addIfMatchesType(ServletRequestDetails.class, requestDetails); .addIfMatchesType(ServletRequestDetails.class, requestDetails);
requestPartitionId = (RequestPartitionId) doCallHooksAndReturnObject( requestPartitionId = (RequestPartitionId) compositeBroadcaster.callHooksAndReturnObject(
myInterceptorBroadcaster, requestDetails, Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE, params); 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

@ -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;