Fixes to migrator and count logic

This commit is contained in:
James Agnew 2018-10-13 12:02:19 -04:00
parent eb7b5ca02a
commit 51a69f0dc9
11 changed files with 560 additions and 90 deletions

View File

@ -32,7 +32,10 @@ import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
import ca.uhn.fhir.jpa.subscription.websocket.SubscriptionWebsocketInterceptor; import ca.uhn.fhir.jpa.subscription.websocket.SubscriptionWebsocketInterceptor;
import ca.uhn.fhir.jpa.util.IReindexController; import ca.uhn.fhir.jpa.util.IReindexController;
import ca.uhn.fhir.jpa.util.ReindexController; import ca.uhn.fhir.jpa.util.ReindexController;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.jpa.HibernatePersistenceProvider; import org.hibernate.jpa.HibernatePersistenceProvider;
import org.hibernate.query.criteria.LiteralHandlingMode;
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -52,6 +55,7 @@ import org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean;
import org.springframework.scheduling.config.ScheduledTaskRegistrar; import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@Configuration @Configuration
@ -85,7 +89,22 @@ public abstract class BaseConfig implements SchedulingConfigurer {
* factory with HAPI FHIR customizations * factory with HAPI FHIR customizations
*/ */
protected LocalContainerEntityManagerFactoryBean entityManagerFactory() { protected LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean(); LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean() {
@Override
public Map<String, Object> getJpaPropertyMap() {
Map<String, Object> retVal = super.getJpaPropertyMap();
if (!retVal.containsKey(AvailableSettings.CRITERIA_LITERAL_HANDLING_MODE)) {
retVal.put(AvailableSettings.CRITERIA_LITERAL_HANDLING_MODE, LiteralHandlingMode.BIND);
}
if (!retVal.containsKey(AvailableSettings.CONNECTION_HANDLING)) {
retVal.put(AvailableSettings.CONNECTION_HANDLING, PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_HOLD);
}
return retVal;
}
};
configureEntityManagerFactory(retVal, fhirContext()); configureEntityManagerFactory(retVal, fhirContext());
return retVal; return retVal;
} }

View File

@ -150,6 +150,13 @@ public class Search implements Serializable {
myFailureMessage = left(theFailureMessage, FAILURE_MESSAGE_LENGTH); myFailureMessage = left(theFailureMessage, FAILURE_MESSAGE_LENGTH);
} }
// FIXME: remove this
private static final Logger ourLog = LoggerFactory.getLogger(Search.class);
@PrePersist
public void prePersist() {
ourLog.info("*** SAVING WITH VERSION {} TOTAL {}", myVersion, myTotalCount);
}
public Long getId() { public Long getId() {
return myId; return myId;
} }

View File

@ -111,8 +111,9 @@ public class SearchParamPresent implements Serializable {
return b.build(); return b.build();
} }
public static long calculateHashPresence(String theResourceType, String theParamName, boolean thePresent) { public static long calculateHashPresence(String theResourceType, String theParamName, Boolean thePresent) {
return BaseResourceIndexedSearchParam.hash(theResourceType, theParamName, Boolean.toString(thePresent)); String string = thePresent != null ? Boolean.toString(thePresent) : Boolean.toString(false);
return BaseResourceIndexedSearchParam.hash(theResourceType, theParamName, string);
} }
} }

View File

@ -24,10 +24,15 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IDao; import ca.uhn.fhir.jpa.dao.IDao;
import ca.uhn.fhir.jpa.dao.ISearchBuilder; import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.dao.data.ISearchDao; import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
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.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
@ -46,6 +51,7 @@ import java.util.*;
public class PersistedJpaBundleProvider implements IBundleProvider { public class PersistedJpaBundleProvider implements IBundleProvider {
private static final Logger ourLog = LoggerFactory.getLogger(PersistedJpaBundleProvider.class);
private FhirContext myContext; private FhirContext myContext;
private IDao myDao; private IDao myDao;
private EntityManager myEntityManager; private EntityManager myEntityManager;
@ -149,11 +155,10 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
if (mySearchEntity == null) { if (mySearchEntity == null) {
ensureDependenciesInjected(); ensureDependenciesInjected();
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager); TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
template.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
return template.execute(new TransactionCallback<Boolean>() { txTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
@Override return txTemplate.execute(s -> {
public Boolean doInTransaction(TransactionStatus theStatus) {
try { try {
setSearchEntity(mySearchDao.findByUuid(myUuid)); setSearchEntity(mySearchDao.findByUuid(myUuid));
@ -161,6 +166,9 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
return false; return false;
} }
// FIXME: remove
ourLog.info("** Retrieved search with version {} and total {}", mySearchEntity.getVersion(), mySearchEntity.getTotalCount());
// Load the includes now so that they are available outside of this transaction // Load the includes now so that they are available outside of this transaction
mySearchEntity.getIncludes().size(); mySearchEntity.getIncludes().size();
@ -168,7 +176,6 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
} catch (NoResultException e) { } catch (NoResultException e) {
return false; return false;
} }
}
}); });
} }
return true; return true;

View File

@ -56,10 +56,13 @@ import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.transaction.TransactionManager;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
@ -421,41 +424,39 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
} }
public abstract class BaseTask implements Callable<Void> { public abstract class BaseTask implements Callable<Void> {
protected Search getSearch() {
return getSearch;
}
private final Search getSearch;
private final SearchParameterMap myParams; private final SearchParameterMap myParams;
private final IDao myCallingDao; private final IDao myCallingDao;
private final String myResourceType; private final String myResourceType;
private final ArrayList<Long> mySyncedPids = new ArrayList<>(); private final ArrayList<Long> mySyncedPids = new ArrayList<>();
protected CountDownLatch getInitialCollectionLatch() {
return myInitialCollectionLatch;
}
private final CountDownLatch myInitialCollectionLatch = new CountDownLatch(1); private final CountDownLatch myInitialCollectionLatch = new CountDownLatch(1);
private final CountDownLatch myCompletionLatch; private final CountDownLatch myCompletionLatch;
private final ArrayList<Long> myUnsyncedPids = new ArrayList<>(); private final ArrayList<Long> myUnsyncedPids = new ArrayList<>();
private Search mySearch;
private boolean myAbortRequested; private boolean myAbortRequested;
private int myCountSaved = 0; private int myCountSaved = 0;
private boolean myAdditionalPrefetchThresholdsRemaining; private boolean myAdditionalPrefetchThresholdsRemaining;
private List<Long> myPreviouslyAddedResourcePids; private List<Long> myPreviouslyAddedResourcePids;
private Integer myMaxResultsToFetch; private Integer myMaxResultsToFetch;
private int myCountFetchedDuringThisPass; private int myCountFetchedDuringThisPass;
/** /**
* Constructor * Constructor
*/ */
protected BaseTask(Search theSearch, IDao theCallingDao, SearchParameterMap theParams, String theResourceType) { protected BaseTask(Search theSearch, IDao theCallingDao, SearchParameterMap theParams, String theResourceType) {
getSearch = theSearch; mySearch = theSearch;
myCallingDao = theCallingDao; myCallingDao = theCallingDao;
myParams = theParams; myParams = theParams;
myResourceType = theResourceType; myResourceType = theResourceType;
myCompletionLatch = new CountDownLatch(1); myCompletionLatch = new CountDownLatch(1);
} }
protected Search getSearch() {
return mySearch;
}
protected CountDownLatch getInitialCollectionLatch() {
return myInitialCollectionLatch;
}
protected void setPreviouslyAddedResourcePids(List<Long> thePreviouslyAddedResourcePids) { protected void setPreviouslyAddedResourcePids(List<Long> thePreviouslyAddedResourcePids) {
myPreviouslyAddedResourcePids = thePreviouslyAddedResourcePids; myPreviouslyAddedResourcePids = thePreviouslyAddedResourcePids;
myCountSaved = myPreviouslyAddedResourcePids.size(); myCountSaved = myPreviouslyAddedResourcePids.size();
@ -475,8 +476,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
boolean keepWaiting; boolean keepWaiting;
do { do {
synchronized (mySyncedPids) { synchronized (mySyncedPids) {
ourLog.trace("Search status is {}", getSearch.getStatus()); ourLog.trace("Search status is {}", mySearch.getStatus());
keepWaiting = mySyncedPids.size() < theToIndex && getSearch.getStatus() == SearchStatusEnum.LOADING; keepWaiting = mySyncedPids.size() < theToIndex && mySearch.getStatus() == SearchStatusEnum.LOADING;
} }
if (keepWaiting) { if (keepWaiting) {
ourLog.info("Waiting, as we only have {} results", mySyncedPids.size()); ourLog.info("Waiting, as we only have {} results", mySyncedPids.size());
@ -492,7 +493,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
ArrayList<Long> retVal = new ArrayList<>(); ArrayList<Long> retVal = new ArrayList<>();
synchronized (mySyncedPids) { synchronized (mySyncedPids) {
verifySearchHasntFailedOrThrowInternalErrorException(getSearch); verifySearchHasntFailedOrThrowInternalErrorException(mySearch);
int toIndex = theToIndex; int toIndex = theToIndex;
if (mySyncedPids.size() < toIndex) { if (mySyncedPids.size() < toIndex) {
@ -526,13 +527,13 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
txTemplate.execute(new TransactionCallbackWithoutResult() { txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override @Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) { protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
if (getSearch.getId() == null) { if (mySearch.getId() == null) {
doSaveSearch(); doSaveSearch();
} }
List<SearchResult> resultsToSave = Lists.newArrayList(); List<SearchResult> resultsToSave = Lists.newArrayList();
for (Long nextPid : myUnsyncedPids) { for (Long nextPid : myUnsyncedPids) {
SearchResult nextResult = new SearchResult(getSearch); SearchResult nextResult = new SearchResult(mySearch);
nextResult.setResourcePid(nextPid); nextResult.setResourcePid(nextPid);
nextResult.setOrder(myCountSaved++); nextResult.setOrder(myCountSaved++);
resultsToSave.add(nextResult); resultsToSave.add(nextResult);
@ -547,22 +548,22 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
myUnsyncedPids.clear(); myUnsyncedPids.clear();
if (theResultIter.hasNext() == false) { if (theResultIter.hasNext() == false) {
getSearch.setNumFound(myCountSaved); mySearch.setNumFound(myCountSaved);
if (myMaxResultsToFetch != null && myCountSaved < myMaxResultsToFetch) { if (myMaxResultsToFetch != null && myCountSaved < myMaxResultsToFetch) {
getSearch.setStatus(SearchStatusEnum.FINISHED); mySearch.setStatus(SearchStatusEnum.FINISHED);
getSearch.setTotalCount(myCountSaved); mySearch.setTotalCount(myCountSaved);
} else if (myAdditionalPrefetchThresholdsRemaining) { } else if (myAdditionalPrefetchThresholdsRemaining) {
ourLog.trace("Setting search status to PASSCMPLET"); ourLog.trace("Setting search status to PASSCMPLET");
getSearch.setStatus(SearchStatusEnum.PASSCMPLET); mySearch.setStatus(SearchStatusEnum.PASSCMPLET);
getSearch.setSearchParameterMap(myParams); mySearch.setSearchParameterMap(myParams);
} else { } else {
getSearch.setStatus(SearchStatusEnum.FINISHED); mySearch.setStatus(SearchStatusEnum.FINISHED);
getSearch.setTotalCount(myCountSaved); mySearch.setTotalCount(myCountSaved);
} }
} }
} }
getSearch.setNumFound(myCountSaved); mySearch.setNumFound(myCountSaved);
int numSynced; int numSynced;
synchronized (mySyncedPids) { synchronized (mySyncedPids) {
@ -615,6 +616,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager); TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
txTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
txTemplate.execute(new TransactionCallbackWithoutResult() { txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override @Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) { protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
@ -622,7 +624,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
} }
}); });
ourLog.info("Completed search for [{}] and found {} resources in {}ms", getSearch.getSearchQueryString(), mySyncedPids.size(), sw.getMillis()); ourLog.info("Completed search for [{}{}] and found {} resources in {}ms", mySearch.getResourceType(), mySearch.getSearchQueryString(), mySyncedPids.size(), sw.getMillis());
} catch (Throwable t) { } catch (Throwable t) {
@ -655,15 +657,15 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
failureCode = ((BaseServerResponseException) t).getStatusCode(); failureCode = ((BaseServerResponseException) t).getStatusCode();
} }
getSearch.setFailureMessage(failureMessage); mySearch.setFailureMessage(failureMessage);
getSearch.setFailureCode(failureCode); mySearch.setFailureCode(failureCode);
getSearch.setStatus(SearchStatusEnum.FAILED); mySearch.setStatus(SearchStatusEnum.FAILED);
saveSearch(); saveSearch();
} finally { } finally {
myIdToSearchTask.remove(getSearch.getUuid()); myIdToSearchTask.remove(mySearch.getUuid());
myInitialCollectionLatch.countDown(); myInitialCollectionLatch.countDown();
markComplete(); markComplete();
@ -672,14 +674,26 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
} }
private void doSaveSearch() { private void doSaveSearch() {
if (getSearch.getId() == null) {
mySearchDao.save(getSearch); // FIXME: remove
for (SearchInclude next : getSearch.getIncludes()) { Integer totalCount = mySearch.getTotalCount();
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
ourLog.info("Have flushed save with total {}", totalCount);
}
});
ourLog.info("** Saving search version {} count {}", mySearch.getVersion(), totalCount);
if (mySearch.getId() == null) {
mySearch = mySearchDao.saveAndFlush(mySearch);
for (SearchInclude next : mySearch.getIncludes()) {
mySearchIncludeDao.save(next); mySearchIncludeDao.save(next);
} }
} else { } else {
mySearchDao.save(getSearch); mySearch = mySearchDao.saveAndFlush(mySearch);
} }
} }
/** /**
@ -698,18 +712,20 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
boolean wantCount = myParams.getSummaryMode().contains(SummaryEnum.COUNT); boolean wantCount = myParams.getSummaryMode().contains(SummaryEnum.COUNT);
boolean wantOnlyCount = wantCount && myParams.getSummaryMode().size() == 1; boolean wantOnlyCount = wantCount && myParams.getSummaryMode().size() == 1;
if (wantCount) { if (wantCount) {
ourLog.info("** performing count");
ISearchBuilder sb = newSearchBuilder(); ISearchBuilder sb = newSearchBuilder();
Iterator<Long> countIterator = sb.createCountQuery(myParams, getSearch.getUuid()); Iterator<Long> countIterator = sb.createCountQuery(myParams, mySearch.getUuid());
Long count = countIterator.next(); Long count = countIterator.next();
ourLog.info("** got count {}", count);
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager); TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); // txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
txTemplate.execute(new TransactionCallbackWithoutResult() { txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override @Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) { protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
getSearch.setTotalCount(count.intValue()); mySearch.setTotalCount(count.intValue());
if (wantOnlyCount) { if (wantOnlyCount) {
getSearch.setStatus(SearchStatusEnum.FINISHED); mySearch.setStatus(SearchStatusEnum.FINISHED);
} }
doSaveSearch(); doSaveSearch();
} }
@ -719,6 +735,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
} }
} }
ourLog.info("** done count");
ISearchBuilder sb = newSearchBuilder(); ISearchBuilder sb = newSearchBuilder();
/* /*
@ -727,7 +744,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
* "pre-fetch thresholds" specified in DaoConfig#getPreFetchThresholds() * "pre-fetch thresholds" specified in DaoConfig#getPreFetchThresholds()
* as well as the value of the _count parameter. * as well as the value of the _count parameter.
*/ */
int currentlyLoaded = defaultIfNull(getSearch.getNumFound(), 0); int currentlyLoaded = defaultIfNull(mySearch.getNumFound(), 0);
int minWanted = 0; int minWanted = 0;
if (myParams.getCount() != null) { if (myParams.getCount() != null) {
minWanted = myParams.getCount(); minWanted = myParams.getCount();
@ -774,7 +791,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
/* /*
* Construct the SQL query we'll be sending to the database * Construct the SQL query we'll be sending to the database
*/ */
Iterator<Long> theResultIterator = sb.createQuery(myParams, getSearch.getUuid()); Iterator<Long> theResultIterator = sb.createQuery(myParams, mySearch.getUuid());
/* /*
* The following loop actually loads the PIDs of the resources * The following loop actually loads the PIDs of the resources

View File

@ -4,10 +4,11 @@ import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.ResultSeverityEnum;
import net.ttddyy.dsproxy.listener.ThreadQueryCountHolder; import net.ttddyy.dsproxy.listener.ThreadQueryCountHolder;
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
import org.apache.commons.dbcp2.BasicDataSource; import org.apache.commons.dbcp2.BasicDataSource;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.query.criteria.LiteralHandlingMode; import org.hibernate.query.criteria.LiteralHandlingMode;
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@ -23,7 +24,7 @@ import java.sql.SQLException;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*; import static org.junit.Assert.fail;
@Configuration @Configuration
@EnableTransactionManagement() @EnableTransactionManagement()
@ -134,7 +135,6 @@ public class TestR4Config extends BaseJavaConfigR4 {
extraProperties.put("hibernate.search.default.directory_provider", "ram"); extraProperties.put("hibernate.search.default.directory_provider", "ram");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT"); extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
extraProperties.put("hibernate.search.autoregister_listeners", "true"); extraProperties.put("hibernate.search.autoregister_listeners", "true");
extraProperties.put("hibernate.criteria.literal_handling_mode", LiteralHandlingMode.BIND);
return extraProperties; return extraProperties;
} }

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.dao.SearchParameterMap; import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.entity.SearchStatusEnum; import ca.uhn.fhir.jpa.entity.SearchStatusEnum;
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
@ -15,6 +16,7 @@ import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean; import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean;
import java.util.ArrayList; import java.util.ArrayList;
@ -26,17 +28,23 @@ import java.util.concurrent.Future;
import static org.apache.commons.lang3.StringUtils.leftPad; import static org.apache.commons.lang3.StringUtils.leftPad;
import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
@SuppressWarnings({"unchecked", "deprecation", "Duplicates"}) @SuppressWarnings({"unchecked", "deprecation", "Duplicates"})
public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchOptimizedTest.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchOptimizedTest.class);
private SearchCoordinatorSvcImpl mySearchCoordinatorSvcImpl;
@Before
public void before() {
mySearchCoordinatorSvcImpl = (SearchCoordinatorSvcImpl) AopProxyUtils.getSingletonTarget(mySearchCoordinatorSvc);
}
@After @After
public final void after() { public final void after() {
mySearchCoordinatorSvcImpl.setLoadingThrottleForUnitTests(null);
mySearchCoordinatorSvcImpl.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE);
} }
@Before @Before
@ -119,6 +127,30 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
} }
@Test
public void testFetchCountAndDataForSlowLoading() {
mySearchCoordinatorSvcImpl.setLoadingThrottleForUnitTests(25);
mySearchCoordinatorSvcImpl.setSyncSizeForUnitTests(10);
myDaoConfig.setSearchPreFetchThresholds(Arrays.asList(1000, -1));
SearchParameterMap params = new SearchParameterMap();
params.setSort(new SortSpec(Patient.SP_NAME));
params.setCount(5);
params.setSummaryMode(Sets.newHashSet(SummaryEnum.COUNT, SummaryEnum.DATA));
IBundleProvider results = myPatientDao.search(params);
String uuid = results.getUuid();
ourLog.info("** Search returned UUID: {}", uuid);
// assertEquals(200, myDatabaseBackedPagingProvider.retrieveResultList(uuid).size().intValue());
assertEquals(200, results.size().intValue());
List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 5, true);
assertEquals("Patient/PT00000", ids.get(0));
assertEquals("Patient/PT00004", ids.get(4));
ourLog.info("** About to make new query for search with UUID: {}", uuid);
assertEquals(200, myDatabaseBackedPagingProvider.retrieveResultList(uuid).size().intValue());
}
@Test @Test
public void testFetchCountAndData() { public void testFetchCountAndData() {
@ -135,6 +167,21 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
assertEquals("Patient/PT00000", ids.get(0)); assertEquals("Patient/PT00000", ids.get(0));
assertEquals("Patient/PT00009", ids.get(9)); assertEquals("Patient/PT00009", ids.get(9));
assertEquals(200, myDatabaseBackedPagingProvider.retrieveResultList(uuid).size().intValue()); assertEquals(200, myDatabaseBackedPagingProvider.retrieveResultList(uuid).size().intValue());
// Try the same query again. This time the same thing should come back, but
// from the cache...
params = new SearchParameterMap();
params.setSort(new SortSpec(Patient.SP_NAME));
params.setSummaryMode(Sets.newHashSet(SummaryEnum.COUNT, SummaryEnum.DATA));
results = myPatientDao.search(params);
uuid = results.getUuid();
assertEquals(200, results.size().intValue());
ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true);
assertEquals("Patient/PT00000", ids.get(0));
assertEquals("Patient/PT00009", ids.get(9));
assertEquals(200, myDatabaseBackedPagingProvider.retrieveResultList(uuid).size().intValue());
} }
@Test @Test

View File

@ -731,7 +731,15 @@ public class RestfulServerUtils {
if (theResource == null) { if (theResource == null) {
// No response is being returned // No response is being returned
} else if (encodingDomainResourceAsText && theResource instanceof IResource) { } else if (encodingDomainResourceAsText && theResource instanceof IResource) {
// DSTU2
writer.append(((IResource) theResource).getText().getDiv().getValueAsString()); writer.append(((IResource) theResource).getText().getDiv().getValueAsString());
} else if (encodingDomainResourceAsText && theResource instanceof IDomainResource) {
// DSTU3+
try {
writer.append(((IDomainResource) theResource).getText().getDivAsString());
} catch (Exception e) {
throw new InternalErrorException(e);
}
} else { } else {
FhirVersionEnum forVersion = theResource.getStructureFhirVersionEnum(); FhirVersionEnum forVersion = theResource.getStructureFhirVersionEnum();
IParser parser = getNewParser(theServer.getFhirContext(), forVersion, theRequestDetails); IParser parser = getNewParser(theServer.getFhirContext(), forVersion, theRequestDetails);

View File

@ -69,23 +69,25 @@ public class SummaryEnumParameter implements IParameter {
retVal = null; retVal = null;
} else if (isBlank(summary[0])) { } else if (isBlank(summary[0])) {
retVal = null; retVal = null;
} else if (summary.length == 1) { } else if (summary.length == 1 && summary[0].indexOf(',') == -1) {
retVal = toCollectionOrNull(SummaryEnum.fromCode(summary[0])); retVal = toCollectionOrNull(SummaryEnum.fromCode(summary[0]));
if (retVal == null) { if (retVal == null) {
retVal = toCollectionOrNull(SummaryEnum.fromCode(summary[0].toLowerCase())); retVal = toCollectionOrNull(SummaryEnum.fromCode(summary[0].toLowerCase()));
} }
} else { } else {
retVal = new HashSet<>(); retVal = new HashSet<>();
for (String next : summary) { for (String nextParamValue : summary) {
SummaryEnum value = SummaryEnum.fromCode(next); for (String nextParamValueTok : nextParamValue.split(",")) {
SummaryEnum value = SummaryEnum.fromCode(nextParamValueTok);
if (value == null) { if (value == null) {
value = SummaryEnum.fromCode(next.toLowerCase()); value = SummaryEnum.fromCode(nextParamValueTok.toLowerCase());
} }
if (value != null) { if (value != null) {
retVal.add(value); retVal.add(value);
} }
} }
} }
}
if (retVal != null) { if (retVal != null) {
if (retVal.contains(SummaryEnum.TEXT)) { if (retVal.contains(SummaryEnum.TEXT)) {

View File

@ -36,13 +36,13 @@ import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
public class SummaryParamTest { public class SummaryParamDstu2Test {
private static CloseableHttpClient ourClient; private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2(); private static FhirContext ourCtx = FhirContext.forDstu2();
private static SummaryEnum ourLastSummary; private static SummaryEnum ourLastSummary;
private static List<SummaryEnum> ourLastSummaryList; private static List<SummaryEnum> ourLastSummaryList;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SummaryParamTest.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SummaryParamDstu2Test.class);
private static int ourPort; private static int ourPort;
private static Server ourServer; private static Server ourServer;

View File

@ -0,0 +1,362 @@
package ca.uhn.fhir.rest.server;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.MedicationRequest;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Reference;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class SummaryParamR4Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forR4();
private static SummaryEnum ourLastSummary;
private static List<SummaryEnum> ourLastSummaryList;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SummaryParamR4Test.class);
private static int ourPort;
private static Server ourServer;
@Before
public void before() {
ourLastSummary = null;
ourLastSummaryList = null;
}
@Test
public void testReadSummaryData() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.DATA.getCode());
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals(Constants.CT_FHIR_XML_NEW + Constants.CHARSET_UTF8_CTSUFFIX.replace(" ", "").toLowerCase(), status.getEntity().getContentType().getValue().replace(" ", "").replace("UTF", "utf"));
assertThat(responseContent, not(containsString("<Bundle")));
assertThat(responseContent, (containsString("<Patien")));
assertThat(responseContent, not(containsString("<div>THE DIV</div>")));
assertThat(responseContent, (containsString("family")));
assertThat(responseContent, (containsString("maritalStatus")));
assertEquals(SummaryEnum.DATA, ourLastSummary);
}
@Test
public void testReadSummaryText() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.TEXT.getCode());
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals(Constants.CT_HTML_WITH_UTF8.replace(" ", "").toLowerCase(), status.getEntity().getContentType().getValue().replace(" ", "").replace("UTF", "utf"));
assertThat(responseContent, not(containsString("<Bundle")));
assertThat(responseContent, not(containsString("<Medic")));
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">THE DIV</div>", responseContent);
assertThat(responseContent, not(containsString("efer")));
assertEquals(SummaryEnum.TEXT, ourLastSummary);
}
@Test
public void testReadSummaryTextWithMandatory() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationRequest/1?_summary=" + SummaryEnum.TEXT.getCode());
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals(Constants.CT_HTML_WITH_UTF8.replace(" ", "").toLowerCase(), status.getEntity().getContentType().getValue().replace(" ", "").replace("UTF", "utf"));
assertThat(responseContent, not(containsString("<Bundle")));
assertThat(responseContent, not(containsString("<Patien")));
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">TEXT</div>", responseContent);
assertThat(responseContent, not(containsString("family")));
assertThat(responseContent, not(containsString("maritalStatus")));
}
@Test
public void testReadSummaryTrue() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.TRUE.getCode());
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals(Constants.CT_FHIR_XML_NEW + Constants.CHARSET_UTF8_CTSUFFIX.replace(" ", "").toLowerCase(), status.getEntity().getContentType().getValue().replace(" ", "").replace("UTF", "utf"));
assertThat(responseContent, not(containsString("<Bundle")));
assertThat(responseContent, (containsString("<Patien")));
assertThat(responseContent, not(containsString("<div>THE DIV</div>")));
assertThat(responseContent, (containsString("family")));
assertThat(responseContent, not(containsString("maritalStatus")));
assertEquals(SummaryEnum.TRUE, ourLastSummary);
}
@Test
public void testSearchSummaryCount() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true&_summary=" + SummaryEnum.COUNT.getCode());
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, (containsString("<total value=\"1\"/>")));
assertThat(responseContent, not(containsString("entry")));
assertThat(responseContent, not(containsString("THE DIV")));
assertThat(responseContent, not(containsString("family")));
assertThat(responseContent, not(containsString("maritalStatus")));
assertEquals(SummaryEnum.COUNT, ourLastSummary);
}
@Test
public void testSearchSummaryCountAndData() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true&_summary=" + SummaryEnum.COUNT.getCode() + "," + SummaryEnum.DATA.getCode());
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, (containsString("<total value=\"1\"/>")));
assertThat(responseContent, (containsString("entry")));
assertThat(responseContent, not(containsString("THE DIV")));
assertThat(responseContent, (containsString("family")));
assertThat(responseContent, (containsString("maritalStatus")));
}
@Test
public void testSearchSummaryData() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.DATA.getCode());
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("<Patient"));
assertThat(responseContent, not(containsString("THE DIV")));
assertThat(responseContent, containsString("family"));
assertThat(responseContent, containsString("maritalStatus"));
assertEquals(SummaryEnum.DATA, ourLastSummary);
}
@Test
public void testSearchSummaryFalse() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=false");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("<Patient"));
assertThat(responseContent, containsString("THE DIV"));
assertThat(responseContent, containsString("family"));
assertThat(responseContent, containsString("maritalStatus"));
assertEquals(SummaryEnum.FALSE, ourLastSummary);
}
@Test
public void testSearchSummaryText() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.TEXT.getCode());
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, (containsString("<total value=\"1\"/>")));
assertThat(responseContent, (containsString("entry")));
assertThat(responseContent, (containsString("THE DIV")));
assertThat(responseContent, not(containsString("family")));
assertThat(responseContent, not(containsString("maritalStatus")));
assertEquals(SummaryEnum.TEXT, ourLastSummary);
}
@Test
public void testSearchSummaryTextWithMandatory() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationRequest?_summary=" + SummaryEnum.TEXT.getCode() + "&_pretty=true");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, (containsString("<total value=\"1\"/>")));
assertThat(responseContent, (containsString("entry")));
assertThat(responseContent, (containsString(">TEXT<")));
assertThat(responseContent, (containsString("Medication/123")));
assertThat(responseContent, not(containsStringIgnoringCase("note")));
}
@Test
public void testSearchSummaryTextMulti() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=multi&_summary=" + SummaryEnum.TEXT.getCode());
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, (containsString("<total value=\"1\"/>")));
assertThat(responseContent, (containsString("entry")));
assertThat(responseContent, (containsString("THE DIV")));
assertThat(responseContent, not(containsString("family")));
assertThat(responseContent, not(containsString("maritalStatus")));
assertThat(ourLastSummaryList, contains(SummaryEnum.TEXT));
}
@Test
public void testSearchSummaryTrue() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.TRUE.getCode());
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("<Patient"));
assertThat(responseContent, not(containsString("THE DIV")));
assertThat(responseContent, containsString("family"));
assertThat(responseContent, not(containsString("maritalStatus")));
assertEquals(SummaryEnum.TRUE, ourLastSummary);
}
@Test
public void testSearchSummaryWithTextAndOthers() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=text&_summary=data");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("Can not combine _summary=text with other values for _summary"));
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setResourceProviders(new DummyPatientResourceProvider(), new DummyMedicationRequestProvider());
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class DummyMedicationRequestProvider implements IResourceProvider{
@Override
public Class<? extends IBaseResource> getResourceType() {
return MedicationRequest.class;
}
@Read
public MedicationRequest read(@IdParam IdType theId) {
MedicationRequest retVal = new MedicationRequest();
retVal.getText().setDivAsString("<div>TEXT</div>");
retVal.addNote().setText("NOTE");
retVal.setMedication(new Reference("Medication/123"));
retVal.setId(theId);
return retVal;
}
@Search
public List<MedicationRequest> read() {
return Arrays.asList(read(new IdType("999")));
}
}
public static class DummyPatientResourceProvider implements IResourceProvider {
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Read
public Patient read(@IdParam IdType theId, SummaryEnum theSummary) {
ourLastSummary = theSummary;
Patient patient = new Patient();
patient.setId("Patient/1/_history/1");
patient.getText().setDivAsString("<div>THE DIV</div>");
patient.addName().setFamily("FAMILY");
patient.getMaritalStatus().addCoding().setCode("D");
return patient;
}
@Search(queryName = "multi")
public Patient search(List<SummaryEnum> theSummary) {
ourLastSummaryList = theSummary;
Patient patient = new Patient();
patient.setId("Patient/1/_history/1");
patient.getText().setDivAsString("<div>THE DIV</div>");
patient.addName().setFamily("FAMILY");
patient.getMaritalStatus().addCoding().setCode("D");
return patient;
}
@Search()
public Patient search(SummaryEnum theSummary) {
ourLastSummary = theSummary;
Patient patient = new Patient();
patient.setId("Patient/1/_history/1");
patient.getText().setDivAsString("<div>THE DIV</div>");
patient.addName().setFamily("FAMILY");
patient.getMaritalStatus().addCoding().setCode("D");
return patient;
}
}
}