Address review comments
This commit is contained in:
parent
81de6cca32
commit
7359b77d99
|
@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.model.entity.*;
|
|||
import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||
import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
|
||||
|
|
|
@ -23,7 +23,9 @@ package ca.uhn.fhir.jpa.dao.index;
|
|||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Specimen;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.model.search.PerformanceMessage;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
|
@ -43,6 +45,8 @@ public class IdHelperService {
|
|||
protected IForcedIdDao myForcedIdDao;
|
||||
@Autowired(required = true)
|
||||
private DaoConfig myDaoConfig;
|
||||
@Autowired
|
||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
|
||||
public void delete(ForcedId forcedId) {
|
||||
myForcedIdDao.delete(forcedId);
|
||||
|
@ -51,7 +55,7 @@ public class IdHelperService {
|
|||
public Long translateForcedIdToPid(String theResourceName, String theResourceId) throws ResourceNotFoundException {
|
||||
// We only pass 1 input in so only 0..1 will come back
|
||||
IdDt id = new IdDt(theResourceName, theResourceId);
|
||||
List<Long> matches = translateForcedIdToPids(myDaoConfig, myForcedIdDao, Collections.singletonList(id));
|
||||
List<Long> matches = translateForcedIdToPids(myDaoConfig, myInterceptorBroadcaster, myForcedIdDao, Collections.singletonList(id));
|
||||
assert matches.size() <= 1;
|
||||
if (matches.isEmpty()) {
|
||||
throw new ResourceNotFoundException(id);
|
||||
|
@ -60,10 +64,10 @@ public class IdHelperService {
|
|||
}
|
||||
|
||||
public List<Long> translateForcedIdToPids(Collection<IIdType> theId) {
|
||||
return IdHelperService.translateForcedIdToPids(myDaoConfig, myForcedIdDao, theId);
|
||||
return IdHelperService.translateForcedIdToPids(myDaoConfig, myInterceptorBroadcaster, myForcedIdDao, theId);
|
||||
}
|
||||
|
||||
static List<Long> translateForcedIdToPids(DaoConfig theDaoConfig, IForcedIdDao theForcedIdDao, Collection<IIdType> theId) {
|
||||
static List<Long> translateForcedIdToPids(DaoConfig theDaoConfig, IInterceptorBroadcaster theInterceptorBroadcaster, IForcedIdDao theForcedIdDao, Collection<IIdType> theId) {
|
||||
theId.forEach(id -> Validate.isTrue(id.hasIdPart()));
|
||||
|
||||
if (theId.isEmpty()) {
|
||||
|
@ -89,7 +93,13 @@ public class IdHelperService {
|
|||
String nextResourceType = nextEntry.getKey();
|
||||
Collection<String> nextIds = nextEntry.getValue();
|
||||
if (isBlank(nextResourceType)) {
|
||||
|
||||
PerformanceMessage msg = new PerformanceMessage()
|
||||
.setMessage("This search uses unqualified resource IDs (an ID without a resource type). This is less efficient than using a qualified type.");
|
||||
theInterceptorBroadcaster.callHooks(Pointcut.PERFTRACE_MESSAGE, msg);
|
||||
|
||||
retVal.addAll(theForcedIdDao.findByForcedId(nextIds));
|
||||
|
||||
} else {
|
||||
retVal.addAll(theForcedIdDao.findByTypeAndForcedId(nextResourceType, nextIds));
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.entity;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
|
|
|
@ -7,24 +7,25 @@ import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
|
|||
import ca.uhn.fhir.util.LogUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.event.Level;
|
||||
|
||||
@Interceptor(manualRegistration = true)
|
||||
public class PerformanceTracingLoggingInterceptor {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(PerformanceTracingLoggingInterceptor.class);
|
||||
private final Logger myLog;
|
||||
private final LogUtil.Level myLevel;
|
||||
private final Level myLevel;
|
||||
|
||||
/**
|
||||
* Constructor that logs to this class with a level of INFO
|
||||
*/
|
||||
public PerformanceTracingLoggingInterceptor() {
|
||||
this(ourLog, LogUtil.Level.INFO);
|
||||
this(ourLog, Level.INFO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that logs to a custom logger and level
|
||||
*/
|
||||
public PerformanceTracingLoggingInterceptor(Logger theLog, LogUtil.Level theLevel) {
|
||||
public PerformanceTracingLoggingInterceptor(Logger theLog, Level theLevel) {
|
||||
myLog = theLog;
|
||||
myLevel = theLevel;
|
||||
}
|
||||
|
@ -39,6 +40,21 @@ public class PerformanceTracingLoggingInterceptor {
|
|||
log("Query found {} matches in {} for query {}", theOutcome.getFoundMatchesCount(), theOutcome.getQueryStopwatch(), theOutcome.getSearchUuid());
|
||||
}
|
||||
|
||||
@Hook(value = Pointcut.PERFTRACE_SEARCH_COMPLETE)
|
||||
public void searchComplete(SearchRuntimeDetails theOutcome) {
|
||||
log("Query {} is complete in {} - Found {} matches", theOutcome.getSearchUuid(), theOutcome.getQueryStopwatch(), theOutcome.getFoundMatchesCount());
|
||||
}
|
||||
|
||||
@Hook(value = Pointcut.PERFTRACE_SEARCH_PASS_COMPLETE)
|
||||
public void searchPassComplete(SearchRuntimeDetails theOutcome) {
|
||||
log("Query {} pass complete and set to status {} in {} - Found {} matches", theOutcome.getSearchUuid(), theOutcome.getSearchStatus(), theOutcome.getQueryStopwatch(), theOutcome.getFoundMatchesCount());
|
||||
}
|
||||
|
||||
@Hook(value = Pointcut.PERFTRACE_SEARCH_FAILED)
|
||||
public void searchFailed(SearchRuntimeDetails theOutcome) {
|
||||
log("Query {} failed in {} - Found {} matches", theOutcome.getSearchUuid(), theOutcome.getQueryStopwatch(), theOutcome.getFoundMatchesCount());
|
||||
}
|
||||
|
||||
private void log(String theMessage, Object... theArgs) {
|
||||
LogUtil.log(myLog, myLevel, theMessage, theArgs);
|
||||
}
|
||||
|
|
|
@ -26,16 +26,13 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.IDao;
|
||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.entity.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl.SearchTask;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundleProvider {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(PersistedJpaSearchFirstPageBundleProvider.class);
|
||||
|
|
|
@ -26,6 +26,9 @@ import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
|||
import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||
import ca.uhn.fhir.jpa.entity.*;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
|
@ -70,6 +73,7 @@ import javax.annotation.Nullable;
|
|||
import javax.annotation.PostConstruct;
|
||||
import javax.persistence.EntityManager;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
|
@ -97,6 +101,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
@Autowired
|
||||
private ISearchResultDao mySearchResultDao;
|
||||
@Autowired
|
||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
@Autowired
|
||||
private PlatformTransactionManager myManagedTxManager;
|
||||
@Autowired
|
||||
private DaoRegistry myDaoRegistry;
|
||||
|
@ -704,7 +710,13 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
}
|
||||
});
|
||||
|
||||
// FIXME: JA interceptor
|
||||
mySearchRuntimeDetails.setSearchStatus(mySearch.getStatus());
|
||||
if (mySearch.getStatus() == SearchStatusEnum.FINISHED) {
|
||||
myInterceptorBroadcaster.callHooks(Pointcut.PERFTRACE_SEARCH_COMPLETE, mySearchRuntimeDetails);
|
||||
} else {
|
||||
myInterceptorBroadcaster.callHooks(Pointcut.PERFTRACE_SEARCH_PASS_COMPLETE, mySearchRuntimeDetails);
|
||||
}
|
||||
|
||||
ourLog.info("Have completed search for [{}{}] and found {} resources in {}ms - Status is {}", mySearch.getResourceType(), mySearch.getSearchQueryString(), mySyncedPids.size(), sw.getMillis(), mySearch.getStatus());
|
||||
|
||||
} catch (Throwable t) {
|
||||
|
@ -742,6 +754,9 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
mySearch.setFailureCode(failureCode);
|
||||
mySearch.setStatus(SearchStatusEnum.FAILED);
|
||||
|
||||
mySearchRuntimeDetails.setSearchStatus(mySearch.getStatus());
|
||||
myInterceptorBroadcaster.callHooks(Pointcut.PERFTRACE_SEARCH_FAILED, mySearchRuntimeDetails);
|
||||
|
||||
saveSearch();
|
||||
|
||||
} finally {
|
||||
|
|
|
@ -57,12 +57,12 @@ public class SubscriptionInterceptorLoader {
|
|||
if (!supportedSubscriptionTypes.isEmpty()) {
|
||||
loadSubscriptions();
|
||||
ourLog.info("Registering subscription activating interceptor");
|
||||
myInterceptorRegistry.registerInterceptor(mySubscriptionActivatingInterceptor);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(mySubscriptionActivatingInterceptor);
|
||||
}
|
||||
if (myDaoConfig.isSubscriptionMatchingEnabled()) {
|
||||
mySubscriptionMatcherInterceptor.start();
|
||||
ourLog.info("Registering subscription matcher interceptor");
|
||||
myInterceptorRegistry.registerInterceptor(mySubscriptionMatcherInterceptor);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(mySubscriptionMatcherInterceptor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ public abstract class BaseJpaTest {
|
|||
|
||||
protected CountDownLatch registerLatchHookInterceptor(int theCount, Pointcut theLatchPointcut) {
|
||||
CountDownLatch deliveryLatch = new CountDownLatch(theCount);
|
||||
myInterceptorRegistry.registerAnonymousHookForUnitTest(theLatchPointcut, Integer.MAX_VALUE, t -> deliveryLatch.countDown());
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(theLatchPointcut, Integer.MAX_VALUE, (thePointcut, t) -> deliveryLatch.countDown());
|
||||
return deliveryLatch;
|
||||
}
|
||||
|
||||
|
|
|
@ -323,7 +323,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
|
|||
myDaoConfig.setInterceptors(myInterceptor);
|
||||
|
||||
myPerformanceTracingLoggingInterceptor = new PerformanceTracingLoggingInterceptor();
|
||||
myInterceptorRegistry.registerInterceptor(myPerformanceTracingLoggingInterceptor);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(myPerformanceTracingLoggingInterceptor);
|
||||
}
|
||||
|
||||
@Before
|
||||
|
|
|
@ -2,7 +2,7 @@ package ca.uhn.fhir.jpa.dao.r4;
|
|||
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.entity.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
|
|
|
@ -4,7 +4,7 @@ import ca.uhn.fhir.jpa.dao.DaoConfig;
|
|||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.entity.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
|
||||
import ca.uhn.fhir.jpa.util.TestUtil;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
|
|
|
@ -5,7 +5,7 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
|||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.entity.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
|
||||
|
|
|
@ -2,7 +2,7 @@ package ca.uhn.fhir.jpa.provider.r4;
|
|||
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.IAnonymousLambdaHook;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.IAnonymousInterceptor;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
|
@ -32,6 +32,7 @@ import static org.hamcrest.Matchers.hasItem;
|
|||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
|
||||
|
@ -137,13 +138,13 @@ public class CompositionDocumentR4Test extends BaseResourceProviderR4Test {
|
|||
@Test
|
||||
public void testInterceptorHookIsCalledForAllContents_RESOURCE_MAY_BE_RETURNED() throws IOException {
|
||||
|
||||
IAnonymousLambdaHook pointcut = mock(IAnonymousLambdaHook.class);
|
||||
myInterceptorRegistry.registerAnonymousHookForUnitTest(Pointcut.RESOURCE_MAY_BE_RETURNED, pointcut);
|
||||
IAnonymousInterceptor pointcut = mock(IAnonymousInterceptor.class);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.RESOURCE_MAY_BE_RETURNED, pointcut);
|
||||
|
||||
String theUrl = ourServerBase + "/" + compId + "/$document?_format=json";
|
||||
fetchBundle(theUrl, EncodingEnum.JSON);
|
||||
|
||||
Mockito.verify(pointcut, times(10)).invoke(myHookParamsCaptor.capture());
|
||||
Mockito.verify(pointcut, times(10)).invoke(eq(Pointcut.RESOURCE_MAY_BE_RETURNED), myHookParamsCaptor.capture());
|
||||
|
||||
List<String> returnedClasses = myHookParamsCaptor
|
||||
.getAllValues()
|
||||
|
|
|
@ -17,7 +17,7 @@ public class HookInterceptorR4Test extends BaseResourceProviderR4Test {
|
|||
|
||||
@Test
|
||||
public void testOP_PRESTORAGE_RESOURCE_CREATED_ModifyResource() {
|
||||
myInterceptorRegistry.registerAnonymousHookForUnitTest(Pointcut.OP_PRESTORAGE_RESOURCE_CREATED, t->{
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.OP_PRESTORAGE_RESOURCE_CREATED, (thePointcut, t)->{
|
||||
Patient contents = (Patient) t.get(IBaseResource.class, 0);
|
||||
contents.getNameFirstRep().setFamily("NEWFAMILY");
|
||||
});
|
||||
|
@ -36,7 +36,7 @@ public class HookInterceptorR4Test extends BaseResourceProviderR4Test {
|
|||
|
||||
@Test
|
||||
public void testOP_PRECOMMIT_RESOURCE_CREATED_ModifyResource() {
|
||||
myInterceptorRegistry.registerAnonymousHookForUnitTest(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, t->{
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, (thePointcut, t)->{
|
||||
Patient contents = (Patient) t.get(IBaseResource.class, 0);
|
||||
contents.getNameFirstRep().setFamily("NEWFAMILY");
|
||||
});
|
||||
|
@ -59,7 +59,7 @@ public class HookInterceptorR4Test extends BaseResourceProviderR4Test {
|
|||
p.setActive(true);
|
||||
IIdType id = ourClient.create().resource(p).execute().getId();
|
||||
|
||||
myInterceptorRegistry.registerAnonymousHookForUnitTest(Pointcut.OP_PRESTORAGE_RESOURCE_UPDATED, t->{
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.OP_PRESTORAGE_RESOURCE_UPDATED, (thePointcut, t)->{
|
||||
Patient contents = (Patient) t.get(IBaseResource.class, 1);
|
||||
contents.getNameFirstRep().setFamily("NEWFAMILY");
|
||||
});
|
||||
|
@ -83,7 +83,7 @@ public class HookInterceptorR4Test extends BaseResourceProviderR4Test {
|
|||
p.setActive(true);
|
||||
IIdType id = ourClient.create().resource(p).execute().getId();
|
||||
|
||||
myInterceptorRegistry.registerAnonymousHookForUnitTest(Pointcut.OP_PRECOMMIT_RESOURCE_UPDATED, t->{
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.OP_PRECOMMIT_RESOURCE_UPDATED, (thePointcut, t)->{
|
||||
Patient contents = (Patient) t.get(IBaseResource.class, 1);
|
||||
contents.getNameFirstRep().setFamily("NEWFAMILY");
|
||||
});
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.IAnonymousInterceptor;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
|
@ -13,6 +19,7 @@ import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
|
|||
import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
|
@ -29,14 +36,19 @@ import org.junit.After;
|
|||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.Matchers.in;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
|
@ -48,6 +60,7 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
private IServerOperationInterceptor myDaoInterceptor;
|
||||
|
||||
private IServerOperationInterceptor myServerInterceptor;
|
||||
private List<Object> myInterceptors = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
@After
|
||||
|
@ -55,7 +68,11 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
super.after();
|
||||
|
||||
myDaoConfig.getInterceptors().remove(myDaoInterceptor);
|
||||
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
|
||||
ourRestServer.unregisterInterceptor(myServerInterceptor);
|
||||
|
||||
myInterceptorRegistry.unregisterInterceptors(myInterceptors);
|
||||
myInterceptors.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,6 +107,51 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
}
|
||||
|
||||
@Mock
|
||||
private IAnonymousInterceptor myHook;
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<HookParams> myParamsCaptor;
|
||||
|
||||
@Test
|
||||
public void testPerfInterceptors() {
|
||||
myDaoConfig.setSearchPreFetchThresholds(Lists.newArrayList(15, 100));
|
||||
for (int i = 0; i < 30; i++) {
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("FAM"+i);
|
||||
myPatientDao.create(p);
|
||||
}
|
||||
|
||||
IAnonymousInterceptor interceptor = mock(IAnonymousInterceptor.class);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.PERFTRACE_SEARCH_FIRST_RESULT_LOADED, interceptor);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.PERFTRACE_SEARCH_COMPLETE, interceptor);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.PERFTRACE_SEARCH_FAILED, interceptor);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.PERFTRACE_SEARCH_PASS_COMPLETE, interceptor);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.PERFTRACE_SEARCH_SELECT_COMPLETE, interceptor);
|
||||
myInterceptors.add(interceptor);
|
||||
|
||||
Bundle results = ourClient.search().forResource(Patient.class).returnBundle(Bundle.class).execute();
|
||||
verify(interceptor, times(1)).invoke(eq(Pointcut.PERFTRACE_SEARCH_FIRST_RESULT_LOADED), myParamsCaptor.capture());
|
||||
verify(interceptor, times(1)).invoke(eq(Pointcut.PERFTRACE_SEARCH_SELECT_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, times(0)).invoke(eq(Pointcut.PERFTRACE_SEARCH_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, times(1)).invoke(eq(Pointcut.PERFTRACE_SEARCH_PASS_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, times(0)).invoke(eq(Pointcut.PERFTRACE_SEARCH_FAILED), myParamsCaptor.capture());
|
||||
|
||||
SearchRuntimeDetails details = myParamsCaptor.getAllValues().get(0).get(SearchRuntimeDetails.class);
|
||||
assertEquals(SearchStatusEnum.PASSCMPLET, details.getSearchStatus());
|
||||
|
||||
// Load the next (and final) page
|
||||
reset(interceptor);
|
||||
results = ourClient.loadPage().next(results).execute();
|
||||
verify(interceptor, times(1)).invoke(eq(Pointcut.PERFTRACE_SEARCH_FIRST_RESULT_LOADED), myParamsCaptor.capture());
|
||||
verify(interceptor, times(1)).invoke(eq(Pointcut.PERFTRACE_SEARCH_SELECT_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, times(1)).invoke(eq(Pointcut.PERFTRACE_SEARCH_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, times(0)).invoke(eq(Pointcut.PERFTRACE_SEARCH_PASS_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, times(0)).invoke(eq(Pointcut.PERFTRACE_SEARCH_FAILED), myParamsCaptor.capture());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateConditionalNoOpResourceInTransaction() throws Exception {
|
||||
String methodName = "foo";
|
||||
|
@ -154,15 +216,12 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
|
||||
HttpPost post = new HttpPost(ourServerBase + "/Patient");
|
||||
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
try {
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(post)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response was: {}", resp);
|
||||
assertEquals(201, response.getStatusLine().getStatusCode());
|
||||
String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue();
|
||||
assertThat(newIdString, startsWith(ourServerBase + "/Patient/"));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
|
@ -235,7 +294,7 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testCreateReflexResourceTheHardWay() throws IOException, ServletException {
|
||||
public void testCreateReflexResourceTheHardWay() {
|
||||
ServerOperationInterceptorAdapter interceptor = new ReflexInterceptor();
|
||||
|
||||
ourRestServer.registerInterceptor(interceptor);
|
||||
|
@ -260,7 +319,7 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Test
|
||||
public void testCreateResourceWithVersionedReference() throws IOException, ServletException {
|
||||
String methodName = "testCreateResourceWithVersionedReference";
|
||||
|
||||
|
@ -287,15 +346,12 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
|
||||
HttpPost post = new HttpPost(ourServerBase + "/Patient");
|
||||
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
try {
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(post)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response was: {}", resp);
|
||||
assertEquals(201, response.getStatusLine().getStatusCode());
|
||||
String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue();
|
||||
assertThat(newIdString, startsWith(ourServerBase + "/Patient/"));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
|
@ -371,11 +427,8 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
String resource = myFhirCtx.newXmlParser().encodeResourceToString(theBundle);
|
||||
HttpPost post = new HttpPost(ourServerBase + "/");
|
||||
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
try {
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(post)) {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.provider.r4;
|
|||
|
||||
import ca.uhn.fhir.jpa.entity.*;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
|
||||
import ca.uhn.fhir.rest.gclient.IClientExecutable;
|
||||
import ca.uhn.fhir.rest.gclient.IQuery;
|
||||
|
|
|
@ -7,7 +7,7 @@ import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao;
|
|||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.entity.SearchResult;
|
||||
import ca.uhn.fhir.jpa.entity.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.util.BaseIterator;
|
||||
|
|
|
@ -82,7 +82,7 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
@Before
|
||||
public void beforeRegisterRestHookListener() {
|
||||
mySubscriptionTestUtil.registerRestHookInterceptor();
|
||||
myInterceptorRegistry.registerInterceptor(ourSubscriptionDebugLogInterceptor);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(ourSubscriptionDebugLogInterceptor);
|
||||
}
|
||||
|
||||
@Before
|
||||
|
|
|
@ -132,7 +132,7 @@ public class RestHookWithInterceptorR4Test extends BaseSubscriptionsR4Test {
|
|||
@Test
|
||||
public void testAttributesAreCopiedAlongPipeline() throws Exception {
|
||||
AttributeCarryingInterceptor interceptor = new AttributeCarryingInterceptor();
|
||||
myInterceptorRegistry.registerInterceptor(interceptor);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(interceptor);
|
||||
try {
|
||||
|
||||
// Create a subscription
|
||||
|
@ -191,7 +191,7 @@ public class RestHookWithInterceptorR4Test extends BaseSubscriptionsR4Test {
|
|||
registerLatch.await(10, TimeUnit.SECONDS);
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
myInterceptorRegistry.registerAnonymousHookForUnitTest(Pointcut.SUBSCRIPTION_AFTER_DELIVERY_FAILED, params -> {
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.SUBSCRIPTION_AFTER_DELIVERY_FAILED, (thePointcut, params) -> {
|
||||
latch.countDown();
|
||||
});
|
||||
|
||||
|
@ -222,9 +222,9 @@ public class RestHookWithInterceptorR4Test extends BaseSubscriptionsR4Test {
|
|||
}).when(loggerMock).debug(any(), ArgumentMatchers.<Object[]>any());
|
||||
|
||||
SubscriptionDebugLogInterceptor interceptor = new SubscriptionDebugLogInterceptor();
|
||||
myInterceptorRegistry.registerInterceptor(interceptor);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(interceptor);
|
||||
SubscriptionDebugLogInterceptor interceptor2 = new SubscriptionDebugLogInterceptor(t -> loggerMock, Level.DEBUG);
|
||||
myInterceptorRegistry.registerInterceptor(interceptor2);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(interceptor2);
|
||||
try {
|
||||
|
||||
String payload = "application/json";
|
||||
|
@ -310,7 +310,7 @@ public class RestHookWithInterceptorR4Test extends BaseSubscriptionsR4Test {
|
|||
@Bean
|
||||
public MyTestInterceptor interceptor() {
|
||||
MyTestInterceptor retVal = new MyTestInterceptor();
|
||||
myInterceptorRegistry.registerInterceptor(retVal);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,11 @@ public class HookParams {
|
|||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(Class<T> theParamType) {
|
||||
return get(theParamType, 0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(Class<T> theParamType, int theIndex) {
|
||||
List<Object> objects = myParams.get(theParamType);
|
||||
Object retVal = null;
|
||||
|
|
|
@ -29,8 +29,8 @@ import com.google.common.annotations.VisibleForTesting;
|
|||
*/
|
||||
@FunctionalInterface
|
||||
@VisibleForTesting
|
||||
public interface IAnonymousLambdaHook {
|
||||
public interface IAnonymousInterceptor {
|
||||
|
||||
void invoke(HookParams theArgs);
|
||||
void invoke(Pointcut thePointcut, HookParams theArgs);
|
||||
|
||||
}
|
|
@ -22,6 +22,9 @@ package ca.uhn.fhir.jpa.model.interceptor.api;
|
|||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
|
||||
public interface IInterceptorRegistry {
|
||||
|
||||
int DEFAULT_ORDER = 0;
|
||||
|
@ -56,7 +59,7 @@ public interface IInterceptorRegistry {
|
|||
* @param theInterceptor The interceptor to register
|
||||
* @return Returns <code>true</code> if at least one valid hook method was found on this interceptor
|
||||
*/
|
||||
boolean registerInterceptor(Object theInterceptor);
|
||||
boolean registerAnonymousInterceptor(Object theInterceptor);
|
||||
|
||||
/**
|
||||
* Unregister an interceptor. This method has no effect if the given interceptor is not already registered.
|
||||
|
@ -77,12 +80,12 @@ public interface IInterceptorRegistry {
|
|||
@Deprecated
|
||||
void unregisterGlobalInterceptor(Object theInterceptor);
|
||||
|
||||
@VisibleForTesting
|
||||
void registerAnonymousHookForUnitTest(Pointcut thePointcut, IAnonymousLambdaHook theHook);
|
||||
void registerAnonymousInterceptor(Pointcut thePointcut, IAnonymousInterceptor theInterceptor);
|
||||
|
||||
@VisibleForTesting
|
||||
void registerAnonymousHookForUnitTest(Pointcut thePointcut, int theOrder, IAnonymousLambdaHook theHook);
|
||||
void registerAnonymousInterceptor(Pointcut thePointcut, int theOrder, IAnonymousInterceptor theInterceptor);
|
||||
|
||||
@VisibleForTesting
|
||||
void clearAnonymousHookForUnitTest();
|
||||
|
||||
void unregisterInterceptors(@Nullable Collection<?> theInterceptors);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.model.interceptor.api;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.model.search.PerformanceMessage;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -406,8 +407,93 @@ public enum Pointcut {
|
|||
* Hooks should return <code>void</code>.
|
||||
* </p>
|
||||
*/
|
||||
PERFTRACE_SEARCH_SELECT_COMPLETE(SearchRuntimeDetails.class.getName());
|
||||
PERFTRACE_SEARCH_SELECT_COMPLETE(SearchRuntimeDetails.class.getName()),
|
||||
|
||||
/**
|
||||
* Note that this is a performance tracing hook. Use with caution in production
|
||||
* systems, since calling it may (or may not) carry a cost.
|
||||
* <p>
|
||||
* This hook is invoked when a search has failed for any reason. When this pointcut
|
||||
* is invoked, the search has completed unsuccessfully and will not be continued.
|
||||
* </p>
|
||||
* Hooks may accept the following parameters:
|
||||
* <ul>
|
||||
* <li>
|
||||
* ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails - Contains details about the search being
|
||||
* performed. Hooks should not modify this object.
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Hooks should return <code>void</code>.
|
||||
* </p>
|
||||
*/
|
||||
PERFTRACE_SEARCH_FAILED(SearchRuntimeDetails.class.getName()),
|
||||
|
||||
/**
|
||||
* Note that this is a performance tracing hook. Use with caution in production
|
||||
* systems, since calling it may (or may not) carry a cost.
|
||||
* <p>
|
||||
* This hook is invoked when a search has failed for any reason. When this pointcut
|
||||
* is invoked, a pass in the Search Coordinator has completed successfully, but
|
||||
* not all possible resources have been loaded yet so a future paging request
|
||||
* may trigger a new task that will load further resources.
|
||||
* </p>
|
||||
* Hooks may accept the following parameters:
|
||||
* <ul>
|
||||
* <li>
|
||||
* ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails - Contains details about the search being
|
||||
* performed. Hooks should not modify this object.
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Hooks should return <code>void</code>.
|
||||
* </p>
|
||||
*/
|
||||
PERFTRACE_SEARCH_PASS_COMPLETE(SearchRuntimeDetails.class.getName()),
|
||||
|
||||
/**
|
||||
* Note that this is a performance tracing hook. Use with caution in production
|
||||
* systems, since calling it may (or may not) carry a cost.
|
||||
* <p>
|
||||
* This hook is invoked when a search has failed for any reason. When this pointcut
|
||||
* is invoked, a pass in the Search Coordinator has completed successfully, and all
|
||||
* possible results have been fetched and loaded into the query cache.
|
||||
* </p>
|
||||
* Hooks may accept the following parameters:
|
||||
* <ul>
|
||||
* <li>
|
||||
* ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails - Contains details about the search being
|
||||
* performed. Hooks should not modify this object.
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Hooks should return <code>void</code>.
|
||||
* </p>
|
||||
*/
|
||||
PERFTRACE_SEARCH_COMPLETE(SearchRuntimeDetails.class.getName()),
|
||||
|
||||
|
||||
/**
|
||||
* Note that this is a performance tracing hook. Use with caution in production
|
||||
* systems, since calling it may (or may not) carry a cost.
|
||||
* <p>
|
||||
* This hook is invoked when any informational or warning messages generated by the
|
||||
* SearchCoordinator are created. It is typically used to provide logging
|
||||
* or capture details related to a specific request.
|
||||
* </p>
|
||||
* Hooks may accept the following parameters:
|
||||
* <ul>
|
||||
* <li>
|
||||
* {@link PerformanceMessage} - Contains the message
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Hooks should return <code>void</code>.
|
||||
* </p>
|
||||
*/
|
||||
PERFTRACE_MESSAGE(PerformanceMessage.class.getName())
|
||||
|
||||
;
|
||||
private final List<String> myParameterTypes;
|
||||
|
||||
Pointcut(String... theParameterTypes) {
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.springframework.core.annotation.AnnotationUtils;
|
|||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
@ -43,22 +44,37 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
|
|||
private final ListMultimap<Pointcut, BaseInvoker> myGlobalInvokers = ArrayListMultimap.create();
|
||||
private final ListMultimap<Pointcut, BaseInvoker> myAnonymousInvokers = ArrayListMultimap.create();
|
||||
private final Object myRegistryMutex = new Object();
|
||||
private String myName;
|
||||
private final ThreadLocal<ListMultimap<Pointcut, BaseInvoker>> myThreadlocalInvokers = new ThreadLocal<>();
|
||||
private boolean myThreadlocalInvokersEnabled;
|
||||
private String myName;
|
||||
private boolean myThreadlocalInvokersEnabled = true;
|
||||
|
||||
/**
|
||||
* Constructor which uses a default name of "default"
|
||||
*/
|
||||
public InterceptorService() {
|
||||
this("default");
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theName The name for this registry (useful for troubleshooting)
|
||||
*/
|
||||
public InterceptorService(String theName) {
|
||||
super();
|
||||
myName = theName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are threadlocal interceptors enabled on this registry (defaults to true)
|
||||
*/
|
||||
public boolean isThreadlocalInvokersEnabled() {
|
||||
return myThreadlocalInvokersEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are threadlocal interceptors enabled on this registry (defaults to true)
|
||||
*/
|
||||
public void setThreadlocalInvokersEnabled(boolean theThreadlocalInvokersEnabled) {
|
||||
myThreadlocalInvokersEnabled = theThreadlocalInvokersEnabled;
|
||||
}
|
||||
|
@ -71,8 +87,8 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
|
|||
|
||||
@Override
|
||||
@VisibleForTesting
|
||||
public void registerAnonymousHookForUnitTest(Pointcut thePointcut, IAnonymousLambdaHook theHook) {
|
||||
registerAnonymousHookForUnitTest(thePointcut, DEFAULT_ORDER, theHook);
|
||||
public void registerAnonymousInterceptor(Pointcut thePointcut, IAnonymousInterceptor theInterceptor) {
|
||||
registerAnonymousInterceptor(thePointcut, DEFAULT_ORDER, theInterceptor);
|
||||
}
|
||||
|
||||
public void setName(String theName) {
|
||||
|
@ -80,17 +96,27 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
|
|||
}
|
||||
|
||||
@Override
|
||||
public void registerAnonymousHookForUnitTest(Pointcut thePointcut, int theOrder, IAnonymousLambdaHook theHook) {
|
||||
public void registerAnonymousInterceptor(Pointcut thePointcut, int theOrder, IAnonymousInterceptor theInterceptor) {
|
||||
Validate.notNull(thePointcut);
|
||||
Validate.notNull(theHook);
|
||||
|
||||
myAnonymousInvokers.put(thePointcut, new AnonymousLambdaInvoker(theHook, theOrder));
|
||||
Validate.notNull(theInterceptor);
|
||||
synchronized (myRegistryMutex) {
|
||||
myAnonymousInvokers.put(thePointcut, new AnonymousLambdaInvoker(thePointcut, theInterceptor, theOrder));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@VisibleForTesting
|
||||
public void clearAnonymousHookForUnitTest() {
|
||||
myAnonymousInvokers.clear();
|
||||
synchronized (myRegistryMutex) {
|
||||
myAnonymousInvokers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterInterceptors(@Nullable Collection<?> theInterceptors) {
|
||||
if (theInterceptors != null) {
|
||||
theInterceptors.forEach(t -> unregisterInterceptor(t));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -125,7 +151,7 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean registerInterceptor(Object theInterceptor) {
|
||||
public boolean registerAnonymousInterceptor(Object theInterceptor) {
|
||||
synchronized (myRegistryMutex) {
|
||||
|
||||
if (isInterceptorAlreadyRegistered(theInterceptor)) {
|
||||
|
@ -159,12 +185,13 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
|
|||
synchronized (myRegistryMutex) {
|
||||
myInterceptors.removeIf(t -> t == theInterceptor);
|
||||
myGlobalInvokers.entries().removeIf(t -> t.getValue().getInterceptor() == theInterceptor);
|
||||
myAnonymousInvokers.entries().removeIf(t -> t.getValue().getInterceptor() == theInterceptor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerGlobalInterceptor(Object theInterceptor) {
|
||||
return registerInterceptor(theInterceptor);
|
||||
return registerAnonymousInterceptor(theInterceptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -281,8 +308,8 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
|
|||
|
||||
retVal = Arrays
|
||||
.stream(theInvokersLists)
|
||||
.filter(t->t!= null)
|
||||
.flatMap(t->t.stream())
|
||||
.filter(t -> t != null)
|
||||
.flatMap(t -> t.stream())
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
|
@ -312,16 +339,18 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
|
|||
}
|
||||
|
||||
private class AnonymousLambdaInvoker extends BaseInvoker {
|
||||
private final IAnonymousLambdaHook myHook;
|
||||
private final IAnonymousInterceptor myHook;
|
||||
private final Pointcut myPointcut;
|
||||
|
||||
public AnonymousLambdaInvoker(IAnonymousLambdaHook theHook, int theOrder) {
|
||||
public AnonymousLambdaInvoker(Pointcut thePointcut, IAnonymousInterceptor theHook, int theOrder) {
|
||||
super(theHook, theOrder);
|
||||
myHook = theHook;
|
||||
myPointcut = thePointcut;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean invoke(HookParams theParams) {
|
||||
myHook.invoke(theParams);
|
||||
myHook.invoke(myPointcut, theParams);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package ca.uhn.fhir.jpa.model.search;
|
||||
|
||||
public class PerformanceMessage {
|
||||
|
||||
private String myMessage;
|
||||
|
||||
public String getMessage() {
|
||||
return myMessage;
|
||||
}
|
||||
|
||||
public PerformanceMessage setMessage(String theMessage) {
|
||||
myMessage = theMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
|
@ -12,6 +12,7 @@ public class SearchRuntimeDetails {
|
|||
private int myFoundMatchesCount;
|
||||
private boolean myLoadSynchronous;
|
||||
private String myQueryString;
|
||||
private SearchStatusEnum mySearchStatus;
|
||||
|
||||
public SearchRuntimeDetails(String theSearchUuid) {
|
||||
mySearchUuid = theSearchUuid;
|
||||
|
@ -52,4 +53,12 @@ public class SearchRuntimeDetails {
|
|||
public String getQueryString() {
|
||||
return myQueryString;
|
||||
}
|
||||
|
||||
public SearchStatusEnum getSearchStatus() {
|
||||
return mySearchStatus;
|
||||
}
|
||||
|
||||
public void setSearchStatus(SearchStatusEnum theSearchStatus) {
|
||||
mySearchStatus = theSearchStatus;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package ca.uhn.fhir.jpa.entity;
|
||||
package ca.uhn.fhir.jpa.model.search;
|
||||
|
||||
/*-
|
||||
* #%L
|
|
@ -1,6 +1,9 @@
|
|||
package ca.uhn.fhir.jpa.model.interceptor.executor;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.*;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.Interceptor;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -14,7 +17,6 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
@ -50,7 +52,7 @@ public class InterceptorServiceTest {
|
|||
int initialSize = myInterceptorRegistry.getGlobalInterceptorsForUnitTest().size();
|
||||
|
||||
try {
|
||||
myInterceptorRegistry.registerInterceptor(new InterceptorThatFailsOnRegister());
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(new InterceptorThatFailsOnRegister());
|
||||
fail();
|
||||
} catch (InternalErrorException e) {
|
||||
// good
|
||||
|
@ -72,7 +74,7 @@ public class InterceptorServiceTest {
|
|||
public void testManuallyRegisterGlobalInterceptor() {
|
||||
|
||||
// Register the manual interceptor (has @Order right in the middle)
|
||||
myInterceptorRegistry.registerInterceptor(myInterceptorManual);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(myInterceptorManual);
|
||||
List<Object> globalInterceptors = myInterceptorRegistry.getGlobalInterceptorsForUnitTest();
|
||||
assertEquals(3, globalInterceptors.size());
|
||||
assertTrue(globalInterceptors.get(0).getClass().toString(), globalInterceptors.get(0) instanceof MyTestInterceptorOne);
|
||||
|
@ -80,7 +82,7 @@ public class InterceptorServiceTest {
|
|||
assertTrue(globalInterceptors.get(2).getClass().toString(), globalInterceptors.get(2) instanceof MyTestInterceptorTwo);
|
||||
|
||||
// Try to register again (should have no effect
|
||||
myInterceptorRegistry.registerInterceptor(myInterceptorManual);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(myInterceptorManual);
|
||||
globalInterceptors = myInterceptorRegistry.getGlobalInterceptorsForUnitTest();
|
||||
assertEquals(3, globalInterceptors.size());
|
||||
assertTrue(globalInterceptors.get(0).getClass().toString(), globalInterceptors.get(0) instanceof MyTestInterceptorOne);
|
||||
|
@ -263,7 +265,7 @@ public class InterceptorServiceTest {
|
|||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
@Ignore("Performance test - Not needed normally")
|
||||
public void testThreadLocalHookInterceptorMicroBenchmark() {
|
||||
threadLocalMicroBenchmark(true, 500000);
|
||||
threadLocalMicroBenchmark(false, 500000);
|
||||
|
@ -328,7 +330,6 @@ public class InterceptorServiceTest {
|
|||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = "ca.uhn.fhir.jpa.model")
|
||||
static class InterceptorRegistryTestCtxConfig {
|
||||
|
||||
@Bean
|
||||
|
@ -343,7 +344,7 @@ public class InterceptorServiceTest {
|
|||
@Bean
|
||||
public MyTestInterceptorTwo interceptor1() {
|
||||
MyTestInterceptorTwo retVal = new MyTestInterceptorTwo();
|
||||
interceptorService().registerInterceptor(retVal);
|
||||
interceptorService().registerAnonymousInterceptor(retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -354,7 +355,7 @@ public class InterceptorServiceTest {
|
|||
@Bean
|
||||
public MyTestInterceptorOne interceptor2() {
|
||||
MyTestInterceptorOne retVal = new MyTestInterceptorOne();
|
||||
interceptorService().registerInterceptor(retVal);
|
||||
interceptorService().registerAnonymousInterceptor(retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.subscription.module;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.IAnonymousLambdaHook;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.IAnonymousInterceptor;
|
||||
import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -15,7 +15,7 @@ import java.util.stream.Collectors;
|
|||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class PointcutLatch implements IAnonymousLambdaHook {
|
||||
public class PointcutLatch implements IAnonymousInterceptor {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(PointcutLatch.class);
|
||||
private static final int DEFAULT_TIMEOUT_SECONDS = 10;
|
||||
private static final FhirObjectPrinter ourFhirObjectToStringMapper = new FhirObjectPrinter();
|
||||
|
@ -25,9 +25,11 @@ public class PointcutLatch implements IAnonymousLambdaHook {
|
|||
private CountDownLatch myCountdownLatch;
|
||||
private AtomicReference<String> myFailure;
|
||||
private AtomicReference<List<HookParams>> myCalledWith;
|
||||
private Pointcut myPointcut;
|
||||
|
||||
public PointcutLatch(Pointcut thePointcut) {
|
||||
this.name = thePointcut.name();
|
||||
myPointcut = thePointcut;
|
||||
}
|
||||
|
||||
public PointcutLatch(String theName) {
|
||||
|
@ -105,7 +107,7 @@ public class PointcutLatch implements IAnonymousLambdaHook {
|
|||
|
||||
|
||||
@Override
|
||||
public void invoke(HookParams theArgs) {
|
||||
public void invoke(Pointcut thePointcut, HookParams theArgs) {
|
||||
if (myCountdownLatch == null) {
|
||||
throw new PointcutLatchException("invoke() called before setExpectedCount() called.", theArgs);
|
||||
} else if (myCountdownLatch.getCount() <= 0) {
|
||||
|
@ -121,7 +123,7 @@ public class PointcutLatch implements IAnonymousLambdaHook {
|
|||
}
|
||||
|
||||
public void call(Object arg) {
|
||||
this.invoke(new HookParams(arg));
|
||||
this.invoke(myPointcut, new HookParams(arg));
|
||||
}
|
||||
|
||||
private class PointcutLatchException extends IllegalStateException {
|
||||
|
|
|
@ -78,8 +78,8 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base
|
|||
ourSubscribableChannel = mySubscriptionChannelFactory.newDeliveryChannel("test", Subscription.SubscriptionChannelType.RESTHOOK.toCode().toLowerCase());
|
||||
ourSubscribableChannel.subscribe(myStandaloneSubscriptionMessageHandler);
|
||||
}
|
||||
myInterceptorRegistry.registerAnonymousHookForUnitTest(Pointcut.SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED, mySubscriptionMatchingPost);
|
||||
myInterceptorRegistry.registerAnonymousHookForUnitTest(Pointcut.SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED, mySubscriptionActivatedPost);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED, mySubscriptionMatchingPost);
|
||||
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED, mySubscriptionActivatedPost);
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -171,7 +171,7 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base
|
|||
public MethodOutcome update(@ResourceParam Observation theObservation, HttpServletRequest theRequest) {
|
||||
ourContentTypes.add(theRequest.getHeader(Constants.HEADER_CONTENT_TYPE).replaceAll(";.*", ""));
|
||||
ourUpdatedObservations.add(theObservation);
|
||||
updateLatch.invoke(new HookParams().add(Observation.class, theObservation));
|
||||
updateLatch.invoke(null, new HookParams().add(Observation.class, theObservation));
|
||||
ourLog.info("Received Listener Update (now have {} updates)", ourUpdatedObservations.size());
|
||||
return new MethodOutcome(new IdType("Observation/1"), false);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue