diff --git a/.gitignore b/.gitignore
index 09734b19d32..db79bca9c1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -141,6 +141,8 @@ local.properties
**/.target
**/.project
**/.classpath
+**/.factorypath
+**/.springBeans
# PDT-specific
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 00000000000..037f9f0e7a7
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,7 @@
+version: 1.0.{build}
+image: Visual Studio 2017
+cache:
+ - C:\maven\
+ - C:\Users\appveyor\.m2
+build_script:
+ - cmd: mvn -P MINPARALLEL,ALLMODULES install
diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml
index 8b0168ae690..93b7bdaed96 100644
--- a/hapi-deployable-pom/pom.xml
+++ b/hapi-deployable-pom/pom.xml
@@ -116,10 +116,10 @@
spring-jcl
-
- changelog.txt
+
+ changelog.txt
javac.bat
-
+
diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirDbConfig.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirDbConfig.java
index 70751c6f170..e7f00c4d037 100644
--- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirDbConfig.java
+++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirDbConfig.java
@@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.demo;
import java.util.Properties;
+import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -23,6 +24,7 @@ public class FhirDbConfig {
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
extraProperties.put("hibernate.cache.use_structured_entries", "false");
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
+ extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
extraProperties.put("hibernate.search.default.indexBase", "target/lucenefiles");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/AdditionalRequestHeadersInterceptor.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/AdditionalRequestHeadersInterceptor.java
new file mode 100644
index 00000000000..840a47f4db4
--- /dev/null
+++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/AdditionalRequestHeadersInterceptor.java
@@ -0,0 +1,96 @@
+package ca.uhn.fhir.rest.client.interceptor;
+
+import ca.uhn.fhir.rest.client.api.IClientInterceptor;
+import ca.uhn.fhir.rest.client.api.IHttpRequest;
+import ca.uhn.fhir.rest.client.api.IHttpResponse;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * This interceptor adds arbitrary header values to requests made by the client.
+ */
+public class AdditionalRequestHeadersInterceptor implements IClientInterceptor {
+ private final Map> additionalHttpHeaders = new HashMap<>();
+
+ public AdditionalRequestHeadersInterceptor() {
+ this(new HashMap>());
+ }
+
+ public AdditionalRequestHeadersInterceptor(Map> additionalHttpHeaders) {
+ super();
+ if (additionalHttpHeaders != null) {
+ this.additionalHttpHeaders.putAll(additionalHttpHeaders);
+ }
+ }
+
+ /**
+ * Adds the given header value.
+ * Note that {@code headerName} and {@code headerValue} cannot be null.
+ * @param headerName the name of the header
+ * @param headerValue the value to add for the header
+ * @throws NullPointerException if either parameter is {@code null}
+ */
+ public void addHeaderValue(String headerName, String headerValue) {
+ Objects.requireNonNull(headerName, "headerName cannot be null");
+ Objects.requireNonNull(headerValue, "headerValue cannot be null");
+
+ getHeaderValues(headerName).add(headerValue);
+ }
+
+ /**
+ * Adds the list of header values for the given header.
+ * Note that {@code headerName} and {@code headerValues} cannot be null.
+ * @param headerName the name of the header
+ * @param headerValues the list of values to add for the header
+ * @throws NullPointerException if either parameter is {@code null}
+ */
+ public void addAllHeaderValues(String headerName, List headerValues) {
+ Objects.requireNonNull(headerName, "headerName cannot be null");
+ Objects.requireNonNull(headerValues, "headerValues cannot be null");
+
+ getHeaderValues(headerName).addAll(headerValues);
+ }
+
+ /**
+ * Gets the header values list for a given header.
+ * If the header doesn't have any values, an empty list will be returned.
+ * @param headerName the name of the header
+ * @return the list of values for the header
+ */
+ private List getHeaderValues(String headerName) {
+ if (additionalHttpHeaders.get(headerName) == null) {
+ additionalHttpHeaders.put(headerName, new ArrayList());
+ }
+ return additionalHttpHeaders.get(headerName);
+ }
+
+ /**
+ * Adds the additional header values to the HTTP request.
+ * @param theRequest the HTTP request
+ */
+ @Override
+ public void interceptRequest(IHttpRequest theRequest) {
+ for (Map.Entry> header : additionalHttpHeaders.entrySet()) {
+ for (String headerValue : header.getValue()) {
+ if (headerValue != null) {
+ theRequest.addHeader(header.getKey(), headerValue);
+ }
+ }
+ }
+ }
+
+ /**
+ * Does nothing since this interceptor is not concerned with the response.
+ * @param theResponse the HTTP response
+ * @throws IOException
+ */
+ @Override
+ public void interceptResponse(IHttpResponse theResponse) throws IOException {
+ // Do nothing. This interceptor is not concerned with the response.
+ }
+}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java
index 4df1d50cfd1..056710bcca0 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java
@@ -26,20 +26,8 @@ import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
-import org.apache.lucene.analysis.core.LowerCaseFilterFactory;
-import org.apache.lucene.analysis.core.StopFilterFactory;
-import org.apache.lucene.analysis.miscellaneous.WordDelimiterFilterFactory;
-import org.apache.lucene.analysis.ngram.EdgeNGramFilterFactory;
-import org.apache.lucene.analysis.ngram.NGramFilterFactory;
-import org.apache.lucene.analysis.pattern.PatternTokenizerFactory;
-import org.apache.lucene.analysis.phonetic.PhoneticFilterFactory;
-import org.apache.lucene.analysis.snowball.SnowballPorterFilterFactory;
-import org.apache.lucene.analysis.standard.StandardFilterFactory;
-import org.apache.lucene.analysis.standard.StandardTokenizerFactory;
-import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.OptimisticLock;
import org.hibernate.search.annotations.*;
-import org.hibernate.search.annotations.Parameter;
import javax.persistence.*;
import javax.persistence.Index;
@@ -61,53 +49,6 @@ import static org.apache.commons.lang3.StringUtils.defaultString;
@Index(name = "IDX_RES_TYPE", columnList = "RES_TYPE"),
@Index(name = "IDX_INDEXSTATUS", columnList = "SP_INDEX_STATUS")
})
-@AnalyzerDefs({
- @AnalyzerDef(name = "autocompleteEdgeAnalyzer",
- tokenizer = @TokenizerDef(factory = PatternTokenizerFactory.class, params = {
- @Parameter(name = "pattern", value = "(.*)"),
- @Parameter(name = "group", value = "1")
- }),
- filters = {
- @TokenFilterDef(factory = LowerCaseFilterFactory.class),
- @TokenFilterDef(factory = StopFilterFactory.class),
- @TokenFilterDef(factory = EdgeNGramFilterFactory.class, params = {
- @Parameter(name = "minGramSize", value = "3"),
- @Parameter(name = "maxGramSize", value = "50")
- }),
- }),
- @AnalyzerDef(name = "autocompletePhoneticAnalyzer",
- tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
- filters = {
- @TokenFilterDef(factory = StandardFilterFactory.class),
- @TokenFilterDef(factory = StopFilterFactory.class),
- @TokenFilterDef(factory = PhoneticFilterFactory.class, params = {
- @Parameter(name = "encoder", value = "DoubleMetaphone")
- }),
- @TokenFilterDef(factory = SnowballPorterFilterFactory.class, params = {
- @Parameter(name = "language", value = "English")
- })
- }),
- @AnalyzerDef(name = "autocompleteNGramAnalyzer",
- tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
- filters = {
- @TokenFilterDef(factory = WordDelimiterFilterFactory.class),
- @TokenFilterDef(factory = LowerCaseFilterFactory.class),
- @TokenFilterDef(factory = NGramFilterFactory.class, params = {
- @Parameter(name = "minGramSize", value = "3"),
- @Parameter(name = "maxGramSize", value = "20")
- }),
- }),
- @AnalyzerDef(name = "standardAnalyzer",
- tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
- filters = {
- @TokenFilterDef(factory = LowerCaseFilterFactory.class),
- }),
- @AnalyzerDef(name = "exactAnalyzer",
- tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
- filters = {
- })
-}
-)
//@formatter:on
public class ResourceTable extends BaseHasResource implements Serializable {
static final int RESTYPE_LEN = 30;
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java
index 800e71ee10f..0cb567950db 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java
@@ -52,16 +52,12 @@ import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
-import org.apache.lucene.analysis.core.WhitespaceTokenizerFactory;
import org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.Analyzer;
-import org.hibernate.search.annotations.AnalyzerDef;
-import org.hibernate.search.annotations.AnalyzerDefs;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Fields;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.Store;
-import org.hibernate.search.annotations.TokenizerDef;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.search.DeferConceptIndexingInterceptor;
@@ -73,12 +69,6 @@ import ca.uhn.fhir.jpa.search.DeferConceptIndexingInterceptor;
}, indexes= {
@Index(name = "IDX_CONCEPT_INDEXSTATUS", columnList="INDEX_STATUS")
})
-@AnalyzerDefs({
- @AnalyzerDef(name = "conceptParentPidsAnalyzer",
- tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
- filters = {
- })
-})
public class TermConcept implements Serializable {
private static final int MAX_DESC_LENGTH = 400;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TermConcept.class);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/LuceneSearchMappingFactory.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/LuceneSearchMappingFactory.java
new file mode 100644
index 00000000000..1f950f28ed4
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/LuceneSearchMappingFactory.java
@@ -0,0 +1,53 @@
+package ca.uhn.fhir.jpa.search;
+
+import org.apache.lucene.analysis.core.LowerCaseFilterFactory;
+import org.apache.lucene.analysis.core.StopFilterFactory;
+import org.apache.lucene.analysis.core.WhitespaceTokenizerFactory;
+import org.apache.lucene.analysis.miscellaneous.WordDelimiterFilterFactory;
+import org.apache.lucene.analysis.ngram.EdgeNGramFilterFactory;
+import org.apache.lucene.analysis.ngram.NGramFilterFactory;
+import org.apache.lucene.analysis.pattern.PatternTokenizerFactory;
+import org.apache.lucene.analysis.phonetic.PhoneticFilterFactory;
+import org.apache.lucene.analysis.snowball.SnowballPorterFilterFactory;
+import org.apache.lucene.analysis.standard.StandardFilterFactory;
+import org.apache.lucene.analysis.standard.StandardTokenizerFactory;
+import org.hibernate.search.annotations.Factory;
+import org.hibernate.search.cfg.SearchMapping;
+
+/**
+ * Factory for defining the analysers.
+ */
+public class LuceneSearchMappingFactory {
+ @Factory
+ public SearchMapping getSearchMapping() {
+ SearchMapping mapping = new SearchMapping();
+
+ mapping.analyzerDef("autocompleteEdgeAnalyzer", PatternTokenizerFactory.class)
+ .tokenizerParam("pattern", "(.*)")
+ .tokenizerParam("group", "1")
+ .filter(LowerCaseFilterFactory.class)
+ .filter(StopFilterFactory.class)
+ .filter(EdgeNGramFilterFactory.class)
+ .param("minGramSize", "3")
+ .param("maxGramSize", "50")
+ .analyzerDef("autocompletePhoneticAnalyzer", StandardTokenizerFactory.class)
+ .filter(StandardFilterFactory.class)
+ .filter(StopFilterFactory.class)
+ .filter(PhoneticFilterFactory.class)
+ .param("encoder", "DoubleMetaphone")
+ .filter(SnowballPorterFilterFactory.class)
+ .param("language", "English")
+ .analyzerDef("autocompleteNGramAnalyzer", StandardTokenizerFactory.class)
+ .filter(WordDelimiterFilterFactory.class)
+ .filter(LowerCaseFilterFactory.class)
+ .filter(NGramFilterFactory.class)
+ .param("minGramSize", "3")
+ .param("maxGramSize", "20")
+ .analyzerDef("standardAnalyzer", StandardTokenizerFactory.class)
+ .filter(LowerCaseFilterFactory.class)
+ .analyzerDef("exactAnalyzer", StandardTokenizerFactory.class)
+ .analyzerDef("conceptParentPidsAnalyzer", WhitespaceTokenizerFactory.class);
+
+ return mapping;
+ }
+}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java
index 49950d34dd3..6f2a727e59e 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java
@@ -19,39 +19,51 @@ package ca.uhn.fhir.jpa.search;
* limitations under the License.
* #L%family
*/
-import java.util.*;
-import java.util.concurrent.*;
-
-import javax.persistence.EntityManager;
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.dao.IDao;
+import ca.uhn.fhir.jpa.dao.ISearchBuilder;
+import ca.uhn.fhir.jpa.dao.SearchParameterMap;
+import ca.uhn.fhir.jpa.dao.data.ISearchDao;
+import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao;
+import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
+import ca.uhn.fhir.jpa.entity.*;
+import ca.uhn.fhir.jpa.util.StopWatch;
+import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.Constants;
+import ca.uhn.fhir.rest.api.server.IBundleProvider;
+import ca.uhn.fhir.rest.server.SimpleBundleProvider;
+import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
+import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
+import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
+import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
+import ca.uhn.fhir.rest.server.method.PageMethodBinding;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.*;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
-import org.springframework.transaction.*;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
-import org.springframework.transaction.support.*;
+import org.springframework.transaction.support.TransactionCallback;
+import org.springframework.transaction.support.TransactionCallbackWithoutResult;
+import org.springframework.transaction.support.TransactionTemplate;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
-
-import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.jpa.dao.*;
-import ca.uhn.fhir.jpa.dao.data.*;
-import ca.uhn.fhir.jpa.entity.*;
-import ca.uhn.fhir.jpa.util.StopWatch;
-import ca.uhn.fhir.model.api.Include;
-import ca.uhn.fhir.rest.api.server.IBundleProvider;
-import ca.uhn.fhir.rest.server.SimpleBundleProvider;
-import ca.uhn.fhir.rest.server.exceptions.*;
-import ca.uhn.fhir.rest.server.method.PageMethodBinding;
+import javax.persistence.EntityManager;
+import java.util.*;
+import java.util.concurrent.*;
public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
public static final int DEFAULT_SYNC_SIZE = 250;
@@ -420,6 +432,19 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
}
}
+ /**
+ * A search task is a Callable task that runs in
+ * a thread pool to handle an individual search. One instance
+ * is created for any requested search and runs from the
+ * beginning to the end of the search.
+ *
+ * Understand:
+ * This class executes in its own thread separate from the
+ * web server client thread that made the request. We do that
+ * so that we can return to the client as soon as possible,
+ * but keep the search going in the background (and have
+ * the next page of results ready to go when the client asks).
+ */
public class SearchTask implements Callable {
private final IDao myCallingDao;
@@ -434,6 +459,9 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
private int myCountSaved = 0;
private String mySearchUuid;
+ /**
+ * Constructor
+ */
public SearchTask(Search theSearch, IDao theCallingDao, SearchParameterMap theParams, String theResourceType, String theSearchUuid) {
mySearch = theSearch;
myCallingDao = theCallingDao;
@@ -443,6 +471,11 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
mySearchUuid = theSearchUuid;
}
+ /**
+ * This method is called by the server HTTP thread, and
+ * will block until at least one page of results have been
+ * fetched from the DB, and will never block after that.
+ */
public Integer awaitInitialSync() {
ourLog.trace("Awaiting initial sync");
do {
@@ -451,6 +484,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
break;
}
} catch (InterruptedException e) {
+ // Shouldn't happen
throw new InternalErrorException(e);
}
} while (mySearch.getStatus() == SearchStatusEnum.LOADING);
@@ -459,11 +493,16 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
return mySearch.getTotalCount();
}
+ /**
+ * This is the method which actually performs the search.
+ * It is called automatically by the thread pool.
+ */
@Override
public Void call() throws Exception {
StopWatch sw = new StopWatch();
try {
+ // Create an initial search in the DB and give it an ID
saveSearch();
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
@@ -480,7 +519,9 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
} catch (Throwable t) {
/*
- * Don't print a stack trace for client errors.. that's just noisy
+ * Don't print a stack trace for client errors (i.e. requests that
+ * aren't valid because the client screwed up).. that's just noise
+ * in the logs and who needs that.
*/
boolean logged = false;
if (t instanceof BaseServerResponseException) {
@@ -535,13 +576,27 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
Class extends IBaseResource> resourceTypeClass = myContext.getResourceDefinition(myResourceType).getImplementingClass();
ISearchBuilder sb = myCallingDao.newSearchBuilder();
sb.setType(resourceTypeClass, myResourceType);
- Iterator theResultIter = sb.createQuery(myParams, mySearchUuid);
+ Iterator theResultIterator = sb.createQuery(myParams, mySearchUuid);
- while (theResultIter.hasNext()) {
- myUnsyncedPids.add(theResultIter.next());
- if (myUnsyncedPids.size() >= mySyncSize) {
- saveUnsynced(theResultIter);
+ while (theResultIterator.hasNext()) {
+ myUnsyncedPids.add(theResultIterator.next());
+
+ boolean shouldSync = myUnsyncedPids.size() >= mySyncSize;
+
+ if (myDaoConfig.getCountSearchResultsUpTo() != null &&
+ myDaoConfig.getCountSearchResultsUpTo() > 0 &&
+ myDaoConfig.getCountSearchResultsUpTo() < myUnsyncedPids.size()) {
+ shouldSync = false;
}
+
+ if (myUnsyncedPids.size() > 50000) {
+ shouldSync = true;
+ }
+
+ if (shouldSync) {
+ saveUnsynced(theResultIterator);
+ }
+
if (myLoadingThrottleForUnitTests != null) {
try {
Thread.sleep(myLoadingThrottleForUnitTests);
@@ -549,9 +604,12 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
// ignore
}
}
+
+ // Check if an abort got requested
Validate.isTrue(myAbortRequested == false, "Abort has been requested");
+
}
- saveUnsynced(theResultIter);
+ saveUnsynced(theResultIterator);
}
public CountDownLatch getCompletionLatch() {
@@ -648,22 +706,25 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
mySearch.setStatus(SearchStatusEnum.FINISHED);
}
}
+
mySearch.setNumFound(myCountSaved);
+
+ int numSynced;
+ synchronized (mySyncedPids) {
+ numSynced = mySyncedPids.size();
+ }
+
+ if (myDaoConfig.getCountSearchResultsUpTo() == null ||
+ myDaoConfig.getCountSearchResultsUpTo() <= 0 ||
+ myDaoConfig.getCountSearchResultsUpTo() <= numSynced) {
+ myInitialCollectionLatch.countDown();
+ }
+
doSaveSearch();
}
});
- int numSynced;
- synchronized (mySyncedPids) {
- numSynced = mySyncedPids.size();
- }
-
- if (myDaoConfig.getCountSearchResultsUpTo() == null ||
- myDaoConfig.getCountSearchResultsUpTo() <= 0 ||
- myDaoConfig.getCountSearchResultsUpTo() <= numSynced) {
- myInitialCollectionLatch.countDown();
- }
}
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java
index d2852edca9b..49c9107ce28 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java
@@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.config;
import ca.uhn.fhir.jpa.dao.DaoConfig;
-import ca.uhn.fhir.jpa.subscription.email.SubscriptionEmailInterceptor;
+import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import org.apache.commons.dbcp2.BasicDataSource;
@@ -60,6 +60,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
extraProperties.put("hibernate.show_sql", "false");
extraProperties.put("hibernate.hbm2ddl.auto", "update");
extraProperties.put("hibernate.dialect", "org.hibernate.dialect.DerbyTenSevenDialect");
+ extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider", "ram");
extraProperties.put("hibernate.search.lucene_version","LUCENE_CURRENT");
return extraProperties;
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java
index baa0a217359..46fb60ba025 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java
@@ -1,5 +1,15 @@
package ca.uhn.fhir.jpa.config;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import javax.persistence.EntityManagerFactory;
+import javax.sql.DataSource;
+
+import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
+import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.subscription.email.IEmailSender;
import ca.uhn.fhir.jpa.subscription.email.JavaMailEmailSender;
@@ -141,6 +151,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
extraProperties.put("hibernate.show_sql", "false");
extraProperties.put("hibernate.hbm2ddl.auto", "update");
extraProperties.put("hibernate.dialect", "org.hibernate.dialect.DerbyTenSevenDialect");
+ extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider", "ram");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
extraProperties.put("hibernate.search.autoregister_listeners", "true");
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java
index 82b78e27799..d40ea5d2284 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java
@@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.config;
import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import net.ttddyy.dsproxy.listener.ThreadQueryCountHolder;
@@ -125,6 +126,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
extraProperties.put("hibernate.show_sql", "false");
extraProperties.put("hibernate.hbm2ddl.auto", "update");
extraProperties.put("hibernate.dialect", "org.hibernate.dialect.DerbyTenSevenDialect");
+ extraProperties.put("hibernate.search.model_mapping", ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider", "ram");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
extraProperties.put("hibernate.search.autoregister_listeners", "true");
diff --git a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
index 90f791c0919..69569f72085 100644
--- a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
+++ b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
@@ -5,6 +5,7 @@ import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
+import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.time.DateUtils;
import org.hibernate.jpa.HibernatePersistenceProvider;
@@ -80,6 +81,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
extraProperties.put("hibernate.cache.use_structured_entries", "false");
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
+ extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
extraProperties.put("hibernate.search.default.indexBase", "target/lucenefiles");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
diff --git a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java
index 0d45ad6b25d..9a6b52e0a2d 100644
--- a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java
+++ b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java
@@ -1,10 +1,9 @@
package ca.uhn.fhir.jpa.demo;
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2;
-import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3;
import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu2;
-import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu3;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
@@ -80,6 +79,7 @@ public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 {
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
extraProperties.put("hibernate.cache.use_structured_entries", "false");
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
+ extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
extraProperties.put("hibernate.search.default.indexBase", "target/lucenefiles");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
diff --git a/hapi-fhir-jpaserver-examples/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java b/hapi-fhir-jpaserver-examples/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
index 4a19ef39012..ae55d7a1ed3 100644
--- a/hapi-fhir-jpaserver-examples/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
+++ b/hapi-fhir-jpaserver-examples/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
@@ -84,6 +84,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
extraProperties.put("hibernate.cache.use_structured_entries", "false");
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
+ extraProperties.put("hibernate.search.model_mapping", SearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
extraProperties.put("hibernate.search.default.indexBase", "target/lucenefiles");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java
index bf568c7d1c9..7b94f1b82df 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java
@@ -2,6 +2,7 @@ package ca.uhn.fhirtest.config;
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2;
import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu2;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
@@ -111,6 +112,7 @@ public class TdlDstu2Config extends BaseJavaConfigDstu2 {
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
extraProperties.put("hibernate.cache.use_structured_entries", "false");
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
+ extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider" ,"filesystem");
extraProperties.put("hibernate.search.default.indexBase", myFhirLuceneLocation);
extraProperties.put("hibernate.search.lucene_version","LUCENE_CURRENT");
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu3Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu3Config.java
index 45be4c9e38b..2d82c131f43 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu3Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu3Config.java
@@ -5,6 +5,7 @@ import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
+import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.time.DateUtils;
import org.hibernate.dialect.DerbyTenSevenDialect;
@@ -13,7 +14,6 @@ import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
@@ -94,6 +94,7 @@ public class TdlDstu3Config extends BaseJavaConfigDstu3 {
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
extraProperties.put("hibernate.cache.use_structured_entries", "false");
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
+ extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
extraProperties.put("hibernate.search.default.indexBase", myFhirLuceneLocation);
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java
index 7b6dabda413..095ca6c39fd 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java
@@ -2,6 +2,7 @@ package ca.uhn.fhirtest.config;
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2;
import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;
@@ -65,6 +66,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
retVal.setAllowExternalReferences(true);
retVal.getTreatBaseUrlsAsLocal().add("http://fhirtest.uhn.ca/baseDstu2");
retVal.getTreatBaseUrlsAsLocal().add("https://fhirtest.uhn.ca/baseDstu2");
+ retVal.setCountSearchResultsUpTo(TestR4Config.COUNT_SEARCH_RESULTS_UP_TO);
return retVal;
}
@@ -115,6 +117,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
extraProperties.put("hibernate.cache.use_structured_entries", "false");
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
+ extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
extraProperties.put("hibernate.search.default.indexBase", myFhirLuceneLocation);
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java
index 11189d2f7bc..1f5dcb028d0 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java
@@ -5,6 +5,7 @@ import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
+import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.time.DateUtils;
import org.hibernate.dialect.DerbyTenSevenDialect;
@@ -54,6 +55,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
retVal.setAllowExternalReferences(true);
retVal.getTreatBaseUrlsAsLocal().add("http://fhirtest.uhn.ca/baseDstu3");
retVal.getTreatBaseUrlsAsLocal().add("https://fhirtest.uhn.ca/baseDstu3");
+ retVal.setCountSearchResultsUpTo(TestR4Config.COUNT_SEARCH_RESULTS_UP_TO);
return retVal;
}
@@ -112,6 +114,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
extraProperties.put("hibernate.cache.use_structured_entries", "false");
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
+ extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
extraProperties.put("hibernate.search.default.indexBase", myFhirLuceneLocation);
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java
index d76e45b40f2..6a96849ea0a 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java
@@ -5,11 +5,11 @@ import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
+import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.time.DateUtils;
import org.hibernate.dialect.DerbyTenSevenDialect;
import org.hibernate.dialect.PostgreSQL94Dialect;
-import org.hibernate.dialect.PostgreSQL95Dialect;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Value;
@@ -110,6 +110,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
extraProperties.put("hibernate.cache.use_structured_entries", "false");
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
+ extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
extraProperties.put("hibernate.search.default.indexBase", myFhirLuceneLocation);
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml
new file mode 100644
index 00000000000..b66479550f3
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml
@@ -0,0 +1,118 @@
+
+ 4.0.0
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-spring-boot
+ 3.1.0-SNAPSHOT
+
+
+ hapi-fhir-spring-boot-autoconfigure
+
+ jar
+
+
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
+
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-base
+ ${project.version}
+ true
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-server
+ ${project.version}
+ true
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-jpaserver-base
+ ${project.version}
+ true
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-jaxrsserver-base
+ ${project.version}
+ true
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-client
+ ${project.version}
+ true
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-client-okhttp
+ ${project.version}
+ true
+
+
+ javax.servlet
+ javax.servlet-api
+ true
+
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework
+ spring-web
+ test
+
+
+ com.h2database
+ h2
+ test
+
+
+ ch.qos.logback
+ logback-classic
+ test
+
+
+ org.slf4j
+ log4j-over-slf4j
+ test
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-validation-resources-dstu2
+ ${project.version}
+ test
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-validation-resources-dstu2.1
+ ${project.version}
+ test
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-validation-resources-dstu3
+ ${project.version}
+ test
+
+
+
+
\ No newline at end of file
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirAutoConfiguration.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirAutoConfiguration.java
new file mode 100644
index 00000000000..3a108336b07
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirAutoConfiguration.java
@@ -0,0 +1,287 @@
+package ca.uhn.fhir.spring.boot.autoconfigure;
+
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.sql.DataSource;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
+import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2;
+import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3;
+import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.provider.BaseJpaProvider;
+import ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider;
+import ca.uhn.fhir.okhttp.client.OkHttpRestfulClientFactory;
+import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
+import ca.uhn.fhir.rest.client.api.IClientInterceptor;
+import ca.uhn.fhir.rest.client.api.IGenericClient;
+import ca.uhn.fhir.rest.client.api.IRestfulClientFactory;
+import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
+import ca.uhn.fhir.rest.server.IPagingProvider;
+import ca.uhn.fhir.rest.server.IResourceProvider;
+import ca.uhn.fhir.rest.server.RestfulServer;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor;
+import okhttp3.OkHttpClient;
+import org.apache.http.client.HttpClient;
+
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.condition.ResourceCondition;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.servlet.ServletRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.AnnotationAwareOrderComparator;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * {@link EnableAutoConfiguration Auto-configuration} for HAPI FHIR.
+ *
+ * @author Mathieu Ouellet
+ */
+@Configuration
+@AutoConfigureAfter({ DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
+@EnableConfigurationProperties(FhirProperties.class)
+public class FhirAutoConfiguration {
+
+ private final FhirProperties properties;
+
+ public FhirAutoConfiguration(FhirProperties properties) {
+ this.properties = properties;
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public FhirContext fhirContext() {
+ FhirContext fhirContext = new FhirContext(properties.getVersion());
+ return fhirContext;
+ }
+
+ @Configuration
+ @ConditionalOnClass(AbstractJaxRsProvider.class)
+ @EnableConfigurationProperties(FhirProperties.class)
+ @ConfigurationProperties("hapi.fhir.rest")
+ @SuppressWarnings("serial")
+ static class FhirRestfulServerConfiguration extends RestfulServer {
+
+ private final FhirProperties properties;
+
+ private final FhirContext fhirContext;
+
+ private final List resourceProviders;
+
+ private final IPagingProvider pagingProvider;
+
+ private final List interceptors;
+
+ private final List customizers;
+
+ public FhirRestfulServerConfiguration(
+ FhirProperties properties,
+ FhirContext fhirContext,
+ ObjectProvider> resourceProviders,
+ ObjectProvider pagingProvider,
+ ObjectProvider> interceptors,
+ ObjectProvider> customizers) {
+ this.properties = properties;
+ this.fhirContext = fhirContext;
+ this.resourceProviders = resourceProviders.getIfAvailable();
+ this.pagingProvider = pagingProvider.getIfAvailable();
+ this.interceptors = interceptors.getIfAvailable();
+ this.customizers = customizers.getIfAvailable();
+ }
+
+ @Bean
+ public ServletRegistrationBean fhirServerRegistrationBean() {
+ ServletRegistrationBean registration = new ServletRegistrationBean(this, this.properties.getServer().getPath());
+ registration.setLoadOnStartup(1);
+ return registration;
+ }
+
+ @Override
+ protected void initialize() throws ServletException {
+ super.initialize();
+
+ setFhirContext(this.fhirContext);
+ setResourceProviders(this.resourceProviders);
+ setPagingProvider(this.pagingProvider);
+ setInterceptors(this.interceptors);
+
+ setServerAddressStrategy(new HardcodedServerAddressStrategy(this.properties.getServer().getPath()));
+
+ customize();
+ }
+
+ private void customize() {
+ if (this.customizers != null) {
+ AnnotationAwareOrderComparator.sort(this.customizers);
+ for (FhirRestfulServerCustomizer customizer : this.customizers) {
+ customizer.customize(this);
+ }
+ }
+ }
+ }
+
+ @Configuration
+ @ConditionalOnClass(BaseJpaProvider.class)
+ @ConditionalOnBean(DataSource.class)
+ @EnableConfigurationProperties(FhirProperties.class)
+ static class FhirJpaServerConfiguration {
+
+ @Configuration
+ @EntityScan("ca.uhn.fhir.jpa.entity")
+ @EnableJpaRepositories(basePackages = "ca.uhn.fhir.jpa.dao.data")
+ static class FhirJpaDaoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ @ConfigurationProperties("hapi.fhir.jpa")
+ public DaoConfig fhirDaoConfig() {
+ DaoConfig fhirDaoConfig = new DaoConfig();
+ return fhirDaoConfig;
+ }
+ }
+
+ @Configuration
+ @ConditionalOnBean({ DaoConfig.class, RestfulServer.class })
+ @SuppressWarnings("rawtypes")
+ static class RestfulServerCustomizer implements FhirRestfulServerCustomizer {
+
+ private final BaseJpaSystemProvider systemProviders;
+
+ public RestfulServerCustomizer(ObjectProvider systemProviders) {
+ this.systemProviders = systemProviders.getIfAvailable();
+ }
+
+ @Override
+ public void customize(RestfulServer server) {
+ server.setPlainProviders(systemProviders);
+ }
+ }
+
+ @Configuration
+ @ConditionalOnMissingBean(type = "ca.uhn.fhir.jpa.config.BaseConfig")
+ @ConditionalOnProperty(name = "hapi.fhir.version", havingValue = "DSTU3")
+ static class Dstu3 extends BaseJavaConfigDstu3 {
+ }
+
+ @Configuration
+ @ConditionalOnMissingBean(type = "ca.uhn.fhir.jpa.config.BaseConfig")
+ @ConditionalOnProperty(name = "hapi.fhir.version", havingValue = "DSTU2")
+ static class Dstu2 extends BaseJavaConfigDstu2 {
+ }
+ }
+
+ @Configuration
+ @Conditional(FhirValidationConfiguration.SchemaAvailableCondition.class)
+ @ConditionalOnProperty(name = "hapi.fhir.validation.enabled", matchIfMissing = true)
+ static class FhirValidationConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public RequestValidatingInterceptor requestValidatingInterceptor() {
+ return new RequestValidatingInterceptor();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnProperty(name = "hapi.fhir.validation.request-only", havingValue = "false")
+ public ResponseValidatingInterceptor responseValidatingInterceptor() {
+ return new ResponseValidatingInterceptor();
+ }
+
+ static class SchemaAvailableCondition extends ResourceCondition {
+
+ SchemaAvailableCondition() {
+ super("ValidationSchema",
+ "hapi.fhir.validation",
+ "schema-location",
+ "classpath:/org/hl7/fhir/instance/model/schema",
+ "classpath:/org/hl7/fhir/dstu2016may/model/schema",
+ "classpath:/org/hl7/fhir/dstu3/model/schema");
+ }
+ }
+ }
+
+ @Configuration
+ @ConditionalOnProperty("hapi.fhir.server.url")
+ @EnableConfigurationProperties(FhirProperties.class)
+ static class FhirRestfulClientConfiguration {
+
+ private final FhirProperties properties;
+
+ private final List clientInterceptors;
+
+ public FhirRestfulClientConfiguration(FhirProperties properties, ObjectProvider> clientInterceptors) {
+ this.properties = properties;
+ this.clientInterceptors = clientInterceptors.getIfAvailable();
+ }
+
+ @Bean
+ @ConditionalOnBean(IRestfulClientFactory.class)
+ public IGenericClient fhirClient(final IRestfulClientFactory clientFactory) {
+ IGenericClient fhirClient = clientFactory.newGenericClient(this.properties.getServer().getUrl());
+ if (!CollectionUtils.isEmpty(this.clientInterceptors)) {
+ for (IClientInterceptor interceptor : this.clientInterceptors) {
+ fhirClient.registerInterceptor(interceptor);
+ }
+ }
+ return fhirClient;
+ }
+
+ @Configuration
+ @ConditionalOnClass(HttpClient.class)
+ @ConditionalOnMissingClass("okhttp3.OkHttpClient")
+ static class Apache {
+
+ private final FhirContext context;
+
+ public Apache(FhirContext context) {
+ this.context = context;
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ @ConfigurationProperties("hapi.fhir.rest.client.apache")
+ public IRestfulClientFactory fhirRestfulClientFactory() {
+ ApacheRestfulClientFactory restfulClientFactory = new ApacheRestfulClientFactory(this.context);
+ return restfulClientFactory;
+ }
+ }
+
+ @Configuration
+ @ConditionalOnClass(OkHttpClient.class)
+ static class OkHttp {
+
+ private final FhirContext context;
+
+ public OkHttp(FhirContext context) {
+ this.context = context;
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ @ConfigurationProperties("hapi.fhir.rest.client.okhttp")
+ public IRestfulClientFactory fhirRestfulClientFactory() {
+ OkHttpRestfulClientFactory restfulClientFactory = new OkHttpRestfulClientFactory(this.context);
+ return restfulClientFactory;
+ }
+ }
+ }
+
+}
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirProperties.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirProperties.java
new file mode 100644
index 00000000000..5bb8d3cc2f5
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirProperties.java
@@ -0,0 +1,85 @@
+package ca.uhn.fhir.spring.boot.autoconfigure;
+
+import ca.uhn.fhir.context.FhirVersionEnum;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix = "hapi.fhir")
+public class FhirProperties {
+
+ private FhirVersionEnum version = FhirVersionEnum.DSTU2;
+
+ private Server server = new Server();
+
+ private Validation validation = new Validation();
+
+ public FhirVersionEnum getVersion() {
+ return version;
+ }
+
+ public void setVersion(FhirVersionEnum version) {
+ this.version = version;
+ }
+
+ public Server getServer() {
+ return server;
+ }
+
+ public void setServer(Server server) {
+ this.server = server;
+ }
+
+ public Validation getValidation() {
+ return validation;
+ }
+
+ public void setValidation(Validation validation) {
+ this.validation = validation;
+ }
+
+ public static class Server {
+
+ private String url;
+
+ private String path = "/fhir/*";
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+ }
+
+ public static class Validation {
+
+ private boolean enabled = true;
+
+ private boolean requestOnly = true;
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isRequestOnly() {
+ return requestOnly;
+ }
+
+ public void setRequestOnly(boolean requestOnly) {
+ this.requestOnly = requestOnly;
+ }
+ }
+}
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirRestfulServerCustomizer.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirRestfulServerCustomizer.java
new file mode 100644
index 00000000000..3f19d1b58a4
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirRestfulServerCustomizer.java
@@ -0,0 +1,13 @@
+package ca.uhn.fhir.spring.boot.autoconfigure;
+
+import ca.uhn.fhir.rest.server.RestfulServer;
+
+@FunctionalInterface
+public interface FhirRestfulServerCustomizer {
+
+ /**
+ * Customize the server.
+ * @param server the server to customize
+ */
+ void customize(RestfulServer server);
+}
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000000..c761b7cf3e0
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -0,0 +1 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=ca.uhn.fhir.spring.boot.autoconfigure.FhirAutoConfiguration
\ No newline at end of file
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/test/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirAutoConfigurationTest.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/test/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirAutoConfigurationTest.java
new file mode 100644
index 00000000000..3b3023e4eff
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/test/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirAutoConfigurationTest.java
@@ -0,0 +1,171 @@
+package ca.uhn.fhir.spring.boot.autoconfigure;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.context.FhirVersionEnum;
+import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.okhttp.client.OkHttpRestfulClientFactory;
+import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
+import ca.uhn.fhir.rest.server.RestfulServer;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
+import ca.uhn.fhir.spring.boot.autoconfigure.FhirAutoConfiguration.FhirJpaServerConfiguration.Dstu3;
+import org.assertj.core.util.Arrays;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
+import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
+import org.springframework.boot.test.util.EnvironmentTestUtils;
+import org.springframework.boot.web.servlet.ServletRegistrationBean;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link FhirAutoConfiguration}.
+ *
+ * @author Mathieu Ouellet
+ */
+public class FhirAutoConfigurationTest {
+
+ @Rule
+ public final ExpectedException thrown = ExpectedException.none();
+
+ private AnnotationConfigApplicationContext context;
+
+ @After
+ public void close() {
+ if (this.context != null) {
+ this.context.close();
+ }
+ }
+
+ @Test
+ public void withFhirContext() throws Exception {
+ load();
+ assertThat(this.context.getBeansOfType(FhirContext.class)).hasSize(1);
+ }
+
+ @Test
+ public void withFhirVersion() throws Exception {
+ load("hapi.fhir.version:DSTU3");
+ assertThat(this.context.getBean(FhirContext.class).getVersion()).isEqualTo(FhirVersionEnum.DSTU3.getVersionImplementation());
+ }
+
+ @Test
+ public void withRestfulServer() {
+ load("hapi.fhir.server.path:/hapi-fhir/*");
+ assertThat(this.context.getBeansOfType(ServletRegistrationBean.class)).hasSize(1);
+ assertThat(this.context.getBeansOfType(RestfulServer.class)).hasSize(1);
+ assertThat(this.context.getBean(ServletRegistrationBean.class).getUrlMappings()).contains("/hapi-fhir/*");
+ }
+
+ @Test
+ public void withJpaServer() {
+ load(
+ Arrays.array(
+ EmbeddedDataSourceConfiguration.class,
+ HibernateJpaAutoConfiguration.class,
+ PropertyPlaceholderAutoConfiguration.class,
+ FhirAutoConfiguration.class),
+ "hapi.fhir.version:DSTU3",
+ "spring.jpa.properties.hibernate.search.default.indexBase:target/lucenefiles",
+ "spring.jpa.properties.hibernate.search.model_mapping:ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory");
+ assertThat(this.context.getBeansOfType(DaoConfig.class)).hasSize(1);
+ assertThat(this.context.getBeansOfType(Dstu3.class)).hasSize(1);
+ }
+
+ @Test
+ public void withNoValidation() {
+ load("hapi.fhir.validation.enabled:false");
+ this.thrown.expect(NoSuchBeanDefinitionException.class);
+ this.context.getBean(RequestValidatingInterceptor.class);
+ }
+
+ @Test
+ public void withValidation() {
+ load();
+ assertThat(this.context.getBeansOfType(IServerInterceptor.class)).hasSize(1);
+ }
+
+ @Test
+ public void withValidations() {
+ load("hapi.fhir.validation.request-only=false");
+ assertThat(this.context.getBeansOfType(IServerInterceptor.class)).hasSize(2);
+ }
+
+ @Test
+ public void withCustomValidationSchemaLocation() {
+ load("hapi.fhir.validation.schema-location:custom-schema-location");
+ assertThat(this.context.getBeansOfType(IServerInterceptor.class)).hasSize(1);
+ }
+
+ @Test
+ public void withApacheHttpClient() {
+ load(new HidePackagesClassLoader("okhttp3"), "hapi.fhir.server.url:http://localhost:8080");
+ assertThat(this.context.getBeansOfType(ApacheRestfulClientFactory.class)).hasSize(1);
+ assertThat(this.context.getBeansOfType(OkHttpRestfulClientFactory.class)).hasSize(0);
+ }
+
+ @Test
+ public void withOkHttpClient() {
+ load("hapi.fhir.server.url:http://localhost:8080");
+ assertThat(this.context.getBeansOfType(OkHttpRestfulClientFactory.class)).hasSize(1);
+ assertThat(this.context.getBeansOfType(ApacheRestfulClientFactory.class)).hasSize(0);
+ }
+
+ private void load(String... environment) {
+ load(new Class>[] { FhirAutoConfiguration.class }, null, environment);
+ }
+
+ private void load(ClassLoader classLoader, String... environment) {
+ load(new Class>[] { FhirAutoConfiguration.class }, classLoader, environment);
+ }
+
+ private void load(Class>[] configs, String... environment) {
+ load(configs, null, environment);
+ }
+
+ private void load(Class>[] configs, ClassLoader classLoader, String... environment) {
+ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
+ EnvironmentTestUtils.addEnvironment(applicationContext, environment);
+ if (classLoader != null) {
+ applicationContext.setClassLoader(classLoader);
+ }
+ if (configs != null) {
+ applicationContext.register(configs);
+ }
+ applicationContext.refresh();
+ this.context = applicationContext;
+ }
+
+ private static final class HidePackagesClassLoader extends URLClassLoader {
+
+ private final String[] hiddenPackages;
+
+ private HidePackagesClassLoader(String... hiddenPackages) {
+ super(new URL[0], FhirAutoConfigurationTest.class.getClassLoader());
+ this.hiddenPackages = hiddenPackages;
+ }
+
+ @Override
+ protected Class> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException {
+ for (String hiddenPackage : this.hiddenPackages) {
+ if (name.startsWith(hiddenPackage)) {
+ throw new ClassNotFoundException();
+ }
+ }
+ return super.loadClass(name, resolve);
+ }
+
+ }
+
+}
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/test/resources/application.yml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/test/resources/application.yml
new file mode 100644
index 00000000000..f3a8cf9c934
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/test/resources/application.yml
@@ -0,0 +1,13 @@
+spring:
+ jpa:
+ hibernate:
+ ddl-auto: update
+ properties:
+ hibernate.jdbc.batch_size: 20
+ hibernate.cache.use_query_cache: false
+ hibernate.cache.use_second_level_cache: false
+ hibernate.cache.use_structured_entries: false
+ hibernate.cache.use_minimal_puts: false
+ hibernate.search.default.directory_provider: filesystem
+ hibernate.search.default.indexBase: target/lucenefiles
+ hibernate.search.lucene_version: LUCENE_CURRENT
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/test/resources/logback-test.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/test/resources/logback-test.xml
new file mode 100644
index 00000000000..ee274734003
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/test/resources/logback-test.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml
new file mode 100644
index 00000000000..963adda0fcb
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml
@@ -0,0 +1,64 @@
+
+ 4.0.0
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-spring-boot-samples
+ 3.1.0-SNAPSHOT
+
+
+ hapi-fhir-spring-boot-sample-client-apache
+
+ jar
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-spring-boot-starter
+ ${project.version}
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-structures-dstu3
+ ${project.version}
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-client
+ ${project.version}
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/src/main/java/sample/fhir/client/SampleApacheRestfulClientApplication.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/src/main/java/sample/fhir/client/SampleApacheRestfulClientApplication.java
new file mode 100644
index 00000000000..e553eebc646
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/src/main/java/sample/fhir/client/SampleApacheRestfulClientApplication.java
@@ -0,0 +1,37 @@
+package sample.fhir.client;
+
+import ca.uhn.fhir.rest.client.api.IGenericClient;
+import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
+import org.hl7.fhir.dstu3.model.CapabilityStatement;
+
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+
+@SpringBootApplication
+public class SampleApacheRestfulClientApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SampleApacheRestfulClientApplication.class, args);
+ }
+
+ @Bean
+ public LoggingInterceptor loggingInterceptor() {
+ return new LoggingInterceptor(true);
+ }
+
+ @Bean
+ public CommandLineRunner runner(final IGenericClient fhirClient) {
+ return new CommandLineRunner() {
+
+ @Override
+ public void run(String... args) throws Exception {
+ fhirClient.capabilities()
+ .ofType(CapabilityStatement.class)
+ .execute();
+ }
+ };
+ }
+
+}
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/src/main/resources/application.yml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/src/main/resources/application.yml
new file mode 100644
index 00000000000..33eb38f1d70
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/src/main/resources/application.yml
@@ -0,0 +1,13 @@
+server:
+ port: 8888
+hapi:
+ fhir:
+ version: dstu3
+ server:
+ url: http://localhost:8080/fhir
+management:
+ security:
+ enabled: false
+logging:
+ level:
+ ca.uhn.fhir.jaxrs: debug
\ No newline at end of file
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml
new file mode 100644
index 00000000000..69cc6e9f6b1
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml
@@ -0,0 +1,64 @@
+
+ 4.0.0
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-spring-boot-samples
+ 3.1.0-SNAPSHOT
+
+
+ hapi-fhir-spring-boot-sample-client-okhttp
+
+ jar
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-spring-boot-starter
+ ${project.version}
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-structures-dstu3
+ ${project.version}
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-client-okhttp
+ ${project.version}
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/src/main/java/sample/fhir/client/SampleOkHttpRestfulClientApplication.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/src/main/java/sample/fhir/client/SampleOkHttpRestfulClientApplication.java
new file mode 100644
index 00000000000..6286d41b824
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/src/main/java/sample/fhir/client/SampleOkHttpRestfulClientApplication.java
@@ -0,0 +1,37 @@
+package sample.fhir.client;
+
+import ca.uhn.fhir.rest.client.api.IGenericClient;
+import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
+import org.hl7.fhir.dstu3.model.CapabilityStatement;
+
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+
+@SpringBootApplication
+public class SampleOkHttpRestfulClientApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SampleOkHttpRestfulClientApplication.class, args);
+ }
+
+ @Bean
+ public LoggingInterceptor loggingInterceptor() {
+ return new LoggingInterceptor(true);
+ }
+
+ @Bean
+ public CommandLineRunner runner(final IGenericClient fhirClient) {
+ return new CommandLineRunner() {
+
+ @Override
+ public void run(String... args) throws Exception {
+ fhirClient.capabilities()
+ .ofType(CapabilityStatement.class)
+ .execute();
+ }
+ };
+ }
+
+}
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/src/main/resources/application.yml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/src/main/resources/application.yml
new file mode 100644
index 00000000000..33eb38f1d70
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/src/main/resources/application.yml
@@ -0,0 +1,13 @@
+server:
+ port: 8888
+hapi:
+ fhir:
+ version: dstu3
+ server:
+ url: http://localhost:8080/fhir
+management:
+ security:
+ enabled: false
+logging:
+ level:
+ ca.uhn.fhir.jaxrs: debug
\ No newline at end of file
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml
new file mode 100644
index 00000000000..439a5ce9252
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml
@@ -0,0 +1,67 @@
+
+ 4.0.0
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-spring-boot-samples
+ 3.1.0-SNAPSHOT
+
+
+ hapi-fhir-spring-boot-sample-server-jersey
+
+ jar
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-starter-jersey
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-spring-boot-starter
+ ${project.version}
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-jaxrsserver-base
+ ${project.version}
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-validation-resources-dstu3
+ ${project.version}
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/java/sample/fhir/server/jersey/SampleJerseyRestfulServerApplication.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/java/sample/fhir/server/jersey/SampleJerseyRestfulServerApplication.java
new file mode 100644
index 00000000000..76b50ec7101
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/java/sample/fhir/server/jersey/SampleJerseyRestfulServerApplication.java
@@ -0,0 +1,20 @@
+package sample.fhir.server.jersey;
+
+import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+
+@SpringBootApplication
+public class SampleJerseyRestfulServerApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SampleJerseyRestfulServerApplication.class, args);
+ }
+
+ @Bean
+ public LoggingInterceptor loggingInterceptor() {
+ return new LoggingInterceptor();
+ }
+}
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/java/sample/fhir/server/jersey/provider/PatientResourceProvider.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/java/sample/fhir/server/jersey/provider/PatientResourceProvider.java
new file mode 100644
index 00000000000..9f12b953a26
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/java/sample/fhir/server/jersey/provider/PatientResourceProvider.java
@@ -0,0 +1,73 @@
+package sample.fhir.server.jersey.provider;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
+import ca.uhn.fhir.rest.annotation.Create;
+import ca.uhn.fhir.rest.annotation.IdParam;
+import ca.uhn.fhir.rest.annotation.Read;
+import ca.uhn.fhir.rest.annotation.ResourceParam;
+import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
+import org.hl7.fhir.dstu3.model.HumanName;
+import org.hl7.fhir.dstu3.model.IdType;
+import org.hl7.fhir.dstu3.model.Patient;
+
+import org.springframework.stereotype.Component;
+
+@Component
+public class PatientResourceProvider extends AbstractJaxRsResourceProvider {
+
+ private static Long counter = 1L;
+
+ private static final ConcurrentHashMap patients = new ConcurrentHashMap<>();
+
+ static {
+ patients.put(String.valueOf(counter), createPatient("Van Houte"));
+ patients.put(String.valueOf(counter), createPatient("Agnew"));
+ for (int i = 0; i < 20; i++) {
+ patients.put(String.valueOf(counter), createPatient("Random Patient " + counter));
+ }
+ }
+
+ public PatientResourceProvider(FhirContext fhirContext) {
+ super(fhirContext);
+ }
+
+ @Read
+ public Patient find(@IdParam final IdType theId) {
+ if (patients.containsKey(theId.getIdPart())) {
+ return patients.get(theId.getIdPart());
+ } else {
+ throw new ResourceNotFoundException(theId);
+ }
+ }
+
+ @Create
+ public MethodOutcome createPatient(@ResourceParam Patient patient) {
+
+ patient.setId(createId(counter, 1L));
+ patients.put(String.valueOf(counter), patient);
+
+ return new MethodOutcome(patient.getIdElement());
+ }
+
+ @Override
+ public Class getResourceType() {
+ return Patient.class;
+ }
+
+ private static IdType createId(final Long id, final Long theVersionId) {
+ return new IdType("Patient", "" + id, "" + theVersionId);
+ }
+
+ private static Patient createPatient(final String name) {
+ final Patient patient = new Patient();
+ patient.getName().add(new HumanName().setFamily(name));
+ patient.setId(createId(counter, 1L));
+ counter++;
+ return patient;
+ }
+
+}
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/resources/application.yml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/resources/application.yml
new file mode 100644
index 00000000000..325409646b7
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/resources/application.yml
@@ -0,0 +1,21 @@
+hapi:
+ fhir:
+ version: dstu3
+ server:
+ path: /fhir/*
+ rest:
+ server-name: hapi-fhir-spring-boot-sample-server-jersey
+ server-version: 1.0.0
+ implementation-description: Spring Boot Jersey Server Sample
+ default-response-encoding: json
+ e-tag-support: enabled
+ default-pretty-print: true
+ validation:
+ enabled: true
+ request-only: true
+management:
+ security:
+ enabled: false
+logging:
+ level:
+ ca.uhn.fhir.jaxrs: debug
\ No newline at end of file
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/test/java/sample/fhir/server/jersey/SampleJerseyRestfulServerApplicationTest.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/test/java/sample/fhir/server/jersey/SampleJerseyRestfulServerApplicationTest.java
new file mode 100644
index 00000000000..59f41dea74b
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/test/java/sample/fhir/server/jersey/SampleJerseyRestfulServerApplicationTest.java
@@ -0,0 +1,41 @@
+package sample.fhir.server.jersey;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+public class SampleJerseyRestfulServerApplicationTest {
+
+ @Autowired
+ private TestRestTemplate restTemplate;
+
+ @Test
+ public void metadata() {
+ ResponseEntity entity = this.restTemplate.getForEntity(
+ "/fhir/metadata",
+ String.class);
+ assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
+ assertThat(entity.getBody()).contains("\"status\": \"active\"");
+ }
+
+ @Test
+ public void patientResource() {
+ ResponseEntity entity = this.restTemplate.getForEntity(
+ "/fhir/Patient/1",
+ String.class);
+ assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
+ assertThat(entity.getBody()).contains("\"family\": \"Van Houte\"");
+ }
+
+}
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml
new file mode 100644
index 00000000000..a7ecaf84aa2
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml
@@ -0,0 +1,75 @@
+
+ 4.0.0
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-spring-boot-samples
+ 3.1.0-SNAPSHOT
+
+
+ hapi-fhir-spring-boot-sample-server-jpa
+
+ jar
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-starter-jersey
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-spring-boot-starter
+ ${project.version}
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-jpaserver-base
+ ${project.version}
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-jaxrsserver-base
+ ${project.version}
+
+
+ com.h2database
+ h2
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/src/main/java/sample/fhir/server/jpa/SampleJpaRestfulServerApplication.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/src/main/java/sample/fhir/server/jpa/SampleJpaRestfulServerApplication.java
new file mode 100644
index 00000000000..721e4b8d3a1
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/src/main/java/sample/fhir/server/jpa/SampleJpaRestfulServerApplication.java
@@ -0,0 +1,12 @@
+package sample.fhir.server.jpa;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SampleJpaRestfulServerApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SampleJpaRestfulServerApplication.class, args);
+ }
+}
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/src/main/resources/application.yml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/src/main/resources/application.yml
new file mode 100644
index 00000000000..2c2f591eb69
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/src/main/resources/application.yml
@@ -0,0 +1,39 @@
+spring:
+ jpa:
+ hibernate:
+ ddl-auto: create-drop
+ properties:
+ hibernate.jdbc.batch_size: 20
+ hibernate.cache.use_query_cache: false
+ hibernate.cache.use_second_level_cache: false
+ hibernate.cache.use_structured_entries: false
+ hibernate.cache.use_minimal_puts: false
+ hibernate.search.default.directory_provider: filesystem
+ hibernate.search.default.indexBase: target/lucenefiles
+ hibernate.search.lucene_version: LUCENE_CURRENT
+ hibernate.search.model_mapping: ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory
+
+ h2:
+ console:
+ enabled: true
+hapi:
+ fhir:
+ version: dstu3
+ server:
+ path: /fhir/*
+ rest:
+ server-name: hapi-fhir-spring-boot-sample-server-jpa
+ server-version: 1.0.0
+ implementation-description: Spring Boot Jpa Server Sample
+ default-response-encoding: json
+ e-tag-support: enabled
+ default-pretty-print: true
+ validation:
+ enabled: true
+ request-only: true
+ jpa:
+ scheduling-disabled: true
+ subscription-enabled: false
+management:
+ security:
+ enabled: false
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/src/test/java/sample/fhir/server/jpa/SampleJpaRestfulServerApplicationTest.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/src/test/java/sample/fhir/server/jpa/SampleJpaRestfulServerApplicationTest.java
new file mode 100644
index 00000000000..bc36ba6eb25
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/src/test/java/sample/fhir/server/jpa/SampleJpaRestfulServerApplicationTest.java
@@ -0,0 +1,41 @@
+package sample.fhir.server.jpa;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.rest.client.api.IGenericClient;
+import org.hl7.fhir.dstu3.model.Patient;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.embedded.LocalServerPort;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+public class SampleJpaRestfulServerApplicationTest {
+
+ @Autowired
+ FhirContext fhirContext;
+
+ @LocalServerPort
+ int port;
+
+ @Test
+ public void createAndRead() {
+ IGenericClient client = fhirContext.newRestfulGenericClient("http://localhost:" + port + "/fhir");
+
+ Patient patient = new Patient();
+ patient.addName().setFamily("Test");
+ IIdType id = client.create().resource(patient).execute().getId();
+
+ System.out.println(id);
+
+ Patient result = client.read().resource(Patient.class).withId(id).execute();
+
+ System.out.println(result);
+ }
+
+}
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml
new file mode 100644
index 00000000000..29511655859
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml
@@ -0,0 +1,21 @@
+
+ 4.0.0
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-spring-boot
+ 3.1.0-SNAPSHOT
+
+
+ hapi-fhir-spring-boot-samples
+ pom
+
+
+ hapi-fhir-spring-boot-sample-client-apache
+ hapi-fhir-spring-boot-sample-client-okhttp
+ hapi-fhir-spring-boot-sample-server-jersey
+ hapi-fhir-spring-boot-sample-server-jpa
+
+
+
\ No newline at end of file
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml
new file mode 100644
index 00000000000..094a37377e3
--- /dev/null
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml
@@ -0,0 +1,27 @@
+
+ 4.0.0
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-spring-boot
+ 3.1.0-SNAPSHOT
+
+
+ hapi-fhir-spring-boot-starter
+
+ jar
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-spring-boot-autoconfigure
+ ${project.version}
+
+
+
+
\ No newline at end of file
diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml
new file mode 100644
index 00000000000..a266e1653ed
--- /dev/null
+++ b/hapi-fhir-spring-boot/pom.xml
@@ -0,0 +1,21 @@
+
+ 4.0.0
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir
+ 3.1.0-SNAPSHOT
+ ../pom.xml
+
+
+ hapi-fhir-spring-boot
+ pom
+
+
+ hapi-fhir-spring-boot-autoconfigure
+ hapi-fhir-spring-boot-starter
+ hapi-fhir-spring-boot-samples
+
+
+
\ No newline at end of file
diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml
index f303ccd1785..d796ef60380 100644
--- a/hapi-fhir-structures-dstu3/pom.xml
+++ b/hapi-fhir-structures-dstu3/pom.xml
@@ -70,13 +70,6 @@
true
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- true
-
-
diff --git a/pom.xml b/pom.xml
index 6c50a8277a6..aa9b1c4c5e3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -370,6 +370,14 @@
malcolmm83
Malcolm McRoberts
+
+ mouellet
+ Mathieu Ouellet
+
+
+ JiajingLiang
+ Jiajing Liang
+
@@ -403,6 +411,7 @@
2.7.1
4.4.11
5.0.0.RELEASE
+ 1.5.6.RELEASE
3.0.7.RELEASE
@@ -970,6 +979,13 @@
spring-websocket
${spring_version}
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
org.thymeleaf
thymeleaf
@@ -1032,7 +1048,7 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.6.2
+ 3.7.0
1.7
@@ -1045,6 +1061,10 @@
1.8
true
UTF-8
+
+ true
+ 128m
+ 1600m
@@ -1880,6 +1900,7 @@
example-projects/hapi-fhir-standalone-overlay-example
hapi-fhir-jacoco
hapi-fhir-igpacks
+ hapi-fhir-spring-boot
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 65d74758394..21cee568c1f 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -154,7 +154,7 @@
JAX-RS client framework now supports the ability to
register your own JAX-RS Component Classes against the client,
as well as better documentation about thread safety. Thanks
- to Sébastien Rivière for the pull request!
+ to Sébastien Rivière for the pull request!
Processing of the If-Modified-Since header on FHIR read operations was reversed,
@@ -195,6 +195,23 @@
explicitly listed, e.g. _include=Bundle.total]]>.
Thanks to @parisni for reporting!
+
+ Add support for Spring Boot for initializing a number of parts of the library,
+ as well as several examples.
+ Thanks to Mathieu Ouellet for the contribution!
+
+
+ JPA server now has lucene index support moved to separate classes from the entity
+ classes in order to facilitate support for ElasticSearch. Thanks to Jiang Liang
+ for the pull request!
+
+
+ A new client interceptor has been added called
+ AdditionalRequestHeadersInterceptor, which allows
+ a developer to add additional custom headers to a
+ client requests.
+ Thanks to Clayton Bodendein for the pull request!
+
@@ -560,7 +577,7 @@ Bundle bundle = client.search().forResource(Patient.class)
Correct an issue in the validator (DSTU3/R4) where elements were not always
correctly validated if the element contained only a profiled extension. Thanks
- to Sébastien Rivière for the pull request!
+ to Sébastien Rivière for the pull request!
Testing UI now has a dropdown for modifiers on token search. Thanks
@@ -574,7 +591,7 @@ Bundle bundle = client.search().forResource(Patient.class)
Extensions on ID datatypes were not parsed or serialized correctly. Thanks to
- Stephen Rivière for the pull request!
+ Stephen Rivière for the pull request!
Fix a bug in REST Hook Subscription interceptors which prevented subscriptions
@@ -895,7 +912,7 @@ Bundle bundle = client.search().forResource(Patient.class)
DaoConfig#setAllowInlineMatchUrlReferences() now defaults to
true]]> since inline conditional references
- are now a part of the FHIR specification. Thanks to Jan Dědek for
+ are now a part of the FHIR specification. Thanks to Jan DÄdek for
pointing this out!
@@ -1875,7 +1892,7 @@ Bundle bundle = client.search().forResource(Patient.class)
Parser failed to parse resources containing an extension with a value type of
- "id". Thanks to Raphael Mäder for reporting!
+ "id". Thanks to Raphael Mäder for reporting!
When committing a transaction in JPA server
@@ -2137,7 +2154,7 @@ Bundle bundle = client.search().forResource(Patient.class)
array instead of a string. Thanks to David Hay for reporting!
- Sébastien Rivière contributed an excellent pull request which adds a
+ Sébastien Rivière contributed an excellent pull request which adds a
number of enhancements to JAX-RS module: