Merge branch 'philips-3.6.0'

This commit is contained in:
James Agnew 2018-10-15 08:37:00 -04:00
commit b44e96a5cd
31 changed files with 1988 additions and 1098 deletions

View File

@ -25,6 +25,8 @@ import ca.uhn.fhir.i18n.HapiLocalizer;
import ca.uhn.fhir.jpa.dao.DaoRegistry; import ca.uhn.fhir.jpa.dao.DaoRegistry;
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider; import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
import ca.uhn.fhir.jpa.search.*; import ca.uhn.fhir.jpa.search.*;
import ca.uhn.fhir.jpa.search.warm.CacheWarmingSvcImpl;
import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc; import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl; import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl;
import ca.uhn.fhir.jpa.subscription.email.SubscriptionEmailInterceptor; import ca.uhn.fhir.jpa.subscription.email.SubscriptionEmailInterceptor;
@ -32,7 +34,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 +57,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,13 +91,33 @@ 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;
} }
public abstract FhirContext fhirContext(); public abstract FhirContext fhirContext();
@Bean
public ICacheWarmingSvc cacheWarmingSvc() {
return new CacheWarmingSvcImpl();
}
@Bean @Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() { public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator(); return new HibernateExceptionTranslator();

View File

@ -511,9 +511,13 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override @Override
@Transactional(propagation = Propagation.NEVER) @Transactional(propagation = Propagation.NEVER)
public ExpungeOutcome expunge(IIdType theId, ExpungeOptions theExpungeOptions) { public ExpungeOutcome expunge(IIdType theId, ExpungeOptions theExpungeOptions) {
BaseHasResource entity = readEntity(theId);
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
BaseHasResource entity = txTemplate.execute(t->readEntity(theId));
if (theId.hasVersionIdPart()) { if (theId.hasVersionIdPart()) {
BaseHasResource currentVersion = readEntity(theId.toVersionless()); BaseHasResource currentVersion;
currentVersion = txTemplate.execute(t->readEntity(theId.toVersionless()));
if (entity.getVersion() == currentVersion.getVersion()) { if (entity.getVersion() == currentVersion.getVersion()) {
throw new PreconditionFailedException("Can not perform version-specific expunge of resource " + theId.toUnqualified().getValue() + " as this is the current version"); throw new PreconditionFailedException("Can not perform version-specific expunge of resource " + theId.toUnqualified().getValue() + " as this is the current version");
} }

View File

@ -24,9 +24,9 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.util.SearchParameterUtil; import ca.uhn.fhir.util.SearchParameterUtil;
import ca.uhn.fhir.util.StopWatch;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
@ -37,6 +37,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.*; import java.util.*;
@ -58,7 +60,8 @@ public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implemen
private DaoConfig myDaoConfig; private DaoConfig myDaoConfig;
private volatile long myLastRefresh; private volatile long myLastRefresh;
private ApplicationContext myApplicationContext; private ApplicationContext myApplicationContext;
@Autowired
private PlatformTransactionManager myTxManager;
public BaseSearchParamRegistry() { public BaseSearchParamRegistry() {
super(); super();
} }
@ -197,7 +200,7 @@ public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implemen
}); });
for (String nextBase : next.getBase()) { for (String nextBase : next.getBase()) {
if (!activeParamNamesToUniqueSearchParams.containsKey(nextBase)) { if (!activeParamNamesToUniqueSearchParams.containsKey(nextBase)) {
activeParamNamesToUniqueSearchParams.put(nextBase, new HashMap<Set<String>, List<JpaRuntimeSearchParam>>()); activeParamNamesToUniqueSearchParams.put(nextBase, new HashMap<>());
} }
if (!activeParamNamesToUniqueSearchParams.get(nextBase).containsKey(paramNames)) { if (!activeParamNamesToUniqueSearchParams.get(nextBase).containsKey(paramNames)) {
activeParamNamesToUniqueSearchParams.get(nextBase).put(paramNames, new ArrayList<JpaRuntimeSearchParam>()); activeParamNamesToUniqueSearchParams.get(nextBase).put(paramNames, new ArrayList<JpaRuntimeSearchParam>());
@ -242,7 +245,17 @@ public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implemen
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE; long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) { if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
synchronized (this) { synchronized (this) {
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) { TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
txTemplate.execute(t->{
doRefresh(refreshInterval);
return null;
});
}
}
}
private void doRefresh(long theRefreshInterval) {
if (System.currentTimeMillis() - theRefreshInterval > myLastRefresh) {
StopWatch sw = new StopWatch(); StopWatch sw = new StopWatch();
Map<String, Map<String, RuntimeSearchParam>> searchParams = new HashMap<>(); Map<String, Map<String, RuntimeSearchParam>> searchParams = new HashMap<>();
@ -322,8 +335,6 @@ public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implemen
ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis()); ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis());
} }
} }
}
}
@Scheduled(fixedDelay = 10 * DateUtils.MILLIS_PER_SECOND) @Scheduled(fixedDelay = 10 * DateUtils.MILLIS_PER_SECOND)
public void refreshCacheOnSchedule() { public void refreshCacheOnSchedule() {

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.dao; package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.jpa.entity.ResourceEncodingEnum; import ca.uhn.fhir.jpa.entity.ResourceEncodingEnum;
import ca.uhn.fhir.jpa.search.warm.WarmCacheEntry;
import ca.uhn.fhir.jpa.util.JpaConstants; import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@ -152,7 +153,8 @@ public class DaoConfig {
private int myReindexThreadCount; private int myReindexThreadCount;
private Set<String> myBundleTypesAllowedForStorage; private Set<String> myBundleTypesAllowedForStorage;
private boolean myValidateSearchParameterExpressionsOnSave = true; private boolean myValidateSearchParameterExpressionsOnSave = true;
private List<Integer> myPreFetchThresholds = Arrays.asList(500, 2000, -1); private List<Integer> mySearchPreFetchThresholds = Arrays.asList(500, 2000, -1);
private List<WarmCacheEntry> myWarmCacheEntries = new ArrayList<>();
/** /**
* Constructor * Constructor
@ -171,6 +173,22 @@ public class DaoConfig {
} }
} }
/**
* Returns a set of searches that should be kept "warm", meaning that
* searches will periodically be performed in the background to
* keep results ready for this search
*/
public List<WarmCacheEntry> getWarmCacheEntries() {
if (myWarmCacheEntries == null) {
myWarmCacheEntries = new ArrayList<>();
}
return myWarmCacheEntries;
}
public void setWarmCacheEntries(List<WarmCacheEntry> theWarmCacheEntries) {
myWarmCacheEntries = theWarmCacheEntries;
}
/** /**
* If set to <code>true</code> (default is false), the reindexing of search parameters * If set to <code>true</code> (default is false), the reindexing of search parameters
* using a query on the HFJ_RESOURCE.SP_INDEX_STATUS column will be disabled completely. * using a query on the HFJ_RESOURCE.SP_INDEX_STATUS column will be disabled completely.
@ -496,18 +514,18 @@ public class DaoConfig {
/** /**
* This may be used to optionally register server interceptors directly against the DAOs. * This may be used to optionally register server interceptors directly against the DAOs.
*/ */
public void setInterceptors(IServerInterceptor... theInterceptor) { public void setInterceptors(List<IServerInterceptor> theInterceptors) {
setInterceptors(new ArrayList<IServerInterceptor>()); myInterceptors = theInterceptors;
if (theInterceptor != null && theInterceptor.length != 0) {
getInterceptors().addAll(Arrays.asList(theInterceptor));
}
} }
/** /**
* This may be used to optionally register server interceptors directly against the DAOs. * This may be used to optionally register server interceptors directly against the DAOs.
*/ */
public void setInterceptors(List<IServerInterceptor> theInterceptors) { public void setInterceptors(IServerInterceptor... theInterceptor) {
myInterceptors = theInterceptors; setInterceptors(new ArrayList<IServerInterceptor>());
if (theInterceptor != null && theInterceptor.length != 0) {
getInterceptors().addAll(Arrays.asList(theInterceptor));
}
} }
/** /**
@ -1345,7 +1363,7 @@ public class DaoConfig {
Validate.isTrue(last != -1, "Prefetch thresholds must be sequential"); Validate.isTrue(last != -1, "Prefetch thresholds must be sequential");
last = nextInt; last = nextInt;
} }
myPreFetchThresholds = thePreFetchThresholds; mySearchPreFetchThresholds = thePreFetchThresholds;
} }
/** /**
@ -1361,8 +1379,8 @@ public class DaoConfig {
* given number. * given number.
* </p> * </p>
*/ */
public List<Integer> getPreFetchThresholds() { public List<Integer> getSearchPreFetchThresholds() {
return myPreFetchThresholds; return mySearchPreFetchThresholds;
} }
public enum IndexEnabledEnum { public enum IndexEnabledEnum {

View File

@ -23,4 +23,7 @@ package ca.uhn.fhir.jpa.dao;
import java.util.Iterator; import java.util.Iterator;
public interface IResultIterator extends Iterator<Long> { public interface IResultIterator extends Iterator<Long> {
int getSkippedCount();
} }

View File

@ -1359,6 +1359,8 @@ public class SearchBuilder implements ISearchBuilder {
} }
private TypedQuery<Long> createQuery(SortSpec sort, Integer theMaximumResults, boolean theCount) { private TypedQuery<Long> createQuery(SortSpec sort, Integer theMaximumResults, boolean theCount) {
myPredicates = new ArrayList<>();
CriteriaQuery<Long> outerQuery; CriteriaQuery<Long> outerQuery;
/* /*
* Sort * Sort
@ -1369,30 +1371,48 @@ public class SearchBuilder implements ISearchBuilder {
if (sort != null) { if (sort != null) {
assert !theCount; assert !theCount;
// outerQuery = myBuilder.createQuery(Long.class);
// Root<ResourceTable> outerQueryFrom = outerQuery.from(ResourceTable.class);
//
// List<Order> orders = Lists.newArrayList();
// List<Predicate> predicates = Lists.newArrayList();
//
// createSort(myBuilder, outerQueryFrom, sort, orders, predicates);
// if (orders.size() > 0) {
// outerQuery.orderBy(orders);
// }
//
// Subquery<Long> subQ = outerQuery.subquery(Long.class);
// Root<ResourceTable> subQfrom = subQ.from(ResourceTable.class);
//
// myResourceTableQuery = subQ;
// myResourceTableRoot = subQfrom;
//
// Expression<Long> selectExpr = subQfrom.get("myId").as(Long.class);
// subQ.select(selectExpr);
//
// predicates.add(0, myBuilder.in(outerQueryFrom.get("myId").as(Long.class)).value(subQ));
//
// outerQuery.multiselect(outerQueryFrom.get("myId").as(Long.class));
// outerQuery.where(predicates.toArray(new Predicate[0]));
outerQuery = myBuilder.createQuery(Long.class); outerQuery = myBuilder.createQuery(Long.class);
Root<ResourceTable> outerQueryFrom = outerQuery.from(ResourceTable.class); myResourceTableQuery = outerQuery;
myResourceTableRoot = myResourceTableQuery.from(ResourceTable.class);
if (theCount) {
outerQuery.multiselect(myBuilder.countDistinct(myResourceTableRoot));
} else {
outerQuery.multiselect(myResourceTableRoot.get("myId").as(Long.class));
}
List<Order> orders = Lists.newArrayList(); List<Order> orders = Lists.newArrayList();
List<Predicate> predicates = Lists.newArrayList(); List<Predicate> predicates = myPredicates; // Lists.newArrayList();
createSort(myBuilder, outerQueryFrom, sort, orders, predicates); createSort(myBuilder, myResourceTableRoot, sort, orders, predicates);
if (orders.size() > 0) { if (orders.size() > 0) {
outerQuery.orderBy(orders); outerQuery.orderBy(orders);
} }
Subquery<Long> subQ = outerQuery.subquery(Long.class);
Root<ResourceTable> subQfrom = subQ.from(ResourceTable.class);
myResourceTableQuery = subQ;
myResourceTableRoot = subQfrom;
Expression<Long> selectExpr = subQfrom.get("myId").as(Long.class);
subQ.select(selectExpr);
predicates.add(0, myBuilder.in(outerQueryFrom.get("myId").as(Long.class)).value(subQ));
outerQuery.multiselect(outerQueryFrom.get("myId").as(Long.class));
outerQuery.where(predicates.toArray(new Predicate[0]));
} else { } else {
@ -1407,8 +1427,6 @@ public class SearchBuilder implements ISearchBuilder {
} }
myPredicates = new ArrayList<>();
if (myParams.getEverythingMode() != null) { if (myParams.getEverythingMode() != null) {
Join<ResourceTable, ResourceLink> join = myResourceTableRoot.join("myResourceLinks", JoinType.LEFT); Join<ResourceTable, ResourceLink> join = myResourceTableRoot.join("myResourceLinks", JoinType.LEFT);
@ -1590,7 +1608,8 @@ public class SearchBuilder implements ISearchBuilder {
if (param.getParamType() == RestSearchParameterTypeEnum.REFERENCE) { if (param.getParamType() == RestSearchParameterTypeEnum.REFERENCE) {
thePredicates.add(join.get("mySourcePath").as(String.class).in(param.getPathsSplit())); thePredicates.add(join.get("mySourcePath").as(String.class).in(param.getPathsSplit()));
} else { } else {
Predicate joinParam1 = theBuilder.equal(join.get("myParamName"), theSort.getParamName()); Long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(myResourceName, theSort.getParamName());
Predicate joinParam1 = theBuilder.equal(join.get("myHashIdentity"), hashIdentity);
thePredicates.add(joinParam1); thePredicates.add(joinParam1);
} }
} else { } else {
@ -1760,7 +1779,8 @@ public class SearchBuilder implements ISearchBuilder {
} }
/** /**
* THIS SHOULD RETURN HASHSET and not just Set because we add to it later (so it can't be Collections.emptySet()) * THIS SHOULD RETURN HASHSET and not just Set because we add to it later
* so it can't be Collections.emptySet() or some such thing
*/ */
@Override @Override
public HashSet<Long> loadIncludes(IDao theCallingDao, FhirContext theContext, EntityManager theEntityManager, Collection<Long> theMatches, Set<Include> theRevIncludes, public HashSet<Long> loadIncludes(IDao theCallingDao, FhirContext theContext, EntityManager theEntityManager, Collection<Long> theMatches, Set<Include> theRevIncludes,
@ -1940,11 +1960,11 @@ public class SearchBuilder implements ISearchBuilder {
return; return;
} }
if (theParamName.equals(BaseResource.SP_RES_ID)) { if (theParamName.equals(IAnyResource.SP_RES_ID)) {
addPredicateResourceId(theAndOrParams); addPredicateResourceId(theAndOrParams);
} else if (theParamName.equals(BaseResource.SP_RES_LANGUAGE)) { } else if (theParamName.equals(IAnyResource.SP_RES_LANGUAGE)) {
addPredicateLanguage(theAndOrParams); addPredicateLanguage(theAndOrParams);
@ -2154,6 +2174,7 @@ public class SearchBuilder implements ISearchBuilder {
private SortSpec mySort; private SortSpec mySort;
private boolean myStillNeedToFetchIncludes; private boolean myStillNeedToFetchIncludes;
private StopWatch myStopwatch = null; private StopWatch myStopwatch = null;
private int mySkipCount = 0;
private QueryIterator() { private QueryIterator() {
mySort = myParams.getSort(); mySort = myParams.getSort();
@ -2193,9 +2214,12 @@ public class SearchBuilder implements ISearchBuilder {
if (myPreResultsIterator != null && myPreResultsIterator.hasNext()) { if (myPreResultsIterator != null && myPreResultsIterator.hasNext()) {
while (myPreResultsIterator.hasNext()) { while (myPreResultsIterator.hasNext()) {
Long next = myPreResultsIterator.next(); Long next = myPreResultsIterator.next();
if (next != null && myPidSet.add(next)) { if (next != null)
if (myPidSet.add(next)) {
myNext = next; myNext = next;
break; break;
} else {
mySkipCount++;
} }
} }
} }
@ -2203,9 +2227,12 @@ public class SearchBuilder implements ISearchBuilder {
if (myNext == null) { if (myNext == null) {
while (myResultsIterator.hasNext()) { while (myResultsIterator.hasNext()) {
Long next = myResultsIterator.next(); Long next = myResultsIterator.next();
if (next != null && myPidSet.add(next)) { if (next != null)
if (myPidSet.add(next)) {
myNext = next; myNext = next;
break; break;
} else {
mySkipCount++;
} }
} }
} }
@ -2218,9 +2245,12 @@ public class SearchBuilder implements ISearchBuilder {
if (myIncludesIterator != null) { if (myIncludesIterator != null) {
while (myIncludesIterator.hasNext()) { while (myIncludesIterator.hasNext()) {
Long next = myIncludesIterator.next(); Long next = myIncludesIterator.next();
if (next != null && myPidSet.add(next)) { if (next != null)
if (myPidSet.add(next)) {
myNext = next; myNext = next;
break; break;
} else {
mySkipCount++;
} }
} }
if (myNext == null) { if (myNext == null) {
@ -2260,6 +2290,11 @@ public class SearchBuilder implements ISearchBuilder {
Validate.isTrue(retVal != NO_MORE, "No more elements"); Validate.isTrue(retVal != NO_MORE, "No more elements");
return retVal; return retVal;
} }
@Override
public int getSkippedCount() {
return mySkipCount;
}
} }
private class UniqueIndexIterator implements IResultIterator { private class UniqueIndexIterator implements IResultIterator {
@ -2296,6 +2331,11 @@ public class SearchBuilder implements ISearchBuilder {
public void remove() { public void remove() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public int getSkippedCount() {
return 0;
}
} }
private static class CountQueryIterator implements Iterator<Long> { private static class CountQueryIterator implements Iterator<Long> {

View File

@ -44,6 +44,10 @@ import static org.apache.commons.lang3.StringUtils.left;
* do not reuse these names: * do not reuse these names:
* IDX_SP_STRING * IDX_SP_STRING
*/ */
// This one us used only for sorting
@Index(name = "IDX_SP_STRING_HASH_IDENT", columnList = "HASH_IDENTITY"),
@Index(name = "IDX_SP_STRING_HASH_NRM", columnList = "HASH_NORM_PREFIX,SP_VALUE_NORMALIZED"), @Index(name = "IDX_SP_STRING_HASH_NRM", columnList = "HASH_NORM_PREFIX,SP_VALUE_NORMALIZED"),
@Index(name = "IDX_SP_STRING_HASH_EXCT", columnList = "HASH_EXACT"), @Index(name = "IDX_SP_STRING_HASH_EXCT", columnList = "HASH_EXACT"),
@ -130,6 +134,11 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
*/ */
@Column(name = "HASH_NORM_PREFIX", nullable = true) @Column(name = "HASH_NORM_PREFIX", nullable = true)
private Long myHashNormalizedPrefix; private Long myHashNormalizedPrefix;
/**
* @since 3.6.0 - At some point this should be made not-null
*/
@Column(name = "HASH_IDENTITY", nullable = true)
private Long myHashIdentity;
/** /**
* @since 3.4.0 - At some point this should be made not-null * @since 3.4.0 - At some point this should be made not-null
*/ */
@ -137,12 +146,10 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
private Long myHashExact; private Long myHashExact;
@Transient @Transient
private transient DaoConfig myDaoConfig; private transient DaoConfig myDaoConfig;
public ResourceIndexedSearchParamString() { public ResourceIndexedSearchParamString() {
super(); super();
} }
public ResourceIndexedSearchParamString(DaoConfig theDaoConfig, String theName, String theValueNormalized, String theValueExact) { public ResourceIndexedSearchParamString(DaoConfig theDaoConfig, String theName, String theValueNormalized, String theValueExact) {
setDaoConfig(theDaoConfig); setDaoConfig(theDaoConfig);
setParamName(theName); setParamName(theName);
@ -150,6 +157,10 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
setValueExact(theValueExact); setValueExact(theValueExact);
} }
public void setHashIdentity(Long theHashIdentity) {
myHashIdentity = theHashIdentity;
}
@PrePersist @PrePersist
public void calculateHashes() { public void calculateHashes() {
if (myHashNormalizedPrefix == null && myDaoConfig != null) { if (myHashNormalizedPrefix == null && myDaoConfig != null) {
@ -159,6 +170,7 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
String valueExact = getValueExact(); String valueExact = getValueExact();
setHashNormalizedPrefix(calculateHashNormalized(myDaoConfig, resourceType, paramName, valueNormalized)); setHashNormalizedPrefix(calculateHashNormalized(myDaoConfig, resourceType, paramName, valueNormalized));
setHashExact(calculateHashExact(resourceType, paramName, valueExact)); setHashExact(calculateHashExact(resourceType, paramName, valueExact));
setHashIdentity(calculateHashIdentity(resourceType, paramName));
} }
} }

View File

@ -5,8 +5,6 @@ import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.DateRangeParam;
import org.apache.commons.lang3.SerializationUtils; import org.apache.commons.lang3.SerializationUtils;
import org.hibernate.annotations.OptimisticLock; import org.hibernate.annotations.OptimisticLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.*; import javax.persistence.*;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;

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,8 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
return false; return false;
} }
ourLog.trace("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 +175,6 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
} catch (NoResultException e) { } catch (NoResultException e) {
return false; return false;
} }
}
}); });
} }
return true; return true;

View File

@ -46,9 +46,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.*;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory; import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionDefinition;
@ -360,7 +358,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
PersistedJpaSearchFirstPageBundleProvider retVal = new PersistedJpaSearchFirstPageBundleProvider(search, theCallingDao, task, sb, myManagedTxManager); PersistedJpaSearchFirstPageBundleProvider retVal = new PersistedJpaSearchFirstPageBundleProvider(search, theCallingDao, task, sb, myManagedTxManager);
populateBundleProvider(retVal); populateBundleProvider(retVal);
ourLog.info("Search initial phase completed in {}ms", w.getMillis()); ourLog.debug("Search initial phase completed in {}ms", w.getMillis());
return retVal; return retVal;
} }
@ -421,41 +419,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 +471,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 +488,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) {
@ -520,19 +516,19 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
}); });
} }
private void saveUnsynced(final Iterator<Long> theResultIter) { private void saveUnsynced(final IResultIterator theResultIter) {
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager); TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
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 +543,23 @@ 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) { int loadedCountThisPass = theResultIter.getSkippedCount() + myCountSaved;
getSearch.setStatus(SearchStatusEnum.FINISHED); if (myMaxResultsToFetch != null && loadedCountThisPass < myMaxResultsToFetch) {
getSearch.setTotalCount(myCountSaved); mySearch.setStatus(SearchStatusEnum.FINISHED);
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 +612,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 +620,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 +653,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,13 +670,21 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
} }
private void doSaveSearch() { private void doSaveSearch() {
if (getSearch.getId() == null) {
mySearchDao.save(getSearch); Search newSearch;
for (SearchInclude next : getSearch.getIncludes()) { if (mySearch.getId() == null) {
newSearch = mySearchDao.save(mySearch);
for (SearchInclude next : mySearch.getIncludes()) {
mySearchIncludeDao.save(next); mySearchIncludeDao.save(next);
} }
} else { } else {
mySearchDao.save(getSearch); newSearch = mySearchDao.save(mySearch);
}
// mySearchDao.save is not supposed to return null, but in unit tests
// it can if the mock search dao isn't set up to handle that
if (newSearch != null) {
mySearch = newSearch;
} }
} }
@ -698,20 +704,22 @@ 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.trace("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.trace("Got count {}", count);
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager); TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
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();
mySearchDao.flush();
} }
}); });
if (wantOnlyCount) { if (wantOnlyCount) {
@ -719,15 +727,16 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
} }
} }
ourLog.trace("Done count");
ISearchBuilder sb = newSearchBuilder(); ISearchBuilder sb = newSearchBuilder();
/* /*
* Figure out how many results we're actually going to fetch from the * Figure out how many results we're actually going to fetch from the
* database in this pass. This calculation takes into consideration the * database in this pass. This calculation takes into consideration the
* "pre-fetch thresholds" specified in DaoConfig#getPreFetchThresholds() * "pre-fetch thresholds" specified in DaoConfig#getSearchPreFetchThresholds()
* 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();
@ -735,7 +744,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
minWanted += currentlyLoaded; minWanted += currentlyLoaded;
} }
for (Iterator<Integer> iter = myDaoConfig.getPreFetchThresholds().iterator(); iter.hasNext(); ) { for (Iterator<Integer> iter = myDaoConfig.getSearchPreFetchThresholds().iterator(); iter.hasNext(); ) {
int next = iter.next(); int next = iter.next();
if (next != -1 && next <= currentlyLoaded) { if (next != -1 && next <= currentlyLoaded) {
continue; continue;
@ -774,7 +783,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()); IResultIterator 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
@ -940,13 +949,33 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
int pageIndex = theFromIndex / pageSize; int pageIndex = theFromIndex / pageSize;
Pageable page = new PageRequest(pageIndex, pageSize) { Pageable page = new AbstractPageRequest(pageIndex, pageSize) {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public long getOffset() { public long getOffset() {
return theFromIndex; return theFromIndex;
} }
@Override
public Sort getSort() {
return Sort.unsorted();
}
@Override
public Pageable next() {
return null;
}
@Override
public Pageable previous() {
return null;
}
@Override
public Pageable first() {
return null;
}
}; };
return page; return page;

View File

@ -0,0 +1,96 @@
package ca.uhn.fhir.jpa.search.warm;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.jpa.dao.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class CacheWarmingSvcImpl implements ICacheWarmingSvc {
@Autowired
private DaoConfig myDaoConfig;
private Map<WarmCacheEntry, Long> myCacheEntryToNextRefresh = new LinkedHashMap<>();
@Autowired
private FhirContext myCtx;
@Autowired
private DaoRegistry myDaoRegistry;
@Override
@Scheduled(fixedDelay = 1000)
public synchronized void performWarmingPass() {
for (WarmCacheEntry nextCacheEntry : new ArrayList<>(myCacheEntryToNextRefresh.keySet())) {
long nextRefresh = myCacheEntryToNextRefresh.get(nextCacheEntry);
if (nextRefresh < System.currentTimeMillis()) {
// Perform the search
refreshNow(nextCacheEntry);
// Set the next time to warm this search
nextRefresh = nextCacheEntry.getPeriodMillis() + System.currentTimeMillis();
myCacheEntryToNextRefresh.put(nextCacheEntry, nextRefresh);
}
}
}
private void refreshNow(WarmCacheEntry theCacheEntry) {
String nextUrl = theCacheEntry.getUrl();
RuntimeResourceDefinition resourceDef = parseWarmUrlResourceType(nextUrl);
IFhirResourceDao<?> callingDao = myDaoRegistry.getResourceDao(resourceDef.getName());
String queryPart = parseWarmUrlParamPart(nextUrl);
SearchParameterMap responseCriteriaUrl = BaseHapiFhirDao.translateMatchUrl(callingDao, myCtx, queryPart, resourceDef);
callingDao.search(responseCriteriaUrl);
}
private String parseWarmUrlParamPart(String theNextUrl) {
int paramIndex = theNextUrl.indexOf('?');
if (paramIndex == -1) {
throw new ConfigurationException("Invalid warm cache URL (must have ? character)");
}
return theNextUrl.substring(paramIndex);
}
private RuntimeResourceDefinition parseWarmUrlResourceType(String theNextUrl) {
int paramIndex = theNextUrl.indexOf('?');
String resourceName = theNextUrl.substring(0, paramIndex);
if (resourceName.contains("/")) {
resourceName = resourceName.substring(resourceName.lastIndexOf('/') + 1);
}
RuntimeResourceDefinition resourceDef = myCtx.getResourceDefinition(resourceName);
return resourceDef;
}
@PostConstruct
public void start() {
initCacheMap();
}
public synchronized void initCacheMap() {
myCacheEntryToNextRefresh.clear();
List<WarmCacheEntry> warmCacheEntries = myDaoConfig.getWarmCacheEntries();
for (WarmCacheEntry next : warmCacheEntries) {
// Validate
parseWarmUrlParamPart(next.getUrl());
parseWarmUrlResourceType(next.getUrl());
myCacheEntryToNextRefresh.put(next, 0L);
}
}
}

View File

@ -0,0 +1,8 @@
package ca.uhn.fhir.jpa.search.warm;
import org.springframework.scheduling.annotation.Scheduled;
public interface ICacheWarmingSvc {
@Scheduled(fixedDelay = 1000)
void performWarmingPass();
}

View File

@ -0,0 +1,61 @@
package ca.uhn.fhir.jpa.search.warm;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
/**
* Denotes a search that should be performed in the background
* periodically in order to keep a fresh copy in the query cache.
* This improves performance for searches by keeping a copy
* loaded in the background.
*/
public class WarmCacheEntry {
private long myPeriodMillis;
private String myUrl;
@Override
public boolean equals(Object theO) {
if (this == theO) {
return true;
}
if (theO == null || getClass() != theO.getClass()) {
return false;
}
WarmCacheEntry that = (WarmCacheEntry) theO;
return new EqualsBuilder()
.append(myPeriodMillis, that.myPeriodMillis)
.append(myUrl, that.myUrl)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(myPeriodMillis)
.append(myUrl)
.toHashCode();
}
public long getPeriodMillis() {
return myPeriodMillis;
}
public WarmCacheEntry setPeriodMillis(long thePeriodMillis) {
myPeriodMillis = thePeriodMillis;
return this;
}
public String getUrl() {
return myUrl;
}
public WarmCacheEntry setUrl(String theUrl) {
myUrl = theUrl;
return this;
}
}

View File

@ -7,7 +7,6 @@ import net.ttddyy.dsproxy.listener.ThreadQueryCountHolder;
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel; 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.query.criteria.LiteralHandlingMode;
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 +22,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 +133,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

@ -11,6 +11,7 @@ import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc; import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc; import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl; import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
@ -51,7 +52,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Map; import java.util.Map;
import static org.junit.Assert.*; import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@ -260,6 +261,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
@Autowired @Autowired
protected ITermConceptMapGroupElementTargetDao myTermConceptMapGroupElementTargetDao; protected ITermConceptMapGroupElementTargetDao myTermConceptMapGroupElementTargetDao;
@Autowired @Autowired
protected ICacheWarmingSvc myCacheWarmingSvc;
@Autowired
private JpaValidationSupportChainR4 myJpaValidationSupportChainR4; private JpaValidationSupportChainR4 myJpaValidationSupportChainR4;
@After() @After()

View File

@ -0,0 +1,112 @@
package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
import ca.uhn.fhir.jpa.search.warm.CacheWarmingSvcImpl;
import ca.uhn.fhir.jpa.search.warm.WarmCacheEntry;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.r4.model.Patient;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class FhirResourceDaoR4CacheWarmingTest extends BaseJpaR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4CacheWarmingTest.class);
@After
public void afterResetDao() {
myDaoConfig.setResourceServerIdStrategy(new DaoConfig().getResourceServerIdStrategy());
myDaoConfig.setWarmCacheEntries(new ArrayList<>());
CacheWarmingSvcImpl cacheWarmingSvc = (CacheWarmingSvcImpl) myCacheWarmingSvc;
cacheWarmingSvc.initCacheMap();
}
@Test
public void testInvalidCacheEntries() {
CacheWarmingSvcImpl cacheWarmingSvc = (CacheWarmingSvcImpl) myCacheWarmingSvc;
myDaoConfig.setWarmCacheEntries(new ArrayList<>());
myDaoConfig.getWarmCacheEntries().add(
new WarmCacheEntry()
.setPeriodMillis(10)
.setUrl("BadResource?name=smith")
);
try {
cacheWarmingSvc.initCacheMap();
fail();
} catch (DataFormatException e) {
assertEquals("Unknown resource name \"BadResource\" (this name is not known in FHIR version \"R4\")", e.getMessage());
}
myDaoConfig.setWarmCacheEntries(new ArrayList<>());
myDaoConfig.getWarmCacheEntries().add(
new WarmCacheEntry()
.setPeriodMillis(10)
.setUrl("foo/Patient")
);
try {
cacheWarmingSvc.initCacheMap();
fail();
} catch (ConfigurationException e) {
assertEquals("Invalid warm cache URL (must have ? character)", e.getMessage());
}
}
@Test
public void testKeepCacheWarm() throws InterruptedException {
myDaoConfig.setWarmCacheEntries(new ArrayList<>());
myDaoConfig.getWarmCacheEntries().add(
new WarmCacheEntry()
.setPeriodMillis(10)
.setUrl("Patient?name=smith")
);
CacheWarmingSvcImpl cacheWarmingSvc = (CacheWarmingSvcImpl) myCacheWarmingSvc;
cacheWarmingSvc.initCacheMap();
Patient p1 = new Patient();
p1.setId("p1");
p1.setActive(true);
myPatientDao.update(p1);
Patient p2 = new Patient();
p2.setId("p2");
p2.setActive(true);
p2.addName().setFamily("Smith");
myPatientDao.update(p2);
Thread.sleep(2000);
SearchParameterMap params = new SearchParameterMap();
params.add("name", new StringParam("smith"));
IBundleProvider result = myPatientDao.search(params);
assertEquals(PersistedJpaBundleProvider.class, result.getClass());
PersistedJpaBundleProvider resultCasted = (PersistedJpaBundleProvider) result;
assertTrue(resultCasted.isCacheHit());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -1,8 +1,10 @@
package ca.uhn.fhir.jpa.dao.r4; package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.dao.DaoConfig;
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 +17,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 +29,24 @@ 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);
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
} }
@Before @Before
@ -62,6 +72,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
params.setSummaryMode(Sets.newHashSet(SummaryEnum.COUNT)); params.setSummaryMode(Sets.newHashSet(SummaryEnum.COUNT));
IBundleProvider results = myPatientDao.search(params); IBundleProvider results = myPatientDao.search(params);
String uuid = results.getUuid(); String uuid = results.getUuid();
ourLog.info("** Search returned UUID: {}", uuid);
assertEquals(200, results.size().intValue()); assertEquals(200, results.size().intValue());
List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true); List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true);
assertThat(ids, empty()); assertThat(ids, empty());
@ -90,6 +101,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
params.setSummaryMode(Sets.newHashSet(SummaryEnum.COUNT)); params.setSummaryMode(Sets.newHashSet(SummaryEnum.COUNT));
IBundleProvider results = myPatientDao.search(params); IBundleProvider results = myPatientDao.search(params);
String uuid = results.getUuid(); String uuid = results.getUuid();
ourLog.info("** Search returned UUID: {}", uuid);
assertEquals(201, results.size().intValue()); assertEquals(201, results.size().intValue());
List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true); List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true);
assertThat(ids, empty()); assertThat(ids, empty());
@ -101,6 +113,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
params.setSummaryMode(Sets.newHashSet(SummaryEnum.COUNT, SummaryEnum.DATA)); params.setSummaryMode(Sets.newHashSet(SummaryEnum.COUNT, SummaryEnum.DATA));
results = myPatientDao.search(params); results = myPatientDao.search(params);
uuid = results.getUuid(); uuid = results.getUuid();
ourLog.info("** Search returned UUID: {}", uuid);
assertEquals(201, results.size().intValue()); assertEquals(201, results.size().intValue());
ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true); ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true);
assertThat(ids, hasSize(10)); assertThat(ids, hasSize(10));
@ -112,6 +125,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
params.setSummaryMode(Sets.newHashSet(SummaryEnum.COUNT)); params.setSummaryMode(Sets.newHashSet(SummaryEnum.COUNT));
results = myPatientDao.search(params); results = myPatientDao.search(params);
uuid = results.getUuid(); uuid = results.getUuid();
ourLog.info("** Search returned UUID: {}", uuid);
assertEquals(201, results.size().intValue()); assertEquals(201, results.size().intValue());
ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true); ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true);
assertThat(ids, empty()); assertThat(ids, empty());
@ -119,6 +133,33 @@ 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());
ourLog.info("** Asking for results");
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);
IBundleProvider search2 = myDatabaseBackedPagingProvider.retrieveResultList(uuid);
Integer search2Size = search2.size();
assertEquals(200, search2Size.intValue());
}
@Test @Test
public void testFetchCountAndData() { public void testFetchCountAndData() {
@ -130,11 +171,28 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
params.setSummaryMode(Sets.newHashSet(SummaryEnum.COUNT, SummaryEnum.DATA)); params.setSummaryMode(Sets.newHashSet(SummaryEnum.COUNT, SummaryEnum.DATA));
IBundleProvider results = myPatientDao.search(params); IBundleProvider results = myPatientDao.search(params);
String uuid = results.getUuid(); String uuid = results.getUuid();
ourLog.info("** Search returned UUID: {}", uuid);
assertEquals(200, results.size().intValue()); assertEquals(200, results.size().intValue());
List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true); List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true);
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();
ourLog.info("** Search returned UUID: {}", uuid);
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
@ -150,6 +208,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
params.setSort(new SortSpec(Patient.SP_NAME)); params.setSort(new SortSpec(Patient.SP_NAME));
IBundleProvider results = myPatientDao.search(params); IBundleProvider results = myPatientDao.search(params);
String uuid = results.getUuid(); String uuid = results.getUuid();
ourLog.info("** Search returned UUID: {}", uuid);
List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 200, true); List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 200, true);
assertEquals("Patient/PT00000", ids.get(0)); assertEquals("Patient/PT00000", ids.get(0));
assertEquals("Patient/PT00199", ids.get(199)); assertEquals("Patient/PT00199", ids.get(199));
@ -203,6 +262,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
params.setSort(new SortSpec(Patient.SP_NAME)); params.setSort(new SortSpec(Patient.SP_NAME));
IBundleProvider results = myPatientDao.search(params); IBundleProvider results = myPatientDao.search(params);
String uuid = results.getUuid(); String uuid = results.getUuid();
ourLog.info("** Search returned UUID: {}", uuid);
List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true); List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true);
assertEquals("Patient/PT00000", ids.get(0)); assertEquals("Patient/PT00000", ids.get(0));
assertEquals("Patient/PT00009", ids.get(9)); assertEquals("Patient/PT00009", ids.get(9));
@ -328,6 +388,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
params.setCount(50); params.setCount(50);
IBundleProvider results = myPatientDao.search(params); IBundleProvider results = myPatientDao.search(params);
String uuid = results.getUuid(); String uuid = results.getUuid();
ourLog.info("** Search returned UUID: {}", uuid);
List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 50, true); List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 50, true);
assertEquals("Patient/PT00000", ids.get(0)); assertEquals("Patient/PT00000", ids.get(0));
assertEquals("Patient/PT00049", ids.get(49)); assertEquals("Patient/PT00049", ids.get(49));
@ -361,6 +422,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
params.setSort(new SortSpec(Patient.SP_NAME)); params.setSort(new SortSpec(Patient.SP_NAME));
IBundleProvider results = myPatientDao.search(params); IBundleProvider results = myPatientDao.search(params);
String uuid = results.getUuid(); String uuid = results.getUuid();
ourLog.info("** Search returned UUID: {}", uuid);
List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true); List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true);
assertEquals("Patient/PT00000", ids.get(0)); assertEquals("Patient/PT00000", ids.get(0));
assertEquals("Patient/PT00009", ids.get(9)); assertEquals("Patient/PT00009", ids.get(9));
@ -418,6 +480,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
params.setSort(new SortSpec(Patient.SP_NAME)); params.setSort(new SortSpec(Patient.SP_NAME));
final IBundleProvider results = myPatientDao.search(params); final IBundleProvider results = myPatientDao.search(params);
String uuid = results.getUuid(); String uuid = results.getUuid();
ourLog.info("** Search returned UUID: {}", uuid);
List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true); List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true);
assertEquals("Patient/PT00000", ids.get(0)); assertEquals("Patient/PT00000", ids.get(0));
assertEquals("Patient/PT00009", ids.get(9)); assertEquals("Patient/PT00009", ids.get(9));
@ -482,6 +545,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
params.add(Patient.SP_RES_ID, new TokenParam("PT00000")); params.add(Patient.SP_RES_ID, new TokenParam("PT00000"));
IBundleProvider results = myPatientDao.search(params); IBundleProvider results = myPatientDao.search(params);
String uuid = results.getUuid(); String uuid = results.getUuid();
ourLog.info("** Search returned UUID: {}", uuid);
List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true); List<String> ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true);
assertEquals("Patient/PT00000", ids.get(0)); assertEquals("Patient/PT00000", ids.get(0));
assertEquals(1, ids.size()); assertEquals(1, ids.size());

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.dao.r4; package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.SearchParameterMap; import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortOrderEnum;
@ -206,31 +207,38 @@ public class FhirResourceDaoR4SortTest extends BaseJpaR4Test {
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Test @Test
public void testSortOnSparselyPopulatedFields() { public void testSortOnSparselyPopulatedFields() {
// myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED);
IIdType pid1, pid2, pid3, pid4, pid5, pid6; IIdType pid1, pid2, pid3, pid4, pid5, pid6;
{ {
Patient p = new Patient(); Patient p = new Patient();
p.setId("pid1");
p.setActive(true); p.setActive(true);
pid1 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); pid1 = myPatientDao.update(p, mySrd).getId().toUnqualifiedVersionless();
} }
{ {
Patient p = new Patient(); Patient p = new Patient();
p.setId("pid2");
p.addName().setFamily("A"); p.addName().setFamily("A");
pid2 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); pid2 = myPatientDao.update(p, mySrd).getId().toUnqualifiedVersionless();
} }
{ {
Patient p = new Patient(); Patient p = new Patient();
p.setId("pid3");
p.addName().setFamily("B"); p.addName().setFamily("B");
pid3 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); pid3 = myPatientDao.update(p, mySrd).getId().toUnqualifiedVersionless();
} }
{ {
Patient p = new Patient(); Patient p = new Patient();
p.setId("pid4");
p.addName().setFamily("B").addGiven("A"); p.addName().setFamily("B").addGiven("A");
pid4 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); pid4 = myPatientDao.update(p, mySrd).getId().toUnqualifiedVersionless();
} }
{ {
Patient p = new Patient(); Patient p = new Patient();
p.setId("pid5");
p.addName().setFamily("B").addGiven("B"); p.addName().setFamily("B").addGiven("B");
pid5 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); pid5 = myPatientDao.update(p, mySrd).getId().toUnqualifiedVersionless();
} }
SearchParameterMap map; SearchParameterMap map;
@ -239,6 +247,7 @@ public class FhirResourceDaoR4SortTest extends BaseJpaR4Test {
map = new SearchParameterMap(); map = new SearchParameterMap();
map.setSort(new SortSpec(Patient.SP_FAMILY, SortOrderEnum.ASC).setChain(new SortSpec(Patient.SP_GIVEN, SortOrderEnum.ASC))); map.setSort(new SortSpec(Patient.SP_FAMILY, SortOrderEnum.ASC).setChain(new SortSpec(Patient.SP_GIVEN, SortOrderEnum.ASC)));
ids = toUnqualifiedVersionlessIds(myPatientDao.search(map)); ids = toUnqualifiedVersionlessIds(myPatientDao.search(map));
ourLog.info("** Got IDs: {}", ids);
assertThat(ids, contains(pid2, pid4, pid5, pid3, pid1)); assertThat(ids, contains(pid2, pid4, pid5, pid3, pid1));
assertEquals(5, ids.size()); assertEquals(5, ids.size());

View File

@ -47,6 +47,7 @@ public class PatientEverythingR4Test extends BaseResourceProviderR4Test {
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis()); myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
myDaoConfig.setEverythingIncludesFetchPageSize(new DaoConfig().getEverythingIncludesFetchPageSize()); myDaoConfig.setEverythingIncludesFetchPageSize(new DaoConfig().getEverythingIncludesFetchPageSize());
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
} }
@Override @Override

View File

@ -58,10 +58,7 @@ import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity; import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicNameValuePair;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator; import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
@ -142,6 +139,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences()); myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis()); myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
myDaoConfig.setCountSearchResultsUpTo(new DaoConfig().getCountSearchResultsUpTo()); myDaoConfig.setCountSearchResultsUpTo(new DaoConfig().getCountSearchResultsUpTo());
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
mySearchCoordinatorSvcRaw.setLoadingThrottleForUnitTests(null); mySearchCoordinatorSvcRaw.setLoadingThrottleForUnitTests(null);
mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE); mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE);
@ -158,6 +156,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myDaoConfig.setAllowMultipleDelete(true); myDaoConfig.setAllowMultipleDelete(true);
ourClient.registerInterceptor(myCapturingInterceptor); ourClient.registerInterceptor(myCapturingInterceptor);
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
} }
@Test @Test
@ -469,7 +468,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
theRequest.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); theRequest.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME);
} }
@Override @Override
public void interceptResponse(IHttpResponse theResponse) throws IOException { // TODO Auto-generated method stu public void interceptResponse(IHttpResponse theResponse) { // TODO Auto-generated method stu
} }
}); });
@ -968,9 +967,9 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
sock.setSoTimeout(3000); sock.setSoTimeout(3000);
try { try {
sock.connect(new InetSocketAddress("localhost", ourPort)); sock.connect(new InetSocketAddress("localhost", ourPort));
sock.getOutputStream().write(("DELETE /fhir/context/Patient?identifier=http://ghh.org/patient|" + methodName + " HTTP/1.1\n").getBytes("UTF-8")); sock.getOutputStream().write(("DELETE /fhir/context/Patient?identifier=http://ghh.org/patient|" + methodName + " HTTP/1.1\n").getBytes(StandardCharsets.UTF_8));
sock.getOutputStream().write("Host: localhost\n".getBytes("UTF-8")); sock.getOutputStream().write("Host: localhost\n".getBytes(StandardCharsets.UTF_8));
sock.getOutputStream().write("\n".getBytes("UTF-8")); sock.getOutputStream().write("\n".getBytes(StandardCharsets.UTF_8));
BufferedReader socketInput = new BufferedReader(new InputStreamReader(sock.getInputStream())); BufferedReader socketInput = new BufferedReader(new InputStreamReader(sock.getInputStream()));
@ -1637,6 +1636,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
*/ */
@Test @Test
public void testEverythingWithLargeSet2() { public void testEverythingWithLargeSet2() {
myDaoConfig.setSearchPreFetchThresholds(Arrays.asList(15, 30, -1));
Patient p = new Patient(); Patient p = new Patient();
p.setActive(true); p.setActive(true);
IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless(); IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
@ -1648,7 +1649,13 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
ourClient.update().resource(obs).execute(); ourClient.update().resource(obs).execute();
} }
Bundle responseBundle = ourClient.operation().onInstance(id).named("everything").withParameter(Parameters.class, "_count", new IntegerType(50)).useHttpGet().returnResourceType(Bundle.class) Bundle responseBundle = ourClient
.operation()
.onInstance(id)
.named("everything")
.withParameter(Parameters.class, "_count", new IntegerType(50))
.useHttpGet()
.returnResourceType(Bundle.class)
.execute(); .execute();
TreeSet<String> ids = new TreeSet<>(); TreeSet<String> ids = new TreeSet<>();
@ -1658,9 +1665,9 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
} }
} }
ourLog.info("Have {} IDs: {}", ids.size(), ids);
BundleLinkComponent nextLink = responseBundle.getLink("next"); BundleLinkComponent nextLink = responseBundle.getLink("next");
ourLog.info("Have {} IDs with next link: ", ids.size(), nextLink);
while (nextLink != null) { while (nextLink != null) {
String nextUrl = nextLink.getUrl(); String nextUrl = nextLink.getUrl();
responseBundle = ourClient.fetchResourceFromUrl(Bundle.class, nextUrl); responseBundle = ourClient.fetchResourceFromUrl(Bundle.class, nextUrl);
@ -1670,8 +1677,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
} }
} }
ourLog.info("Have {} IDs: {}", ids.size(), ids);
nextLink = responseBundle.getLink("next"); nextLink = responseBundle.getLink("next");
ourLog.info("Have {} IDs with next link: ", ids.size(), nextLink);
} }
assertThat(ids, hasItem(id.getIdPart())); assertThat(ids, hasItem(id.getIdPart()));
@ -2731,14 +2738,11 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test @Test
public void testSearchBundleDoesntIncludeTextElement() throws Exception { public void testSearchBundleDoesntIncludeTextElement() throws Exception {
HttpGet read = new HttpGet(ourServerBase + "/Patient?_format=json"); HttpGet read = new HttpGet(ourServerBase + "/Patient?_format=json");
CloseableHttpResponse response = ourHttpClient.execute(read); try (CloseableHttpResponse response = ourHttpClient.execute(read)) {
try {
String text = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String text = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(text); ourLog.info(text);
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatusLine().getStatusCode()); assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatusLine().getStatusCode());
assertThat(text, not(containsString("\"text\",\"type\""))); assertThat(text, not(containsString("\"text\",\"type\"")));
} finally {
response.close();
} }
} }
@ -2768,7 +2772,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
assertEquals("Jernelöv", p.getName().get(0).getFamily()); assertEquals("Jernelöv", p.getName().get(0).getFamily());
} finally { } finally {
IOUtils.closeQuietly(resp.getEntity().getContent()); resp.getEntity().getContent().close();
} }
} }
@ -2793,7 +2797,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
found = ourClient found = ourClient
.search() .search()
.forResource(Patient.class) .forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().systemAndValues(null, id1.getIdPart(), id2.getIdPart())) .where(IAnyResource.RES_ID.exactly().systemAndValues(null, id1.getIdPart(), id2.getIdPart()))
.returnBundle(Bundle.class) .returnBundle(Bundle.class)
.execute(); .execute();
@ -2802,7 +2806,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
found = ourClient found = ourClient
.search() .search()
.forResource(Patient.class) .forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().systemAndValues(null, Arrays.asList(id1.getIdPart(), id2.getIdPart(), "FOOOOO"))) .where(IAnyResource.RES_ID.exactly().systemAndValues(null, Arrays.asList(id1.getIdPart(), id2.getIdPart(), "FOOOOO")))
.returnBundle(Bundle.class) .returnBundle(Bundle.class)
.execute(); .execute();
@ -2811,7 +2815,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
found = ourClient found = ourClient
.search() .search()
.forResource(Patient.class) .forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().systemAndCode(null, id1.getIdPart())) .where(IAnyResource.RES_ID.exactly().systemAndCode(null, id1.getIdPart()))
.returnBundle(Bundle.class) .returnBundle(Bundle.class)
.execute(); .execute();
@ -2820,8 +2824,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
found = ourClient found = ourClient
.search() .search()
.forResource(Patient.class) .forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().codes(id1.getIdPart(), id2.getIdPart())) .where(IAnyResource.RES_ID.exactly().codes(id1.getIdPart(), id2.getIdPart()))
.and(BaseResource.RES_ID.exactly().code(id1.getIdPart())) .and(IAnyResource.RES_ID.exactly().code(id1.getIdPart()))
.returnBundle(Bundle.class) .returnBundle(Bundle.class)
.execute(); .execute();
@ -2830,8 +2834,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
found = ourClient found = ourClient
.search() .search()
.forResource(Patient.class) .forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().codes(Arrays.asList(id1.getIdPart(), id2.getIdPart(), "FOOOOO"))) .where(IAnyResource.RES_ID.exactly().codes(Arrays.asList(id1.getIdPart(), id2.getIdPart(), "FOOOOO")))
.and(BaseResource.RES_ID.exactly().code(id1.getIdPart())) .and(IAnyResource.RES_ID.exactly().code(id1.getIdPart()))
.returnBundle(Bundle.class) .returnBundle(Bundle.class)
.execute(); .execute();
@ -2840,7 +2844,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
found = ourClient found = ourClient
.search() .search()
.forResource(Patient.class) .forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().codes(id1.getIdPart(), id2.getIdPart(), "FOOO")) .where(IAnyResource.RES_ID.exactly().codes(id1.getIdPart(), id2.getIdPart(), "FOOO"))
.returnBundle(Bundle.class) .returnBundle(Bundle.class)
.execute(); .execute();
@ -2849,7 +2853,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
found = ourClient found = ourClient
.search() .search()
.forResource(Patient.class) .forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().codes("FOOO")) .where(IAnyResource.RES_ID.exactly().codes("FOOO"))
.returnBundle(Bundle.class) .returnBundle(Bundle.class)
.execute(); .execute();
@ -2867,7 +2871,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Patient p2 = new Patient(); Patient p2 = new Patient();
p2.addIdentifier().setSystem("urn:system").setValue("testSearchByIdentifier02"); p2.addIdentifier().setSystem("urn:system").setValue("testSearchByIdentifier02");
p2.addName().setFamily("testSearchByIdentifierFamily01").addGiven("testSearchByIdentifierGiven02"); p2.addName().setFamily("testSearchByIdentifierFamily01").addGiven("testSearchByIdentifierGiven02");
ourClient.create().resource(p2).execute().getId(); ourClient.create().resource(p2).execute();
//@formatter:off //@formatter:off
Bundle actual = ourClient Bundle actual = ourClient
@ -2929,7 +2933,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
try { try {
assertEquals(200, response.getStatusLine().getStatusCode()); assertEquals(200, response.getStatusLine().getStatusCode());
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(response.getEntity().getContent()); response.getEntity().getContent().close();
ourLog.info(output); ourLog.info(output);
List<IIdType> ids = toUnqualifiedVersionlessIds(myFhirCtx.newXmlParser().parseResource(Bundle.class, output)); List<IIdType> ids = toUnqualifiedVersionlessIds(myFhirCtx.newXmlParser().parseResource(Bundle.class, output));
ourLog.info(ids.toString()); ourLog.info(ids.toString());
@ -2943,7 +2947,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
try { try {
assertEquals(200, response.getStatusLine().getStatusCode()); assertEquals(200, response.getStatusLine().getStatusCode());
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(response.getEntity().getContent()); response.getEntity().getContent().close();
ourLog.info(output); ourLog.info(output);
List<IIdType> ids = toUnqualifiedVersionlessIds(myFhirCtx.newXmlParser().parseResource(Bundle.class, output)); List<IIdType> ids = toUnqualifiedVersionlessIds(myFhirCtx.newXmlParser().parseResource(Bundle.class, output));
ourLog.info(ids.toString()); ourLog.info(ids.toString());
@ -2982,7 +2986,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
try { try {
assertEquals(200, response.getStatusLine().getStatusCode()); assertEquals(200, response.getStatusLine().getStatusCode());
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(response.getEntity().getContent()); response.getEntity().getContent().close();
ourLog.info(output); ourLog.info(output);
List<String> ids = toUnqualifiedVersionlessIdValues(myFhirCtx.newXmlParser().parseResource(Bundle.class, output)); List<String> ids = toUnqualifiedVersionlessIdValues(myFhirCtx.newXmlParser().parseResource(Bundle.class, output));
ourLog.info(ids.toString()); ourLog.info(ids.toString());
@ -2996,7 +3000,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
try { try {
assertEquals(200, response.getStatusLine().getStatusCode()); assertEquals(200, response.getStatusLine().getStatusCode());
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(response.getEntity().getContent()); response.getEntity().getContent().close();
ourLog.info(output); ourLog.info(output);
List<String> ids = toUnqualifiedVersionlessIdValues(myFhirCtx.newXmlParser().parseResource(Bundle.class, output)); List<String> ids = toUnqualifiedVersionlessIdValues(myFhirCtx.newXmlParser().parseResource(Bundle.class, output));
ourLog.info(ids.toString()); ourLog.info(ids.toString());
@ -3034,10 +3038,10 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
.where(Patient.ORGANIZATION.hasAnyOfIds(Arrays.asList(o1id.getIdPart(), o2id.getIdPart()))) .where(Patient.ORGANIZATION.hasAnyOfIds(Arrays.asList(o1id.getIdPart(), o2id.getIdPart())))
.encodedJson().prettyPrint().returnBundle(Bundle.class).execute(); .encodedJson().prettyPrint().returnBundle(Bundle.class).execute();
//@formatter:on //@formatter:on
Set<String> expectedIds = new HashSet<String>(); Set<String> expectedIds = new HashSet<>();
expectedIds.add(p1Id.getIdPart()); expectedIds.add(p1Id.getIdPart());
expectedIds.add(p2Id.getIdPart()); expectedIds.add(p2Id.getIdPart());
Set<String> actualIds = new HashSet<String>(); Set<String> actualIds = new HashSet<>();
for (BundleEntryComponent ele : actual.getEntry()) { for (BundleEntryComponent ele : actual.getEntry()) {
actualIds.add(ele.getResource().getIdElement().getIdPart()); actualIds.add(ele.getResource().getIdElement().getIdPart());
} }
@ -3094,7 +3098,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
assertThat(respString, containsString("Invalid parameter chain: subject.id")); assertThat(respString, containsString("Invalid parameter chain: subject.id"));
assertEquals(400, resp.getStatusLine().getStatusCode()); assertEquals(400, resp.getStatusLine().getStatusCode());
} finally { } finally {
IOUtils.closeQuietly(resp.getEntity().getContent()); resp.getEntity().getContent().close();
} }
ourLog.info("Outgoing post: {}", httpPost); ourLog.info("Outgoing post: {}", httpPost);
} }
@ -3212,14 +3216,11 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
IIdType moId = myMedicationAdministrationDao.create(ma).getId().toUnqualifiedVersionless(); IIdType moId = myMedicationAdministrationDao.create(ma).getId().toUnqualifiedVersionless();
HttpGet get = new HttpGet(ourServerBase + "/MedicationAdministration?medication.code=04823543"); HttpGet get = new HttpGet(ourServerBase + "/MedicationAdministration?medication.code=04823543");
CloseableHttpResponse response = ourHttpClient.execute(get); try (CloseableHttpResponse response = ourHttpClient.execute(get)) {
try {
assertEquals(200, response.getStatusLine().getStatusCode()); assertEquals(200, response.getStatusLine().getStatusCode());
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseString); ourLog.info(responseString);
assertThat(responseString, containsString(moId.getIdPart())); assertThat(responseString, containsString(moId.getIdPart()));
} finally {
response.close();
} }
} }
@ -3244,7 +3245,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
assertThat(ids, contains(oid1)); assertThat(ids, contains(oid1));
assertThat(ids, not(contains(oid2))); assertThat(ids, not(contains(oid2)));
} finally { } finally {
IOUtils.closeQuietly(resp); resp.close();
} }
} }
@ -3258,7 +3259,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Patient patient = new Patient(); Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("0"); patient.addIdentifier().setSystem("urn:system").setValue("0");
patient.addName().setFamily(methodName).addGiven("Joe"); patient.addName().setFamily(methodName).addGiven("Joe");
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
} }
for (int i = 1; i <= 20; i++) { for (int i = 1; i <= 20; i++) {
@ -3289,7 +3290,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
HttpGet get = new HttpGet(ourServerBase + search); HttpGet get = new HttpGet(ourServerBase + search);
CloseableHttpResponse response = ourHttpClient.execute(get); CloseableHttpResponse response = ourHttpClient.execute(get);
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(response.getEntity().getContent()); response.getEntity().getContent().close();
ourLog.info(resp); ourLog.info(resp);
Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, resp); Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, resp);
matches = bundle.getTotal(); matches = bundle.getTotal();
@ -3326,7 +3327,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test @Test
public void testSearchReusesNoParams() { public void testSearchReusesNoParams() {
List<IBaseResource> resources = new ArrayList<IBaseResource>(); List<IBaseResource> resources = new ArrayList<>();
for (int i = 0; i < 50; i++) { for (int i = 0; i < 50; i++) {
Organization org = new Organization(); Organization org = new Organization();
org.setName("HELLO"); org.setName("HELLO");
@ -3357,7 +3358,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test @Test
public void testSearchReusesResultsDisabled() { public void testSearchReusesResultsDisabled() {
List<IBaseResource> resources = new ArrayList<IBaseResource>(); List<IBaseResource> resources = new ArrayList<>();
for (int i = 0; i < 50; i++) { for (int i = 0; i < 50; i++) {
Organization org = new Organization(); Organization org = new Organization();
org.setName("HELLO"); org.setName("HELLO");
@ -3403,7 +3404,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test @Test
public void testSearchReusesResultsEnabled() throws Exception { public void testSearchReusesResultsEnabled() throws Exception {
List<IBaseResource> resources = new ArrayList<IBaseResource>(); List<IBaseResource> resources = new ArrayList<>();
for (int i = 0; i < 50; i++) { for (int i = 0; i < 50; i++) {
Organization org = new Organization(); Organization org = new Organization();
org.setName("HELLO"); org.setName("HELLO");
@ -3422,12 +3423,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
.execute(); .execute();
final String uuid1 = toSearchUuidFromLinkNext(result1); final String uuid1 = toSearchUuidFromLinkNext(result1);
Search search1 = newTxTemplate().execute(new TransactionCallback<Search>() { Search search1 = newTxTemplate().execute(theStatus -> mySearchEntityDao.findByUuid(uuid1));
@Override
public Search doInTransaction(TransactionStatus theStatus) {
return mySearchEntityDao.findByUuid(uuid1);
}
});
Date lastReturned1 = search1.getSearchLastReturned(); Date lastReturned1 = search1.getSearchLastReturned();
Bundle result2 = ourClient Bundle result2 = ourClient
@ -3439,12 +3435,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
.execute(); .execute();
final String uuid2 = toSearchUuidFromLinkNext(result2); final String uuid2 = toSearchUuidFromLinkNext(result2);
Search search2 = newTxTemplate().execute(new TransactionCallback<Search>() { Search search2 = newTxTemplate().execute(theStatus -> mySearchEntityDao.findByUuid(uuid2));
@Override
public Search doInTransaction(TransactionStatus theStatus) {
return mySearchEntityDao.findByUuid(uuid2);
}
});
Date lastReturned2 = search2.getSearchLastReturned(); Date lastReturned2 = search2.getSearchLastReturned();
assertTrue(lastReturned2.getTime() > lastReturned1.getTime()); assertTrue(lastReturned2.getTime() > lastReturned1.getTime());
@ -3467,7 +3458,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test @Test
public void testSearchReusesResultsEnabledNoParams() { public void testSearchReusesResultsEnabledNoParams() {
List<IBaseResource> resources = new ArrayList<IBaseResource>(); List<IBaseResource> resources = new ArrayList<>();
for (int i = 0; i < 50; i++) { for (int i = 0; i < 50; i++) {
Organization org = new Organization(); Organization org = new Organization();
org.setName("HELLO"); org.setName("HELLO");
@ -3484,12 +3475,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
.execute(); .execute();
final String uuid1 = toSearchUuidFromLinkNext(result1); final String uuid1 = toSearchUuidFromLinkNext(result1);
Search search1 = newTxTemplate().execute(new TransactionCallback<Search>() { Search search1 = newTxTemplate().execute(theStatus -> mySearchEntityDao.findByUuid(uuid1));
@Override
public Search doInTransaction(TransactionStatus theStatus) {
return mySearchEntityDao.findByUuid(uuid1);
}
});
Date lastReturned1 = search1.getSearchLastReturned(); Date lastReturned1 = search1.getSearchLastReturned();
Bundle result2 = ourClient Bundle result2 = ourClient
@ -3499,12 +3485,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
.execute(); .execute();
final String uuid2 = toSearchUuidFromLinkNext(result2); final String uuid2 = toSearchUuidFromLinkNext(result2);
Search search2 = newTxTemplate().execute(new TransactionCallback<Search>() { Search search2 = newTxTemplate().execute(theStatus -> mySearchEntityDao.findByUuid(uuid2));
@Override
public Search doInTransaction(TransactionStatus theStatus) {
return mySearchEntityDao.findByUuid(uuid2);
}
});
Date lastReturned2 = search2.getSearchLastReturned(); Date lastReturned2 = search2.getSearchLastReturned();
assertTrue(lastReturned2.getTime() > lastReturned1.getTime()); assertTrue(lastReturned2.getTime() > lastReturned1.getTime());
@ -3601,10 +3582,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
ourLog.info("** Done searching in {}ms with count of 1", sw.getMillis()); ourLog.info("** Done searching in {}ms with count of 1", sw.getMillis());
ourLog.info(myCapturingInterceptor.getLastResponse().getAllHeaders().toString()); ourLog.info(myCapturingInterceptor.getLastResponse().getAllHeaders().toString());
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), Matchers.<String>empty()); assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), Matchers.empty());
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE.toLowerCase()), Matchers.<String>empty()); assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE.toLowerCase()), Matchers.empty());
String msg = "Total is " + found.getTotalElement().getValue() + " and took " + sw.getMillis() + " millis";
// When we've only got one DB connection available, we are forced to wait for the // When we've only got one DB connection available, we are forced to wait for the
// search to finish before returning // search to finish before returning
@ -3667,8 +3646,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
.count(1) .count(1)
.execute(); .execute();
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), Matchers.<String>empty()); assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), Matchers.empty());
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE.toLowerCase()), Matchers.<String>empty()); assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE.toLowerCase()), Matchers.empty());
// WHen we've only got one DB connection available, we are forced to wait for the // WHen we've only got one DB connection available, we are forced to wait for the
// search to finish before returning // search to finish before returning
@ -3715,7 +3694,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Patient pat = new Patient(); Patient pat = new Patient();
pat.addIdentifier().setSystem("urn:system:rpr4").setValue("testSearchWithInclude02"); pat.addIdentifier().setSystem("urn:system:rpr4").setValue("testSearchWithInclude02");
pat.getManagingOrganization().setReferenceElement(orgId.toUnqualifiedVersionless()); pat.getManagingOrganization().setReferenceElement(orgId.toUnqualifiedVersionless());
ourClient.create().resource(pat).prettyPrint().encodedXml().execute().getId(); ourClient.create().resource(pat).prettyPrint().encodedXml().execute();
Bundle found = ourClient Bundle found = ourClient
.search() .search()
@ -3886,7 +3865,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
assertThat(ids, contains(id1.getValue())); assertThat(ids, contains(id1.getValue()));
assertThat(ids, not(contains(id2.getValue()))); assertThat(ids, not(contains(id2.getValue())));
} finally { } finally {
IOUtils.closeQuietly(resp); resp.close();
} }
} }
@ -3917,7 +3896,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
ourLog.info(responseContent); ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals(200, status.getStatusLine().getStatusCode());
} finally { } finally {
IOUtils.closeQuietly(status.getEntity().getContent()); status.getEntity().getContent().close();
} }
} }
@ -4181,13 +4160,10 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
String contents = loadClasspath("/update.xml"); String contents = loadClasspath("/update.xml");
HttpPost post = new HttpPost(ourServerBase); HttpPost post = new HttpPost(ourServerBase);
post.setEntity(new StringEntity(contents, ContentType.create("application/xml+fhir", "UTF-8"))); post.setEntity(new StringEntity(contents, ContentType.create("application/xml+fhir", "UTF-8")));
CloseableHttpResponse resp = ourHttpClient.execute(post); try (CloseableHttpResponse resp = ourHttpClient.execute(post)) {
try {
assertEquals(200, resp.getStatusLine().getStatusCode()); assertEquals(200, resp.getStatusLine().getStatusCode());
String output = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8); String output = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output); ourLog.info(output);
} finally {
resp.close();
} }
} }
@ -4199,7 +4175,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
p1.setManagingOrganization(new Reference("Organization/99999999999")); p1.setManagingOrganization(new Reference("Organization/99999999999"));
try { try {
ourClient.create().resource(p1).execute().getId(); ourClient.create().resource(p1).execute();
fail(); fail();
} catch (InvalidRequestException e) { } catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Organization/99999999999")); assertThat(e.getMessage(), containsString("Organization/99999999999"));
@ -4217,16 +4193,13 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
HttpPut post = new HttpPut(ourServerBase + "/Patient"); HttpPut post = new HttpPut(ourServerBase + "/Patient");
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post); try (CloseableHttpResponse response = ourHttpClient.execute(post)) {
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseString); ourLog.info(responseString);
assertEquals(400, response.getStatusLine().getStatusCode()); assertEquals(400, response.getStatusLine().getStatusCode());
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, responseString); OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, responseString);
assertThat(oo.getIssue().get(0).getDiagnostics(), assertThat(oo.getIssue().get(0).getDiagnostics(),
containsString("Can not update resource, request URL must contain an ID element for update (PUT) operation (it must be of the form [base]/[resource type]/[id])")); containsString("Can not update resource, request URL must contain an ID element for update (PUT) operation (it must be of the form [base]/[resource type]/[id])"));
} finally {
response.close();
} }
} }
@ -4241,15 +4214,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
HttpPut post = new HttpPut(ourServerBase + "/Patient"); HttpPut post = new HttpPut(ourServerBase + "/Patient");
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post); try (CloseableHttpResponse response = ourHttpClient.execute(post)) {
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseString); ourLog.info(responseString);
assertThat(responseString, containsString("Can not update resource, request URL must contain an ID element for update (PUT) operation (it must be of the form [base]/[resource type]/[id])")); assertThat(responseString, containsString("Can not update resource, request URL must contain an ID element for update (PUT) operation (it must be of the form [base]/[resource type]/[id])"));
assertThat(responseString, containsString("<OperationOutcome")); assertThat(responseString, containsString("<OperationOutcome"));
assertEquals(400, response.getStatusLine().getStatusCode()); assertEquals(400, response.getStatusLine().getStatusCode());
} finally {
response.close();
} }
} }
@ -4267,15 +4237,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
HttpPut post = new HttpPut(ourServerBase + "/Patient/FOO"); HttpPut post = new HttpPut(ourServerBase + "/Patient/FOO");
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post); try (CloseableHttpResponse response = ourHttpClient.execute(post)) {
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseString); ourLog.info(responseString);
assertThat(responseString, containsString("Can not update resource, request URL must contain an ID element for update (PUT) operation (it must be of the form [base]/[resource type]/[id])")); assertThat(responseString, containsString("Can not update resource, request URL must contain an ID element for update (PUT) operation (it must be of the form [base]/[resource type]/[id])"));
assertThat(responseString, containsString("<OperationOutcome")); assertThat(responseString, containsString("<OperationOutcome"));
assertEquals(400, response.getStatusLine().getStatusCode()); assertEquals(400, response.getStatusLine().getStatusCode());
} finally {
response.close();
} }
} }
@ -4481,7 +4448,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
try { try {
assertEquals(200, response.getStatusLine().getStatusCode()); assertEquals(200, response.getStatusLine().getStatusCode());
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(response.getEntity().getContent()); response.getEntity().getContent().close();
Patient respPt = myFhirCtx.newXmlParser().parseResource(Patient.class, responseString); Patient respPt = myFhirCtx.newXmlParser().parseResource(Patient.class, responseString);
assertEquals("2", respPt.getIdElement().getVersionIdPart()); assertEquals("2", respPt.getIdElement().getVersionIdPart());

View File

@ -1,8 +1,11 @@
package ca.uhn.fhir.jpa.provider.r4; package ca.uhn.fhir.jpa.provider.r4;
import ca.uhn.fhir.jpa.config.TestR4Config;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl; import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
import com.google.common.collect.Lists;
import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Narrative; import org.hl7.fhir.r4.model.Narrative;
import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient;
@ -11,6 +14,8 @@ import org.junit.AfterClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.test.util.AopTestUtils; import org.springframework.test.util.AopTestUtils;
import java.util.ArrayList;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@SuppressWarnings("Duplicates") @SuppressWarnings("Duplicates")
@ -26,6 +31,7 @@ public class ResourceProviderSummaryModeR4Test extends BaseResourceProviderR4Tes
myDaoConfig.setCountSearchResultsUpTo(null); myDaoConfig.setCountSearchResultsUpTo(null);
mySearchCoordinatorSvcRaw.setLoadingThrottleForUnitTests(null); mySearchCoordinatorSvcRaw.setLoadingThrottleForUnitTests(null);
mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE); mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE);
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
} }
@Override @Override
@ -34,9 +40,11 @@ public class ResourceProviderSummaryModeR4Test extends BaseResourceProviderR4Tes
myDaoConfig.setCountSearchResultsUpTo(5); myDaoConfig.setCountSearchResultsUpTo(5);
mySearchCoordinatorSvcRaw = AopTestUtils.getTargetObject(mySearchCoordinatorSvc); mySearchCoordinatorSvcRaw = AopTestUtils.getTargetObject(mySearchCoordinatorSvc);
mySearchCoordinatorSvcRaw.setLoadingThrottleForUnitTests(250); mySearchCoordinatorSvcRaw.setLoadingThrottleForUnitTests(50);
mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(5); mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(5);
myDaoConfig.setSearchPreFetchThresholds(Lists.newArrayList(20, 50, -1));
runInTransaction(() -> { runInTransaction(() -> {
for (int i = 0; i < 104; i++) { for (int i = 0; i < 104; i++) {
Patient p = new Patient(); Patient p = new Patient();

View File

@ -422,6 +422,10 @@ public class SearchCoordinatorSvcImplTest {
return myWrap.next(); return myWrap.next();
} }
@Override
public int getSkippedCount() {
return myWrap.getSkippedCount();
}
} }
public static class ResultIterator extends BaseIterator<Long> implements IResultIterator { public static class ResultIterator extends BaseIterator<Long> implements IResultIterator {
@ -441,16 +445,29 @@ public class SearchCoordinatorSvcImplTest {
public Long next() { public Long next() {
return myWrap.next(); return myWrap.next();
} }
@Override
public int getSkippedCount() {
return 0;
}
} }
public static class SlowIterator extends BaseIterator<Long> implements IResultIterator { public static class SlowIterator extends BaseIterator<Long> implements IResultIterator {
private final IResultIterator myResultIteratorWrap;
private int myDelay; private int myDelay;
private Iterator<Long> myWrap; private Iterator<Long> myWrap;
public SlowIterator(Iterator<Long> theWrap, int theDelay) { public SlowIterator(Iterator<Long> theWrap, int theDelay) {
myWrap = theWrap; myWrap = theWrap;
myDelay = theDelay; myDelay = theDelay;
myResultIteratorWrap = null;
}
public SlowIterator(IResultIterator theWrap, int theDelay) {
myWrap = theWrap;
myResultIteratorWrap = theWrap;
myDelay = theDelay;
} }
@Override @Override
@ -468,6 +485,15 @@ public class SearchCoordinatorSvcImplTest {
return myWrap.next(); return myWrap.next();
} }
@Override
public int getSkippedCount() {
if (myResultIteratorWrap == null) {
return 0;
} else {
return myResultIteratorWrap.getSkippedCount();
}
}
} }
@AfterClass @AfterClass

View File

@ -184,6 +184,10 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
.addIndex("IDX_SP_STRING_HASH_NRM") .addIndex("IDX_SP_STRING_HASH_NRM")
.unique(false) .unique(false)
.withColumns("HASH_NORM_PREFIX", "SP_VALUE_NORMALIZED"); .withColumns("HASH_NORM_PREFIX", "SP_VALUE_NORMALIZED");
spidxString
.addColumn("HASH_EXACT")
.nullable()
.type(AddColumnTask.ColumnTypeEnum.LONG);
spidxString spidxString
.addIndex("IDX_SP_STRING_HASH_EXCT") .addIndex("IDX_SP_STRING_HASH_EXCT")
.unique(false) .unique(false)
@ -254,6 +258,10 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
.addIndex("IDX_SP_URI_HASH_IDENTITY") .addIndex("IDX_SP_URI_HASH_IDENTITY")
.unique(false) .unique(false)
.withColumns("HASH_IDENTITY", "SP_URI"); .withColumns("HASH_IDENTITY", "SP_URI");
spidxUri
.addColumn("HASH_URI")
.nullable()
.type(AddColumnTask.ColumnTypeEnum.LONG);
spidxUri spidxUri
.addIndex("IDX_SP_URI_HASH_URI") .addIndex("IDX_SP_URI_HASH_URI")
.unique(false) .unique(false)
@ -290,7 +298,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
"where HFJ_RES_PARAM_PRESENT.HASH_PRESENCE is null"; "where HFJ_RES_PARAM_PRESENT.HASH_PRESENCE is null";
consolidateSearchParamPresenceIndexesTask.addQuery(sql, ArbitrarySqlTask.QueryModeEnum.BATCH_UNTIL_NO_MORE, t -> { consolidateSearchParamPresenceIndexesTask.addQuery(sql, ArbitrarySqlTask.QueryModeEnum.BATCH_UNTIL_NO_MORE, t -> {
Long pid = (Long) t.get("PID"); Long pid = (Long) t.get("PID");
Boolean present = (Boolean) t.get("HASH_PRESENCE"); Boolean present = (Boolean) t.get("SP_PRESENT");
String resType = (String) t.get("RES_TYPE"); String resType = (String) t.get("RES_TYPE");
String paramName = (String) t.get("PARAM_NAME"); String paramName = (String) t.get("PARAM_NAME");
Long hash = SearchParamPresent.calculateHashPresence(resType, paramName, present); Long hash = SearchParamPresent.calculateHashPresence(resType, paramName, present);

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

@ -215,9 +215,11 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
linkPrev = RestfulServerUtils.createPagingLink(theIncludes, serverBase, searchId, theResult.getPreviousPageId(), theRequest.getParameters(), prettyPrint, theBundleType); linkPrev = RestfulServerUtils.createPagingLink(theIncludes, serverBase, searchId, theResult.getPreviousPageId(), theRequest.getParameters(), prettyPrint, theBundleType);
} }
} else if (searchId != null) { } else if (searchId != null) {
int offset = theOffset + resourceList.size();
// We're doing offset pages // We're doing offset pages
if (numTotalResults == null || theOffset + numToReturn < numTotalResults) { if (numTotalResults == null || offset < numTotalResults) {
linkNext = (RestfulServerUtils.createPagingLink(theIncludes, serverBase, searchId, theOffset + numToReturn, numToReturn, theRequest.getParameters(), prettyPrint, theBundleType)); linkNext = (RestfulServerUtils.createPagingLink(theIncludes, serverBase, searchId, offset, numToReturn, theRequest.getParameters(), prettyPrint, theBundleType));
} }
if (theOffset > 0) { if (theOffset > 0) {
int start = Math.max(0, theOffset - theLimit); int start = Math.max(0, theOffset - theLimit);

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

@ -30,12 +30,14 @@ package org.hl7.fhir.instance.model;
*/ */
// Generated on Wed, Jul 13, 2016 05:32+1000 for FHIR v1.0.2 // Generated on Wed, Jul 13, 2016 05:32+1000 for FHIR v1.0.2
import java.util.*;
import ca.uhn.fhir.model.api.annotation.*;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
import ca.uhn.fhir.model.api.annotation.*; import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/** /**
* A request to perform an action. * A request to perform an action.
*/ */

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

View File

@ -71,6 +71,11 @@
timezones where the date that could apply. This makes the search slightly more timezones where the date that could apply. This makes the search slightly more
inclusive, which errs on the side of caution. inclusive, which errs on the side of caution.
</action> </action>
<action type="fix">
A bug was fixed in the JPA server $expunge operation where a database connection
could sometimes be opened and not returned to the pool immediately, leading to
pool starvation if the operation was called many times in a row.
</action>
</release> </release>
<release version="3.5.0" date="2018-09-17"> <release version="3.5.0" date="2018-09-17">