Skip to content

Commit 9576cfa

Browse files
authored
Feature/elastic on boot (#856)
* Fixing up elastic for Spring Boot * Adding class shadowing for issue reported on hapifhir/hapi-fhir#7242 * Formatting * corrected condition * fix import * fix2 * crappy fix3 * fix actuator endpoint * Simplified expression * Ironed out a few legacy issues * more rework * major overhaul * Disabling invalid test * Reverting back to defaults for text searches * Added default hibernate settings from the EnvironmentHelper * Formatting * Added comment on class shadow * Added missing default
1 parent 4265137 commit 9576cfa

39 files changed

+556
-535
lines changed

pom.xml

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<properties>
77
<java.version>17</java.version>
8-
<hapi.fhir.jpa.server.starter.revision>2</hapi.fhir.jpa.server.starter.revision>
8+
<hapi.fhir.jpa.server.starter.revision>3</hapi.fhir.jpa.server.starter.revision>
99
<clinical-reasoning.version>3.26.0</clinical-reasoning.version>
1010
</properties>
1111

@@ -60,6 +60,16 @@
6060

6161
<dependencies>
6262

63+
<dependency>
64+
<groupId>org.springframework.boot</groupId>
65+
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
66+
<version>${spring_boot_version}</version>
67+
</dependency>
68+
<dependency>
69+
<groupId>co.elastic.clients</groupId>
70+
<artifactId>elasticsearch-java</artifactId>
71+
</dependency>
72+
6373
<dependency>
6474
<groupId>org.postgresql</groupId>
6575
<artifactId>postgresql</artifactId>
@@ -265,12 +275,7 @@
265275
<artifactId>moment</artifactId>
266276
</dependency>
267277

268-
<!-- The following dependencies are only needed for automated unit tests, you do not neccesarily need them to run the example. -->
269-
<dependency>
270-
<groupId>co.elastic.clients</groupId>
271-
<artifactId>elasticsearch-java</artifactId>
272-
<scope>test</scope>
273-
</dependency>
278+
274279

275280
<dependency>
276281
<groupId>ca.uhn.hapi.fhir</groupId>
@@ -386,17 +391,15 @@
386391
<dependency>
387392
<groupId>org.springframework.ai</groupId>
388393
<artifactId>spring-ai-mcp</artifactId>
389-
<version>1.0.2</version>
394+
<version>1.1.0-M1</version>
390395
</dependency>
391-
<!--
392-
This will be included as well as using Spring Automatic Configuration
393-
once spring-ai and io.modelcontextprotocol.sdk are on par
394-
-->
395-
<!--<dependency>
396+
397+
<!--implementation("org.springframework.ai:spring-ai-starter-mcp-server-webmvc:1.1.0-M1")-->
398+
<dependency>
396399
<groupId>org.springframework.ai</groupId>
397400
<artifactId>spring-ai-starter-mcp-server</artifactId>
398-
<version>1.0.2</version>
399-
</dependency>-->
401+
<version>1.1.0-M1</version>
402+
</dependency>
400403

401404
<dependency>
402405
<groupId>io.modelcontextprotocol.sdk</groupId>
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* #%L
3+
* HAPI FHIR JPA Server
4+
* %%
5+
* Copyright (C) 2014 - 2025 Smile CDR, Inc.
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package ca.uhn.fhir.jpa.provider;
21+
22+
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoObservation;
23+
import ca.uhn.fhir.jpa.model.util.JpaConstants;
24+
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
25+
import ca.uhn.fhir.model.api.annotation.Description;
26+
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
27+
import ca.uhn.fhir.rest.annotation.Operation;
28+
import ca.uhn.fhir.rest.annotation.OperationParam;
29+
import ca.uhn.fhir.rest.annotation.RawParam;
30+
import ca.uhn.fhir.rest.api.Constants;
31+
import ca.uhn.fhir.rest.api.server.IBundleProvider;
32+
import ca.uhn.fhir.rest.param.DateAndListParam;
33+
import ca.uhn.fhir.rest.param.ReferenceAndListParam;
34+
import ca.uhn.fhir.rest.param.TokenAndListParam;
35+
import org.hl7.fhir.instance.model.api.IBaseResource;
36+
import org.hl7.fhir.instance.model.api.IPrimitiveType;
37+
38+
import java.util.List;
39+
import java.util.Map;
40+
41+
// Can be removed when https://github.com/hapifhir/hapi-fhir/issues/7255 is resolved
42+
public abstract class BaseJpaResourceProviderObservation<T extends IBaseResource> extends BaseJpaResourceProvider<T> {
43+
44+
/**
45+
* Observation/$lastn
46+
*/
47+
@Operation(name = JpaConstants.OPERATION_LASTN, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET)
48+
public IBundleProvider observationLastN(
49+
jakarta.servlet.http.HttpServletRequest theServletRequest,
50+
jakarta.servlet.http.HttpServletResponse theServletResponse,
51+
ca.uhn.fhir.rest.api.server.RequestDetails theRequestDetails,
52+
@Description(
53+
formalDefinition =
54+
"Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
55+
@OperationParam(name = Constants.PARAM_COUNT, typeName = "unsignedInt")
56+
IPrimitiveType<Integer> theCount,
57+
@Description(shortDefinition = "The classification of the type of observation")
58+
@OperationParam(name = "category")
59+
TokenAndListParam theCategory,
60+
@Description(shortDefinition = "The code of the observation type") @OperationParam(name = "code")
61+
TokenAndListParam theCode,
62+
@Description(shortDefinition = "The effective date of the observation") @OperationParam(name = "date")
63+
DateAndListParam theDate,
64+
@Description(shortDefinition = "The subject that the observation is about (if patient)")
65+
@OperationParam(name = "patient")
66+
ReferenceAndListParam thePatient,
67+
@Description(shortDefinition = "The subject that the observation is about")
68+
@OperationParam(name = "subject")
69+
ReferenceAndListParam theSubject,
70+
@Description(shortDefinition = "The maximum number of observations to return for each observation code")
71+
@OperationParam(name = "max", typeName = "integer", min = 0, max = 1)
72+
IPrimitiveType<Integer> theMax,
73+
@RawParam Map<String, List<String>> theAdditionalRawParams) {
74+
startRequest(theServletRequest);
75+
try {
76+
SearchParameterMap paramMap = new SearchParameterMap();
77+
paramMap.add(org.hl7.fhir.r4.model.Observation.SP_CATEGORY, theCategory);
78+
paramMap.add(org.hl7.fhir.r4.model.Observation.SP_CODE, theCode);
79+
paramMap.add(org.hl7.fhir.r4.model.Observation.SP_DATE, theDate);
80+
if (thePatient != null) {
81+
paramMap.add(org.hl7.fhir.r4.model.Observation.SP_PATIENT, thePatient);
82+
}
83+
if (theSubject != null) {
84+
paramMap.add(org.hl7.fhir.r4.model.Observation.SP_SUBJECT, theSubject);
85+
}
86+
if (theMax != null) {
87+
paramMap.setLastNMax(theMax.getValue());
88+
89+
/**
90+
* The removal of the original raw parameter is required as every implementing class
91+
* has the "Observation" resource class defined. For this resource, the max parameter
92+
* is not supported and thus has to be removed before the use of "translateRawParameters".
93+
*/
94+
if (theAdditionalRawParams != null) theAdditionalRawParams.remove("max");
95+
}
96+
if (theCount != null) {
97+
paramMap.setCount(theCount.getValue());
98+
}
99+
100+
getDao().translateRawParameters(theAdditionalRawParams, paramMap);
101+
102+
return ((IFhirResourceDaoObservation<?>) getDao())
103+
.observationsLastN(paramMap, theRequestDetails, theServletResponse);
104+
} finally {
105+
endRequest(theServletRequest);
106+
}
107+
}
108+
}

src/main/java/ca/uhn/fhir/jpa/starter/Application.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
1717
import org.springframework.boot.SpringApplication;
1818
import org.springframework.boot.autoconfigure.SpringBootApplication;
19-
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration;
2019
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration;
2120
import org.springframework.boot.web.servlet.ServletComponentScan;
2221
import org.springframework.boot.web.servlet.ServletRegistrationBean;
@@ -26,7 +25,7 @@
2625
import org.springframework.context.annotation.Import;
2726

2827
@ServletComponentScan(basePackageClasses = {RestfulServer.class})
29-
@SpringBootApplication(exclude = {ElasticsearchRestClientAutoConfiguration.class, ThymeleafAutoConfiguration.class})
28+
@SpringBootApplication(exclude = {ThymeleafAutoConfiguration.class})
3029
@Import({
3130
StarterCrR4Config.class,
3231
StarterCrDstu3Config.class,

src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnImplementationGuidesPresent.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package ca.uhn.fhir.jpa.starter.annotations;
22

33
import ca.uhn.fhir.jpa.starter.AppProperties;
4-
import org.springframework.boot.context.properties.bind.Binder;
4+
import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper;
55
import org.springframework.context.annotation.Condition;
66
import org.springframework.context.annotation.ConditionContext;
77
import org.springframework.core.type.AnnotatedTypeMetadata;
@@ -10,9 +10,7 @@ public class OnImplementationGuidesPresent implements Condition {
1010
@Override
1111
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
1212

13-
AppProperties config = Binder.get(conditionContext.getEnvironment())
14-
.bind("hapi.fhir", AppProperties.class)
15-
.orElse(null);
13+
AppProperties config = EnvironmentHelper.getConfiguration(conditionContext, "hapi.fhir", AppProperties.class);
1614
if (config == null) return false;
1715
if (config.getImplementationGuides() == null) return false;
1816
return !config.getImplementationGuides().isEmpty();

src/main/java/ca/uhn/fhir/jpa/starter/common/ElasticsearchConfig.java

Lines changed: 0 additions & 31 deletions
This file was deleted.

src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import ca.uhn.fhir.jpa.model.config.SubscriptionSettings;
1010
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
1111
import ca.uhn.fhir.jpa.starter.AppProperties;
12+
import ca.uhn.fhir.jpa.starter.elastic.ElasticsearchBootSvcImpl;
1213
import ca.uhn.fhir.jpa.starter.util.JpaHibernatePropertiesProvider;
1314
import ca.uhn.fhir.jpa.subscription.match.deliver.email.EmailSenderImpl;
1415
import ca.uhn.fhir.jpa.subscription.match.deliver.email.IEmailSender;
@@ -19,10 +20,7 @@
1920
import org.slf4j.Logger;
2021
import org.slf4j.LoggerFactory;
2122
import org.springframework.boot.env.YamlPropertySourceLoader;
22-
import org.springframework.context.annotation.Bean;
23-
import org.springframework.context.annotation.Configuration;
24-
import org.springframework.context.annotation.Lazy;
25-
import org.springframework.context.annotation.Primary;
23+
import org.springframework.context.annotation.*;
2624
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
2725
import org.springframework.transaction.annotation.EnableTransactionManagement;
2826

@@ -36,6 +34,7 @@
3634
*/
3735
@Configuration
3836
@EnableTransactionManagement
37+
@Import(ElasticsearchBootSvcImpl.class)
3938
public class FhirServerConfigCommon {
4039

4140
private static final Logger ourLog = LoggerFactory.getLogger(FhirServerConfigCommon.class);
@@ -274,7 +273,15 @@ public JpaStorageSettings jpaStorageSettings(AppProperties appProperties) {
274273
ourLog.debug("Server configured to Store Meta Source: {}", appProperties.getStore_meta_source_information());
275274
jpaStorageSettings.setStoreMetaSourceInformation(appProperties.getStore_meta_source_information());
276275

277-
storageSettings(appProperties, jpaStorageSettings);
276+
jpaStorageSettings.setAllowContainsSearches(appProperties.getAllow_contains_searches());
277+
jpaStorageSettings.setAllowExternalReferences(appProperties.getAllow_external_references());
278+
jpaStorageSettings.setDefaultSearchParamsCanBeOverridden(
279+
appProperties.getAllow_override_default_search_params());
280+
281+
jpaStorageSettings.setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level());
282+
283+
jpaStorageSettings.setIndexOnContainedResources(appProperties.getEnable_index_contained_resource());
284+
jpaStorageSettings.setIndexIdentifierOfType(appProperties.getEnable_index_of_type());
278285
return jpaStorageSettings;
279286
}
280287

@@ -332,19 +339,6 @@ public HibernatePropertiesProvider jpaStarterDialectProvider(
332339
return new JpaHibernatePropertiesProvider(myEntityManagerFactory);
333340
}
334341

335-
protected StorageSettings storageSettings(AppProperties appProperties, JpaStorageSettings jpaStorageSettings) {
336-
jpaStorageSettings.setAllowContainsSearches(appProperties.getAllow_contains_searches());
337-
jpaStorageSettings.setAllowExternalReferences(appProperties.getAllow_external_references());
338-
jpaStorageSettings.setDefaultSearchParamsCanBeOverridden(
339-
appProperties.getAllow_override_default_search_params());
340-
341-
jpaStorageSettings.setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level());
342-
343-
jpaStorageSettings.setIndexOnContainedResources(appProperties.getEnable_index_contained_resource());
344-
jpaStorageSettings.setIndexIdentifierOfType(appProperties.getEnable_index_of_type());
345-
return jpaStorageSettings;
346-
}
347-
348342
@Lazy
349343
@Bean
350344
public IBinaryStorageSvc binaryStorageSvc(AppProperties appProperties) {

src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu3.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99

1010
@Configuration
1111
@Conditional(OnDSTU3Condition.class)
12-
@Import({JpaDstu3Config.class, StarterJpaConfig.class, StarterCrDstu3Config.class, ElasticsearchConfig.class})
12+
@Import({JpaDstu3Config.class, StarterJpaConfig.class, StarterCrDstu3Config.class})
1313
public class FhirServerConfigDstu3 {}

src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,5 @@
1010

1111
@Configuration
1212
@Conditional(OnR4Condition.class)
13-
@Import({
14-
JpaR4Config.class,
15-
StarterJpaConfig.class,
16-
StarterCrR4Config.class,
17-
ElasticsearchConfig.class,
18-
StarterIpsConfig.class
19-
})
13+
@Import({JpaR4Config.class, StarterJpaConfig.class, StarterCrR4Config.class, StarterIpsConfig.class})
2014
public class FhirServerConfigR4 {}

src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4B.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99

1010
@Configuration
1111
@Conditional(OnR4BCondition.class)
12-
@Import({JpaR4BConfig.class, SubscriptionTopicConfig.class, StarterJpaConfig.class, ElasticsearchConfig.class})
12+
@Import({JpaR4BConfig.class, SubscriptionTopicConfig.class, StarterJpaConfig.class})
1313
public class FhirServerConfigR4B {}

src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR5.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99

1010
@Configuration
1111
@Conditional(OnR5Condition.class)
12-
@Import({StarterJpaConfig.class, JpaR5Config.class, SubscriptionTopicConfig.class, ElasticsearchConfig.class})
12+
@Import({StarterJpaConfig.class, JpaR5Config.class, SubscriptionTopicConfig.class})
1313
public class FhirServerConfigR5 {}

0 commit comments

Comments
 (0)