Skip to content

Commit af5bc23

Browse files
authored
Merge pull request #2970 from IBM/issue-2969
Add support for looking up and skipping provider during service loader discovery
2 parents f81a953 + a22cca5 commit af5bc23

10 files changed

Lines changed: 269 additions & 8 deletions

File tree

fhir-bulkdata-webapp/src/main/java/com/ibm/fhir/bulkdata/jbatch/control/JobControlContextListener.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
import javax.servlet.annotation.WebListener;
2222

2323
import com.ibm.fhir.config.FHIRConfiguration;
24+
import com.ibm.fhir.model.util.FHIRUtil;
25+
import com.ibm.fhir.model.util.ModelSupport;
26+
import com.ibm.fhir.registry.FHIRRegistry;
27+
import com.ibm.fhir.search.util.SearchUtil;
2428

2529
/**
2630
* This Servlet Context Listener provides a hook to the BatchRuntime.
@@ -35,6 +39,19 @@ public void contextInitialized(ServletContextEvent sce) {
3539

3640
FHIRConfiguration.setConfigHome(System.getenv("FHIR_CONFIG_HOME"));
3741

42+
log.fine("Initializing ModelSupport...");
43+
ModelSupport.init();
44+
45+
log.fine("Initializing FHIRUtil...");
46+
FHIRUtil.init();
47+
48+
log.fine("Initializing FHIRRegistry...");
49+
FHIRRegistry.getInstance();
50+
FHIRRegistry.init();
51+
52+
log.fine("Initializing SearchUtil...");
53+
SearchUtil.init();
54+
3855
try {
3956
@SuppressWarnings("unused")
4057
Subject subject = Subject.getSubject(AccessController.getContext());

fhir-profile/src/test/java/com/ibm/fhir/profile/constraint/test/ProfileConstraintProviderTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,21 @@
1212
import java.util.Set;
1313
import java.util.stream.Collectors;
1414

15+
import org.testng.annotations.BeforeClass;
1516
import org.testng.annotations.Test;
1617

1718
import com.ibm.fhir.model.annotation.Constraint;
1819
import com.ibm.fhir.model.resource.Observation;
1920
import com.ibm.fhir.profile.ProfileSupport;
21+
import com.ibm.fhir.registry.FHIRRegistry;
2022

2123
public class ProfileConstraintProviderTest {
24+
@BeforeClass
25+
public void before() {
26+
FHIRRegistry.getInstance();
27+
FHIRRegistry.init();
28+
}
29+
2230
@Test
2331
public void testProfileConstraintProvider() {
2432
List<Constraint> constraints = ProfileSupport.getConstraints("http://hl7.org/fhir/StructureDefinition/bp", Observation.class);
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* (C) Copyright IBM Corp. 2021
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package com.ibm.fhir.profile.test.provider;
8+
9+
import static org.testng.Assert.assertNotNull;
10+
11+
import org.testng.annotations.BeforeClass;
12+
import org.testng.annotations.Test;
13+
14+
import com.ibm.fhir.model.annotation.Constraint;
15+
import com.ibm.fhir.model.resource.StructureDefinition;
16+
import com.ibm.fhir.model.type.Extension;
17+
import com.ibm.fhir.model.util.ModelSupport;
18+
import com.ibm.fhir.profile.ProfileSupport;
19+
import com.ibm.fhir.registry.FHIRRegistry;
20+
import com.ibm.fhir.registry.resource.FHIRRegistryResource;
21+
22+
public class ProviderTest {
23+
@BeforeClass
24+
public void before() {
25+
FHIRRegistry.getInstance();
26+
FHIRRegistry.init();
27+
}
28+
29+
@Test
30+
public void testProviderRegistryLookup() {
31+
String url = "http://ibm.com/example/fhir/StructureDefinition/orgRef";
32+
StructureDefinition sd = FHIRRegistry.getInstance().getResource(url, StructureDefinition.class);
33+
assertNotNull(sd);
34+
}
35+
36+
@Test
37+
public void testProviderWithLocalLookup() throws Exception {
38+
TestRegistryResourceProvider provider = new TestRegistryResourceProvider();
39+
provider.init();
40+
for (FHIRRegistryResource registryResource : provider.getRegistryResources()) {
41+
if (StructureDefinition.class.equals(registryResource.getResourceType())) {
42+
String url = registryResource.getUrl();
43+
System.out.println(url);
44+
Class<?> type = ModelSupport.isResourceType(registryResource.getType()) ? ModelSupport.getResourceType(registryResource.getType()) : Extension.class;
45+
for (Constraint constraint : ProfileSupport.getConstraints(url, type)) {
46+
System.out.println(" " + constraint);
47+
}
48+
}
49+
}
50+
}
51+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* (C) Copyright IBM Corp. 2021
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package com.ibm.fhir.profile.test.provider;
8+
9+
import static com.ibm.fhir.model.type.String.string;
10+
11+
import java.util.Arrays;
12+
import java.util.Collection;
13+
import java.util.Collections;
14+
import java.util.List;
15+
import java.util.stream.Collectors;
16+
17+
import com.ibm.fhir.model.resource.Resource;
18+
import com.ibm.fhir.model.resource.SearchParameter;
19+
import com.ibm.fhir.model.resource.StructureDefinition;
20+
import com.ibm.fhir.model.type.Boolean;
21+
import com.ibm.fhir.model.type.Canonical;
22+
import com.ibm.fhir.model.type.Markdown;
23+
import com.ibm.fhir.model.type.Uri;
24+
import com.ibm.fhir.model.type.code.ExtensionContextType;
25+
import com.ibm.fhir.model.type.code.PublicationStatus;
26+
import com.ibm.fhir.model.type.code.StructureDefinitionKind;
27+
import com.ibm.fhir.registry.resource.FHIRRegistryResource;
28+
import com.ibm.fhir.registry.spi.AbstractRegistryResourceProvider;
29+
30+
/**
31+
* A TestRegistryResourceProvider that supplies a custom StructureDefinition for an Extension
32+
*/
33+
public class TestRegistryResourceProvider extends AbstractRegistryResourceProvider {
34+
35+
private List<FHIRRegistryResource> registryResources = null;
36+
37+
/**
38+
* When using a ServiceLoader statics/class level instances the ServiceLoader
39+
* loads from the classloader and the order is not fixed.
40+
* Once these are created, then we can use the resource lookup to support retrieval.
41+
*/
42+
@Override
43+
public void init() {
44+
deferredLoad();
45+
}
46+
private void deferredLoad() {
47+
if (registryResources == null) {
48+
synchronized (TestRegistryResourceProvider.class) {
49+
if (registryResources == null) {
50+
// @implNote this double check idiom is intentional.
51+
registryResources = Arrays.asList(
52+
FHIRRegistryResource.from(generateStructureDefinition()));
53+
}
54+
}
55+
}
56+
}
57+
58+
/**
59+
* generates the structured definition used in the deferredLoad
60+
* @return
61+
*/
62+
private StructureDefinition generateStructureDefinition() {
63+
StructureDefinition.Builder builder = StructureDefinition.builder();
64+
builder.setValidating(false);
65+
66+
return builder.url(Uri.of("http://ibm.com/example/fhir/StructureDefinition/orgRef"))
67+
.name(string("OrganizationRef"))
68+
.description(Markdown.of("Sample example organization ref."))
69+
._abstract(Boolean.FALSE)
70+
.type(Uri.of("Extension"))
71+
.status(PublicationStatus.DRAFT)
72+
.kind(StructureDefinitionKind.COMPLEX_TYPE)
73+
.context(com.ibm.fhir.model.resource.StructureDefinition.Context.builder()
74+
.type(ExtensionContextType.ELEMENT)
75+
.expression(string("Endpoint")).build())
76+
.baseDefinition(Canonical.builder().value("Extension")
77+
.build())
78+
.snapshot(loadFromRegistry("http://hl7.org/fhir/StructureDefinition/Extension", StructureDefinition.class).getSnapshot())
79+
.differential(null)
80+
.build();
81+
}
82+
83+
@Override
84+
protected List<FHIRRegistryResource> getRegistryResources(Class<? extends Resource> resourceType, String url) {
85+
return registryResources.stream()
86+
.filter(rr -> rr.getResourceType() == resourceType && rr.getUrl().equals(url))
87+
.collect(Collectors.toList());
88+
}
89+
90+
@Override
91+
public Collection<FHIRRegistryResource> getRegistryResources(Class<? extends Resource> resourceType) {
92+
return registryResources.stream()
93+
.filter(rr -> rr.getResourceType().equals(resourceType))
94+
.collect(Collectors.toSet());
95+
}
96+
97+
@Override
98+
public Collection<FHIRRegistryResource> getRegistryResources() {
99+
return registryResources;
100+
}
101+
102+
@Override
103+
public Collection<FHIRRegistryResource> getProfileResources(String type) {
104+
return Collections.emptySet();
105+
}
106+
107+
@Override
108+
public Collection<FHIRRegistryResource> getSearchParameterResources(String type) {
109+
return registryResources.stream()
110+
.filter(rr -> rr.getResourceType() == SearchParameter.class)
111+
.filter(rr -> ((SearchParameter) rr.getResource()).getType().getValue().equals(type))
112+
.collect(Collectors.toSet());
113+
}
114+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
com.ibm.fhir.profile.test.provider.TestRegistryResourceProvider

fhir-registry/src/main/java/com/ibm/fhir/registry/FHIRRegistry.java

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ private FHIRRegistry() {
5858
public void addProvider(FHIRRegistryResourceProvider provider) {
5959
Objects.requireNonNull(provider);
6060
providers.add(provider);
61+
provider.init();
6162
}
6263

6364
/**
@@ -80,7 +81,7 @@ public String getDefaultVersion(String url, Class<? extends Resource> resourceTy
8081
url = url.substring(0, index);
8182
}
8283

83-
FHIRRegistryResource resource = findRegistryResource(resourceType, url, null);
84+
FHIRRegistryResource resource = findRegistryResource(resourceType, url, null, null);
8485
return (resource != null) ? resource.getVersion().toString() : null;
8586
}
8687

@@ -141,6 +142,26 @@ public Collection<Canonical> getProfiles(String type) {
141142
* if the resource type is not a definitional resource type
142143
*/
143144
public <T extends Resource> T getResource(String url, Class<T> resourceType) {
145+
return getResource(url, resourceType, null);
146+
}
147+
148+
/**
149+
* Get the resource for the given canonical url and resource type
150+
*
151+
* @param url
152+
* the canonical url (with optional version postfix)
153+
* @param resourceType
154+
* the resource type
155+
* @param providerNameToExclude
156+
* the canonical class name of the provider that is to be excluded
157+
* @return
158+
* the resource for the given canonical url and resource type if exists, null otherwise
159+
* @throws ClassCastException
160+
* if the resource exists in the registry but its type does not match given resource type
161+
* @throws IllegalArgumentException
162+
* if the resource type is not a definitional resource type
163+
*/
164+
public <T extends Resource> T getResource(String url, Class<T> resourceType, String providerNameToExclude) {
144165
Objects.requireNonNull(url);
145166
Objects.requireNonNull(resourceType);
146167
requireDefinitionalResourceType(resourceType);
@@ -159,7 +180,7 @@ public <T extends Resource> T getResource(String url, Class<T> resourceType) {
159180
url = url.substring(0, index);
160181
}
161182

162-
return resourceType.cast(getResource(findRegistryResource(resourceType, url, version), url, id));
183+
return resourceType.cast(getResource(findRegistryResource(resourceType, url, version, providerNameToExclude), url, id));
163184
}
164185

165186
/**
@@ -259,11 +280,11 @@ public boolean hasResource(String url, Class<? extends Resource> resourceType) {
259280
url = url.substring(0, index);
260281
}
261282

262-
FHIRRegistryResource registryResource = findRegistryResource(resourceType, url, version);
283+
FHIRRegistryResource registryResource = findRegistryResource(resourceType, url, version, null);
263284
return (id != null) ? (getResource(registryResource, url, id) != null) : (registryResource != null);
264285
}
265286

266-
private FHIRRegistryResource findRegistryResource(Class<? extends Resource> resourceType, String url, String version) {
287+
private FHIRRegistryResource findRegistryResource(Class<? extends Resource> resourceType, String url, String version, String providerNameToExclude) {
267288
if (version != null) {
268289
// find the first registry resource with the specified resourceType, url, and version (across all providers)
269290
for (FHIRRegistryResourceProvider provider : providers) {
@@ -276,6 +297,10 @@ private FHIRRegistryResource findRegistryResource(Class<? extends Resource> reso
276297
// find the default (or latest) version of the registry resource with the specified resourceType and url (across all providers)
277298
Set<FHIRRegistryResource> distinct = new HashSet<>();
278299
for (FHIRRegistryResourceProvider provider : providers) {
300+
if (providerNameToExclude != null && providerNameToExclude.equals(provider.getClass().getCanonicalName())) {
301+
// Needs to skip as the provider is calling the findRegistryResource and trying to find it in itself.
302+
continue;
303+
}
279304
FHIRRegistryResource registryResource = provider.getRegistryResource(resourceType, url, version);
280305
if (registryResource != null) {
281306
distinct.add(registryResource);
@@ -326,6 +351,15 @@ private List<FHIRRegistryResourceProvider> loadProviders() {
326351
return providers;
327352
}
328353

354+
/**
355+
* initializes the Resource Providers.
356+
*/
357+
public static void init() {
358+
for (FHIRRegistryResourceProvider provider : getInstance().providers) {
359+
provider.init();
360+
}
361+
}
362+
329363
/**
330364
* Get the singleton instance of this class
331365
*

fhir-registry/src/main/java/com/ibm/fhir/registry/spi/AbstractRegistryResourceProvider.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.Objects;
1212

1313
import com.ibm.fhir.model.resource.Resource;
14+
import com.ibm.fhir.registry.FHIRRegistry;
1415
import com.ibm.fhir.registry.resource.FHIRRegistryResource;
1516
import com.ibm.fhir.registry.resource.FHIRRegistryResource.Version;
1617

@@ -45,6 +46,22 @@ public final FHIRRegistryResource getRegistryResource(Class<? extends Resource>
4546
return null;
4647
}
4748

49+
/**
50+
* facilitates the retrieval of a resource from the registry allowing the provider to
51+
* be excluded from the resource retrieval.
52+
*
53+
* designed to be used at startup.
54+
*
55+
* @param <T>
56+
* @param url
57+
* @param resourceType
58+
* @return
59+
*/
60+
protected <T extends Resource> T loadFromRegistry(String url, Class<T> resourceType){
61+
return FHIRRegistry.getInstance()
62+
.getResource(url, resourceType, getClass().getCanonicalName());
63+
}
64+
4865
/**
4966
* Return a sorted list of FHIRRegistryResource with the passed canonical url
5067
*

fhir-registry/src/main/java/com/ibm/fhir/registry/spi/FHIRRegistryResourceProvider.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,18 @@
1313
import com.ibm.fhir.registry.resource.FHIRRegistryResource;
1414

1515
/**
16-
* An SPI for {@link FHIRRegistryResource} instances
16+
* An SPI for {@link FHIRRegistryResource} instances.
17+
*
18+
* When implementing this SPI, the constructor and instance variables should not make calls to the FHIRRegistry. Use the init method to call back to the Registry.
1719
*/
1820
public interface FHIRRegistryResourceProvider {
21+
/**
22+
* Facilitates callbacks after the ServiceLoader has discovered the providers and conditionally loaded the Providers.
23+
*/
24+
default void init() {
25+
// NOP
26+
}
27+
1928
/**
2029
* Get the registry resource from this provider for the given resource type, url and version
2130
*

fhir-server-test/src/test/java/com/ibm/fhir/server/test/FHIRServerTestBase.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
import com.ibm.fhir.model.type.code.RestfulCapabilityMode;
7373
import com.ibm.fhir.model.type.code.SystemRestfulInteraction;
7474
import com.ibm.fhir.model.type.code.TypeRestfulInteraction;
75+
import com.ibm.fhir.registry.FHIRRegistry;
7576
import com.ibm.fhir.server.test.websocket.FHIRNotificationServiceClientEndpoint;
7677
import com.ibm.fhir.validation.FHIRValidator;
7778

@@ -172,6 +173,12 @@ public boolean shouldSkipCleanup() {
172173
return Boolean.FALSE;
173174
}
174175

176+
@BeforeClass
177+
public void before() {
178+
FHIRRegistry.getInstance();
179+
FHIRRegistry.init();
180+
}
181+
175182
@AfterClass
176183
public void cleanup() {
177184
cleanupResourcesUsingResourceRegistry(shouldSkipCleanup());

0 commit comments

Comments
 (0)