Add Spring Boot Starter

Provides spring-boot-starter with auto-configurations for Rest server,
Jpa server, FHIR Validation and clients.

Default configurations overridable by configuration properties and 
rest-server customizer.
This commit is contained in:
Mathieu Ouellet 2017-08-22 06:57:00 -04:00
parent 56a71f9222
commit 30c391db00
28 changed files with 1386 additions and 0 deletions

2
.gitignore vendored
View File

@ -141,6 +141,8 @@ local.properties
**/.target **/.target
**/.project **/.project
**/.classpath **/.classpath
**/.factorypath
**/.springBeans
# PDT-specific # PDT-specific

View File

@ -0,0 +1,118 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-autoconfigure</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Compile dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- Optional dependencies -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client-okhttp</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<optional>true</optional>
</dependency>
<!-- @ConfigurationProperties annotation processing (metadata for IDEs) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2.1</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -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<IResourceProvider> resourceProviders;
private final IPagingProvider pagingProvider;
private final List<IServerInterceptor> interceptors;
private final List<FhirRestfulServerCustomizer> customizers;
public FhirRestfulServerConfiguration(
FhirProperties properties,
FhirContext fhirContext,
ObjectProvider<List<IResourceProvider>> resourceProviders,
ObjectProvider<IPagingProvider> pagingProvider,
ObjectProvider<List<IServerInterceptor>> interceptors,
ObjectProvider<List<FhirRestfulServerCustomizer>> 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<BaseJpaSystemProvider> 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<IClientInterceptor> clientInterceptors;
public FhirRestfulClientConfiguration(FhirProperties properties, ObjectProvider<List<IClientInterceptor>> 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;
}
}
}
}

View File

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

View File

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

View File

@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=ca.uhn.fhir.spring.boot.autoconfigure.FhirAutoConfiguration

View File

@ -0,0 +1,170 @@
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");
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);
}
}
}

View File

@ -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

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
</configuration>

View File

@ -0,0 +1,64 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-starter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Optional -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -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();
}
};
}
}

View File

@ -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

View File

@ -0,0 +1,64 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-client-okhttp</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-starter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client-okhttp</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Optional -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -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();
}
};
}
}

View File

@ -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

View File

@ -0,0 +1,67 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-server-jersey</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-starter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Optional -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -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();
}
}

View File

@ -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<Patient> {
private static Long counter = 1L;
private static final ConcurrentHashMap<String, Patient> 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<Patient> 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;
}
}

View File

@ -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

View File

@ -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<String> 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<String> entity = this.restTemplate.getForEntity(
"/fhir/Patient/1",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"family\": \"Van Houte\"");
}
}

View File

@ -0,0 +1,75 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-server-jpa</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-starter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<!-- Optional -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

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

View File

@ -0,0 +1,37 @@
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
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

View File

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

View File

@ -0,0 +1,21 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<packaging>pom</packaging>
<modules>
<module>hapi-fhir-spring-boot-sample-client-apache</module>
<module>hapi-fhir-spring-boot-sample-client-okhttp</module>
<module>hapi-fhir-spring-boot-sample-server-jersey</module>
<module>hapi-fhir-spring-boot-sample-server-jpa</module>
</modules>
</project>

View File

@ -0,0 +1,27 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-starter</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-autoconfigure</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,21 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-spring-boot</artifactId>
<packaging>pom</packaging>
<modules>
<module>hapi-fhir-spring-boot-autoconfigure</module>
<module>hapi-fhir-spring-boot-starter</module>
<module>hapi-fhir-spring-boot-samples</module>
</modules>
</project>

View File

@ -396,6 +396,7 @@
<phloc_schematron_version>2.7.1</phloc_schematron_version> <phloc_schematron_version>2.7.1</phloc_schematron_version>
<phloc_commons_version>4.4.11</phloc_commons_version> <phloc_commons_version>4.4.11</phloc_commons_version>
<spring_version>4.3.10.RELEASE</spring_version> <spring_version>4.3.10.RELEASE</spring_version>
<spring-boot.version>1.5.6.RELEASE</spring-boot.version>
<thymeleaf-version>3.0.7.RELEASE</thymeleaf-version> <thymeleaf-version>3.0.7.RELEASE</thymeleaf-version>
<!-- We are aiming to still work on a very old version of SLF4j even though we depend on the newest, just to be nice to users of the API. This version is tested in the hapi-fhir-cobertura. --> <!-- We are aiming to still work on a very old version of SLF4j even though we depend on the newest, just to be nice to users of the API. This version is tested in the hapi-fhir-cobertura. -->
@ -963,6 +964,13 @@
<artifactId>spring-websocket</artifactId> <artifactId>spring-websocket</artifactId>
<version>${spring_version}</version> <version>${spring_version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency> <dependency>
<groupId>org.thymeleaf</groupId> <groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId> <artifactId>thymeleaf</artifactId>
@ -1875,6 +1883,7 @@
<module>example-projects/hapi-fhir-standalone-overlay-example</module> <module>example-projects/hapi-fhir-standalone-overlay-example</module>
<module>hapi-fhir-jacoco</module> <module>hapi-fhir-jacoco</module>
<module>hapi-fhir-igpacks</module> <module>hapi-fhir-igpacks</module>
<module>hapi-fhir-spring-boot</module>
<!--<module>hapi-fhir-osgi-core</module>--> <!--<module>hapi-fhir-osgi-core</module>-->
</modules> </modules>
</profile> </profile>