
001/*- 002 * #%L 003 * HAPI FHIR - Server Framework 004 * %% 005 * Copyright (C) 2014 - 2025 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.rest.server.util; 021 022import ca.uhn.fhir.interceptor.api.Hook; 023import ca.uhn.fhir.interceptor.api.HookParams; 024import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; 025import ca.uhn.fhir.interceptor.api.Pointcut; 026import ca.uhn.fhir.interceptor.executor.BaseInterceptorService; 027import ca.uhn.fhir.rest.api.server.RequestDetails; 028import jakarta.annotation.Nonnull; 029import jakarta.annotation.Nullable; 030 031import java.util.ArrayList; 032import java.util.Arrays; 033import java.util.Collection; 034import java.util.Comparator; 035import java.util.List; 036import java.util.Objects; 037import java.util.stream.Collectors; 038 039import static org.apache.commons.lang3.ObjectUtils.getIfNull; 040 041/** 042 * This is an {@link IInterceptorBroadcaster} which combines multiple interceptor 043 * broadcasters. Hook methods are called across all broadcasters, respecting 044 * the {@link Hook#order()} across all broadcasters. 045 */ 046public class CompositeInterceptorBroadcaster implements IInterceptorBroadcaster { 047 048 private final List<IInterceptorBroadcaster> myServices; 049 050 /** 051 * Constructor 052 */ 053 private CompositeInterceptorBroadcaster(Collection<IInterceptorBroadcaster> theServices) { 054 myServices = theServices.stream().filter(Objects::nonNull).collect(Collectors.toList()); 055 } 056 057 @Override 058 public boolean callHooks(Pointcut thePointcut, HookParams theParams) { 059 assert BaseInterceptorService.haveAppropriateParams(thePointcut, theParams); 060 assert thePointcut.getReturnType() == void.class 061 || thePointcut.getReturnType() == thePointcut.getBooleanReturnTypeForEnum(); 062 063 List<IInvoker> invokers = getInvokersForPointcut(thePointcut); 064 Object retVal = BaseInterceptorService.callInvokers(thePointcut, theParams, invokers); 065 retVal = getIfNull(retVal, true); 066 return (Boolean) retVal; 067 } 068 069 @Override 070 public Object callHooksAndReturnObject(Pointcut thePointcut, HookParams theParams) { 071 assert BaseInterceptorService.haveAppropriateParams(thePointcut, theParams); 072 assert thePointcut.getReturnType() != void.class; 073 074 List<IInvoker> invokers = getInvokersForPointcut(thePointcut); 075 return BaseInterceptorService.callInvokers(thePointcut, theParams, invokers); 076 } 077 078 @Override 079 @Nonnull 080 public List<IInvoker> getInvokersForPointcut(Pointcut thePointcut) { 081 List<IInvoker> invokers = new ArrayList<>(); 082 for (IInterceptorBroadcaster services : myServices) { 083 if (services.hasHooks(thePointcut)) { 084 List<IInvoker> serviceInvokers = services.getInvokersForPointcut(thePointcut); 085 assert serviceInvokers != null; 086 invokers.addAll(serviceInvokers); 087 } 088 } 089 invokers.sort(Comparator.naturalOrder()); 090 return invokers; 091 } 092 093 @Override 094 public boolean hasHooks(Pointcut thePointcut) { 095 for (IInterceptorBroadcaster service : myServices) { 096 if (service.hasHooks(thePointcut)) { 097 return true; 098 } 099 } 100 return false; 101 } 102 103 /** 104 * @since 8.0.0 105 */ 106 public static IInterceptorBroadcaster newCompositeBroadcaster(IInterceptorBroadcaster... theServices) { 107 return new CompositeInterceptorBroadcaster(Arrays.asList(theServices)); 108 } 109 110 /** 111 * @since 5.5.0 112 */ 113 public static IInterceptorBroadcaster newCompositeBroadcaster( 114 @Nonnull IInterceptorBroadcaster theInterceptorBroadcaster, @Nullable RequestDetails theRequestDetails) { 115 assert theInterceptorBroadcaster != null; 116 if (theRequestDetails != null) { 117 IInterceptorBroadcaster requestBroadcaster = theRequestDetails.getInterceptorBroadcaster(); 118 if (requestBroadcaster != null) { 119 return newCompositeBroadcaster(theInterceptorBroadcaster, requestBroadcaster); 120 } 121 } 122 123 return newCompositeBroadcaster(theInterceptorBroadcaster); 124 } 125}