Merge branch 'master' into date-match-bug

This commit is contained in:
Tadgh 2020-04-29 16:17:50 -07:00
commit 8f8a8a3dc2
1244 changed files with 29032 additions and 62522 deletions

17
.github/stale.yml vendored Normal file
View File

@ -0,0 +1,17 @@
# Two years until issues go stale
daysUntilStale: 730
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@ -6,7 +6,7 @@ HAPI FHIR - Java API for HL7 FHIR Clients and Servers
[![Build Status](https://dev.azure.com/jamesagnew214/jamesagnew214/_apis/build/status/jamesagnew.hapi-fhir?branchName=master)](https://dev.azure.com/jamesagnew214/jamesagnew214/_build/latest?definitionId=1&branchName=master)
[![codecov](https://codecov.io/gh/jamesagnew/hapi-fhir/branch/master/graph/badge.svg)](https://codecov.io/gh/jamesagnew/hapi-fhir)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/ca.uhn.hapi.fhir/hapi-fhir-base/badge.svg)](http://search.maven.org/#search|ga|1|ca.uhn.hapi.fhir)
[![License](https://img.shields.io/badge/license-apache%202.0-60C060.svg)](http://jamesagnew.github.io/hapi-fhir/license.html)
[![License](https://img.shields.io/badge/license-apache%202.0-60C060.svg)](https://hapifhir.io/hapi-fhir/license.html)
Complete project documentation is available here:
http://hapifhir.io

View File

@ -40,10 +40,8 @@
</p>
</th:block>
<p>
<b style="color: red;">
<span class="glyphicon glyphicon-warning-sign"/>
This is not a production server!
</b>
<b class="text-danger"><span class="glyphicon glyphicon-warning-sign"/></b>
<b><span class="text-danger">This is not a production server!</span></b>
Do not store any information here that contains personal health information
or any other confidential information. This server will be regularly purged
and reloaded with fixed test data.

View File

@ -40,10 +40,8 @@
</p>
</th:block>
<p>
<b style="color: red;">
<span class="glyphicon glyphicon-warning-sign"/>
This is not a production server!
</b>
<b class="text-danger"><span class="glyphicon glyphicon-warning-sign"/></b>
<b><span class="text-danger">This is not a production server!</span></b>
Do not store any information here that contains personal health information
or any other confidential information. This server will be regularly purged
and reloaded with fixed test data.

View File

@ -40,10 +40,8 @@
</p>
</th:block>
<p>
<b style="color: red;">
<span class="glyphicon glyphicon-warning-sign"/>
This is not a production server!
</b>
<b class="text-danger"><span class="glyphicon glyphicon-warning-sign"/></b>
<b><span class="text-danger">This is not a production server!</span></b>
Do not store any information here that contains personal health information
or any other confidential information. This server will be regularly purged
and reloaded with fixed test data.

125
examples/.gitignore vendored
View File

@ -1,125 +0,0 @@
/target/
# Created by https://www.gitignore.io
### Java ###
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
### Vim ###
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
*~
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Eclipse ###
*.pydevproject
.metadata
.gradle
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.loadpath
# Eclipse Core
.project
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# JDT-specific (Eclipse Java Development Tools)
# PDT-specific
.buildpath
# sbteclipse plugin
.target
# TeXlipse plugin
.texlipse

View File

@ -1,133 +0,0 @@
<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>4.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-base-examples</artifactId>
<packaging>jar</packaging>
<name>HAPI FHIR - Examples (for site)</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-bom</artifactId>
<version>${project.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2.1</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-converter</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client-okhttp</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- jax rs server -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.ejb</groupId>
<artifactId>ejb-api</artifactId>
<version>3.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j_version}</version>
</dependency>
</dependencies>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</reporting>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,216 +0,0 @@
package example;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.auth.*;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.util.List;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@SuppressWarnings("unused")
public class AuthorizationInterceptors {
public class PatientResourceProvider implements IResourceProvider
{
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
public MethodOutcome create(@ResourceParam Patient thePatient, RequestDetails theRequestDetails) {
return new MethodOutcome(); // populate this
}
}
//START SNIPPET: patientAndAdmin
@SuppressWarnings("ConstantConditions")
public class PatientAndAdminAuthorizationInterceptor extends AuthorizationInterceptor {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
// Process authorization header - The following is a fake
// implementation. Obviously we'd want something more real
// for a production scenario.
//
// In this basic example we have two hardcoded bearer tokens,
// one which is for a user that has access to one patient, and
// another that has full access.
IdDt userIdPatientId = null;
boolean userIsAdmin = false;
String authHeader = theRequestDetails.getHeader("Authorization");
if ("Bearer dfw98h38r".equals(authHeader)) {
// This user has access only to Patient/1 resources
userIdPatientId = new IdDt("Patient", 1L);
} else if ("Bearer 39ff939jgg".equals(authHeader)) {
// This user has access to everything
userIsAdmin = true;
} else {
// Throw an HTTP 401
throw new AuthenticationException("Missing or invalid Authorization header value");
}
// If the user is a specific patient, we create the following rule chain:
// Allow the user to read anything in their own patient compartment
// Allow the user to write anything in their own patient compartment
// If a client request doesn't pass either of the above, deny it
if (userIdPatientId != null) {
return new RuleBuilder()
.allow().read().allResources().inCompartment("Patient", userIdPatientId).andThen()
.allow().write().allResources().inCompartment("Patient", userIdPatientId).andThen()
.denyAll()
.build();
}
// If the user is an admin, allow everything
if (userIsAdmin) {
return new RuleBuilder()
.allowAll()
.build();
}
// By default, deny everything. This should never get hit, but it's
// good to be defensive
return new RuleBuilder()
.denyAll()
.build();
}
}
//END SNIPPET: patientAndAdmin
//START SNIPPET: conditionalUpdate
@Update()
public MethodOutcome update(
@IdParam IdDt theId,
@ResourceParam Patient theResource,
@ConditionalUrlParam String theConditionalUrl,
ServletRequestDetails theRequestDetails,
IInterceptorBroadcaster theInterceptorBroadcaster) {
// If we're processing a conditional URL...
if (isNotBlank(theConditionalUrl)) {
// Pretend we've done the conditional processing. Now let's
// notify the interceptors that an update has been performed
// and supply the actual ID that's being updated
IdDt actual = new IdDt("Patient", "1123");
}
// In a real server, perhaps we would process the conditional
// request differently and follow a separate path. Either way,
// let's pretend there is some storage code here.
theResource.setId(theId.withVersion("2"));
// Notify the interceptor framework when we're about to perform an update. This is
// useful as the authorization interceptor will pick this event up and use it
// to factor into a decision about whether the operation should be allowed to proceed.
IBaseResource previousContents = theResource;
IBaseResource newContents = theResource;
HookParams params = new HookParams()
.add(IBaseResource.class, previousContents)
.add(IBaseResource.class, newContents)
.add(RequestDetails.class, theRequestDetails)
.add(ServletRequestDetails.class, theRequestDetails);
theInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED, params);
MethodOutcome retVal = new MethodOutcome();
retVal.setCreated(true);
retVal.setResource(theResource);
return retVal;
}
//END SNIPPET: conditionalUpdate
public void authorizeTenantAction() {
//START SNIPPET: authorizeTenantAction
new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().read().resourcesOfType(Patient.class).withAnyId().forTenantIds("TENANTA").andThen()
.build();
}
};
//END SNIPPET: authorizeTenantAction
//START SNIPPET: patchAll
new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
// Authorize patch requests
.allow().patch().allRequests().andThen()
// Authorize actual writes that patch may perform
.allow().write().allResources().inCompartment("Patient", new IdType("Patient/123")).andThen()
.build();
}
};
//END SNIPPET: patchAll
}
//START SNIPPET: narrowing
public class MyPatientSearchNarrowingInterceptor extends SearchNarrowingInterceptor {
/**
* This method must be overridden to provide the list of compartments
* and/or resources that the current user should have access to
*/
@Override
protected AuthorizedList buildAuthorizedList(RequestDetails theRequestDetails) {
// Process authorization header - The following is a fake
// implementation. Obviously we'd want something more real
// for a production scenario.
//
// In this basic example we have two hardcoded bearer tokens,
// one which is for a user that has access to one patient, and
// another that has full access.
String authHeader = theRequestDetails.getHeader("Authorization");
if ("Bearer dfw98h38r".equals(authHeader)) {
// This user will have access to two compartments
return new AuthorizedList()
.addCompartment("Patient/123")
.addCompartment("Patient/456");
} else if ("Bearer 39ff939jgg".equals(authHeader)) {
// This user has access to everything
return new AuthorizedList();
} else {
throw new AuthenticationException("Unknown bearer token");
}
}
}
//END SNIPPET: narrowing
}

View File

@ -1,23 +0,0 @@
package example;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor;
import ca.uhn.fhir.rest.server.util.ITestingUiClientFactory;
public class AuthorizingTesterUiClientFactory implements ITestingUiClientFactory {
@Override
public IGenericClient newClient(FhirContext theFhirContext, HttpServletRequest theRequest, String theServerBaseUrl) {
// Create a client
IGenericClient client = theFhirContext.newRestfulGenericClient(theServerBaseUrl);
// Register an interceptor which adds credentials
client.registerInterceptor(new BasicAuthInterceptor("someusername", "somepassword"));
return client;
}
}

View File

@ -1,67 +0,0 @@
package example;
import java.util.HashSet;
import java.util.Set;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.RelatedPerson;
import ca.uhn.fhir.rest.client.api.IGenericClient;
/**
* @author Bill de Beaubien on 1/13/2016.
*/
public class BundleFetcher {
public static void fetchRestOfBundle(IGenericClient theClient, Bundle theBundle) {
// we need to keep track of which resources are already in the bundle so that if other resources (e.g. Practitioner) are _included,
// we don't end up with multiple copies
Set<String> resourcesAlreadyAdded = new HashSet<String>();
addInitialUrlsToSet(theBundle, resourcesAlreadyAdded);
Bundle partialBundle = theBundle;
for (;;) {
if (partialBundle.getLink(IBaseBundle.LINK_NEXT) != null) {
partialBundle = theClient.loadPage().next(partialBundle).execute();
addAnyResourcesNotAlreadyPresentToBundle(theBundle, partialBundle, resourcesAlreadyAdded);
} else {
break;
}
}
// the self and next links for the aggregated bundle aren't really valid anymore, so remove them
theBundle.getLink().clear();
}
private static void addInitialUrlsToSet(Bundle theBundle, Set<String> theResourcesAlreadyAdded) {
for (Bundle.Entry entry : theBundle.getEntry()) {
theResourcesAlreadyAdded.add(entry.getFullUrl());
}
}
private static void addAnyResourcesNotAlreadyPresentToBundle(Bundle theAggregatedBundle, Bundle thePartialBundle, Set<String> theResourcesAlreadyAdded) {
for (Bundle.Entry entry : thePartialBundle.getEntry()) {
if (!theResourcesAlreadyAdded.contains(entry.getFullUrl())) {
theResourcesAlreadyAdded.add(entry.getFullUrl());
theAggregatedBundle.getEntry().add(entry);
}
}
}
public static void main(String[] args) throws Exception {
FhirContext ctx = FhirContext.forDstu2();
String serverBase = "http://fhirtest.uhn.ca/baseDstu2";
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
// use RelatedPerson because there aren't that many on the server
Bundle bundle = client.search().forResource(RelatedPerson.class).returnBundle(Bundle.class).execute();
BundleFetcher.fetchRestOfBundle(client, bundle);
if (bundle.getTotal() != bundle.getEntry().size()) {
System.out.println("Counts didn't match! Expected " + bundle.getTotal() + " but bundle only had " + bundle.getEntry().size() + " entries!");
}
// IParser parser = ctx.newXmlParser().setPrettyPrint(true);
// System.out.println(parser.encodeResourceToString(bundle));
}
}

View File

@ -1,224 +0,0 @@
package example;
import ca.uhn.fhir.rest.api.CacheControlDirective;
import org.hl7.fhir.dstu3.model.Bundle;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.okhttp.client.OkHttpRestfulClientFactory;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.client.apache.GZipContentInterceptor;
import ca.uhn.fhir.rest.client.api.*;
import ca.uhn.fhir.rest.client.interceptor.*;
import org.hl7.fhir.r4.model.Patient;
public class ClientExamples {
public interface IPatientClient extends IBasicClient {
// nothing yet
}
@SuppressWarnings("unused")
public void createProxy() {
// START SNIPPET: proxy
FhirContext ctx = FhirContext.forDstu2();
// Set connections to access the network via the HTTP proxy at
// example.com : 8888
ctx.getRestfulClientFactory().setProxy("example.com", 8888);
// If the proxy requires authentication, use the following as well
ctx.getRestfulClientFactory().setProxyCredentials("theUsername", "thePassword");
// Create the client
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
// END SNIPPET: proxy
}
@SuppressWarnings("unused")
public void processMessage() {
// START SNIPPET: processMessage
FhirContext ctx = FhirContext.forDstu3();
// Create the client
IGenericClient client = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
Bundle bundle = new Bundle();
// ..populate the bundle..
Bundle response = client
.operation()
.processMessage() // New operation for sending messages
.setMessageBundle(bundle)
.asynchronous(Bundle.class)
.execute();
// END SNIPPET: processMessage
}
@SuppressWarnings("unused")
public void cacheControl() {
FhirContext ctx = FhirContext.forDstu3();
// Create the client
IGenericClient client = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
Bundle bundle = new Bundle();
// ..populate the bundle..
// START SNIPPET: cacheControl
Bundle response = client
.search()
.forResource(Patient.class)
.returnBundle(Bundle.class)
.cacheControl(new CacheControlDirective().setNoCache(true)) // <-- add a directive
.execute();
// END SNIPPET: cacheControl
}
@SuppressWarnings("unused")
public void createOkHttp() {
// START SNIPPET: okhttp
FhirContext ctx = FhirContext.forDstu3();
// Use OkHttp
ctx.setRestfulClientFactory(new OkHttpRestfulClientFactory(ctx));
// Create the client
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
// END SNIPPET: okhttp
}
@SuppressWarnings("unused")
public void createTimeouts() {
// START SNIPPET: timeouts
FhirContext ctx = FhirContext.forDstu2();
// Set how long to try and establish the initial TCP connection (in ms)
ctx.getRestfulClientFactory().setConnectTimeout(20 * 1000);
// Set how long to block for individual read/write operations (in ms)
ctx.getRestfulClientFactory().setSocketTimeout(20 * 1000);
// Create the client
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
// END SNIPPET: timeouts
}
@SuppressWarnings("unused")
public void createSecurity() {
// START SNIPPET: security
// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// Create an HTTP basic auth interceptor
String username = "foobar";
String password = "boobear";
IClientInterceptor authInterceptor = new BasicAuthInterceptor(username, password);
// If you're usinf an annotation client, use this style to
// register it
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(authInterceptor);
// If you're using a generic client, use this instead
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
genericClient.registerInterceptor(authInterceptor);
// END SNIPPET: security
}
@SuppressWarnings("unused")
public void createCookie() {
// START SNIPPET: cookie
// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// Create a cookie interceptor. This cookie will have the name "mycookie" and
// the value "Chips Ahoy"
CookieInterceptor interceptor = new CookieInterceptor("mycookie=Chips Ahoy");
// Register the interceptor with your client (either style)
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(interceptor);
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
annotationClient.registerInterceptor(interceptor);
// END SNIPPET: cookie
}
@SuppressWarnings("unused")
public void gzip() {
// START SNIPPET: gzip
// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// Register the interceptor with your client (either style)
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(new GZipContentInterceptor());
// END SNIPPET: gzip
}
@SuppressWarnings("unused")
public void createSecurityBearer() {
// START SNIPPET: securityBearer
// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// In reality the token would have come from an authorization server
String token = "3w03fj.r3r3t";
BearerTokenAuthInterceptor authInterceptor = new BearerTokenAuthInterceptor(token);
// Register the interceptor with your client (either style)
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(authInterceptor);
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
annotationClient.registerInterceptor(authInterceptor);
// END SNIPPET: securityBearer
}
@SuppressWarnings("unused")
public void createLogging() {
{
// START SNIPPET: logging
// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// Create a logging interceptor
LoggingInterceptor loggingInterceptor = new LoggingInterceptor();
// Optionally you may configure the interceptor (by default only
// summary info is logged)
loggingInterceptor.setLogRequestSummary(true);
loggingInterceptor.setLogRequestBody(true);
// Register the interceptor with your client (either style)
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(loggingInterceptor);
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
genericClient.registerInterceptor(loggingInterceptor);
// END SNIPPET: logging
}
/******************************/
{
// START SNIPPET: clientConfig
// Create a client
FhirContext ctx = FhirContext.forDstu2();
IPatientClient client = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/");
// Request JSON encoding from the server (_format=json)
client.setEncoding(EncodingEnum.JSON);
// Request pretty printing from the server (_pretty=true)
client.setPrettyPrint(true);
// END SNIPPET: clientConfig
}
}
}

View File

@ -1,94 +0,0 @@
package example;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.*;
import ca.uhn.fhir.model.dstu2.valueset.*;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.client.api.IGenericClient;
public class ClientTransactionExamples {
public static void main(String[] args) {
conditionalCreate();
}
private static void conditionalCreate() {
//START SNIPPET: conditional
// Create a patient object
Patient patient = new Patient();
patient.addIdentifier()
.setSystem("http://acme.org/mrns")
.setValue("12345");
patient.addName()
.addFamily("Jameson")
.addGiven("J")
.addGiven("Jonah");
patient.setGender(AdministrativeGenderEnum.MALE);
// Give the patient a temporary UUID so that other resources in
// the transaction can refer to it
patient.setId(IdDt.newRandomUuid());
// Create an observation object
Observation observation = new Observation();
observation.setStatus(ObservationStatusEnum.FINAL);
observation
.getCode()
.addCoding()
.setSystem("http://loinc.org")
.setCode("789-8")
.setDisplay("Erythrocytes [#/volume] in Blood by Automated count");
observation.setValue(
new QuantityDt()
.setValue(4.12)
.setUnit("10 trillion/L")
.setSystem("http://unitsofmeasure.org")
.setCode("10*12/L"));
// The observation refers to the patient using the ID, which is already
// set to a temporary UUID
observation.setSubject(new ResourceReferenceDt(patient.getId().getValue()));
// Create a bundle that will be used as a transaction
Bundle bundle = new Bundle();
bundle.setType(BundleTypeEnum.TRANSACTION);
// Add the patient as an entry. This entry is a POST with an
// If-None-Exist header (conditional create) meaning that it
// will only be created if there isn't already a Patient with
// the identifier 12345
bundle.addEntry()
.setFullUrl(patient.getId().getValue())
.setResource(patient)
.getRequest()
.setUrl("Patient")
.setIfNoneExist("identifier=http://acme.org/mrns|12345")
.setMethod(HTTPVerbEnum.POST);
// Add the observation. This entry is a POST with no header
// (normal create) meaning that it will be created even if
// a similar resource already exists.
bundle.addEntry()
.setResource(observation)
.getRequest()
.setUrl("Observation")
.setMethod(HTTPVerbEnum.POST);
// Log the request
FhirContext ctx = FhirContext.forDstu2();
System.out.println(ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(bundle));
// Create a client and post the transaction to the server
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
Bundle resp = client.transaction().withBundle(bundle).execute();
// Log the response
System.out.println(ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
//END SNIPPET: conditional
}
}

View File

@ -1,66 +0,0 @@
package example;
//START SNIPPET: client
import java.io.IOException;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.Organization;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
public class CompleteExampleClient {
/**
* This is a simple client interface. It can have many methods for various
* searches but in this case it has only 1.
*/
public static interface ClientInterface extends IRestfulClient {
/**
* This is translated into a URL similar to the following:
* http://fhir.healthintersections.com.au/open/Patient?identifier=urn:oid:1.2.36.146.595.217.0.1%7C12345
*/
@Search
List<Patient> findPatientsForMrn(@RequiredParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier);
}
/**
* The main method here will directly call an open FHIR server and retrieve a
* list of resources matching a given criteria, then load a linked resource.
*/
public static void main(String[] args) throws IOException {
// Create a client factory
FhirContext ctx = FhirContext.forDstu2();
// Create the client
String serverBase = "http://fhir.healthintersections.com.au/open";
ClientInterface client = ctx.newRestfulClient(ClientInterface.class, serverBase);
// Invoke the client to search for patient
List<Patient> patients = client.findPatientsForMrn(new IdentifierDt("urn:oid:1.2.36.146.595.217.0.1", "12345"));
System.out.println("Found " + patients.size() + " patients");
// Print a value from the loaded resource
Patient patient = patients.get(0);
System.out.println("Patient Last Name: " + patient.getName().get(0).getFamily().get(0).getValue());
// Load a referenced resource
ResourceReferenceDt managingRef = patient.getManagingOrganization();
Organization org = (Organization) managingRef.loadResource(client);
// Print organization name
System.out.println(org.getName());
}
}
// END SNIPPET: client

View File

@ -1,75 +0,0 @@
package example;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.interceptor.consent.ConsentOutcome;
import ca.uhn.fhir.rest.server.interceptor.consent.IConsentContextServices;
import ca.uhn.fhir.rest.server.interceptor.consent.IConsentService;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Observation;
@SuppressWarnings("unused")
public class ConsentInterceptors {
//START SNIPPET: service
public class MyConsentService implements IConsentService {
/**
* Invoked once at the start of every request
*/
@Override
public ConsentOutcome startOperation(RequestDetails theRequestDetails, IConsentContextServices theContextServices) {
// This means that all requests should flow through the consent service
// This has performance implications - If you know that some requests
// don't need consent checking it is a good idea to return
// ConsentOutcome.AUTHORIZED instead for those requests.
return ConsentOutcome.PROCEED;
}
/**
* Can a given resource be returned to the user?
*/
@Override
public ConsentOutcome canSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) {
// In this basic example, we will filter out lab results so that they
// are never disclosed to the user. A real interceptor might do something
// more nuanced.
if (theResource instanceof Observation) {
Observation obs = (Observation)theResource;
if (obs.getCategoryFirstRep().hasCoding("http://hl7.org/fhir/codesystem-observation-category.html", "laboratory")) {
return ConsentOutcome.REJECT;
}
}
// Otherwise, allow the
return ConsentOutcome.PROCEED;
}
/**
* Modify resources that are being shown to the user
*/
@Override
public ConsentOutcome willSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) {
// Don't return the subject for Observation resources
if (theResource instanceof Observation) {
Observation obs = (Observation)theResource;
obs.setSubject(null);
}
return ConsentOutcome.AUTHORIZED;
}
@Override
public void completeOperationSuccess(RequestDetails theRequestDetails, IConsentContextServices theContextServices) {
// We could write an audit trail entry in here
}
@Override
public void completeOperationFailure(RequestDetails theRequestDetails, BaseServerResponseException theException, IConsentContextServices theContextServices) {
// We could write an audit trail entry in here
}
}
//END SNIPPET: service
}

View File

@ -1,35 +0,0 @@
package example;
import org.hl7.fhir.convertors.conv10_30.Observation10_30;
import org.hl7.fhir.convertors.conv14_30.Questionnaire14_30;
import org.hl7.fhir.exceptions.FHIRException;
public class ConverterExamples {
@SuppressWarnings("unused")
public void c1020() throws FHIRException {
//START SNIPPET: 1020
// Create an input resource to convert
org.hl7.fhir.dstu2.model.Observation input = new org.hl7.fhir.dstu2.model.Observation();
input.setEncounter(new org.hl7.fhir.dstu2.model.Reference("Encounter/123"));
// Convert the resource
org.hl7.fhir.dstu3.model.Observation output = Observation10_30.convertObservation(input);
String context = output.getContext().getReference();
//END SNIPPET: 1020
}
@SuppressWarnings("unused")
public void c1420() throws FHIRException {
//START SNIPPET: 1420
// Create a resource to convert
org.hl7.fhir.dstu2016may.model.Questionnaire input = new org.hl7.fhir.dstu2016may.model.Questionnaire();
input.setTitle("My title");
// Convert the resource
org.hl7.fhir.dstu3.model.Questionnaire output = Questionnaire14_30.convertQuestionnaire(input);
String context = output.getTitle();
//END SNIPPET: 1420
}
}

View File

@ -1,121 +0,0 @@
package example;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.util.ResourceReferenceInfo;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@SuppressWarnings("unused")
public class Copier {
private static final Logger ourLog = LoggerFactory.getLogger(Copier.class);
public static void main(String[] args) {
FhirContext ctx = FhirContext.forDstu3();
IGenericClient source = ctx.newRestfulGenericClient("http://localhost:8080/baseDstu3");
IGenericClient target = ctx.newRestfulGenericClient("https://try.smilecdr.com:8000");
List<String> resType = Arrays.asList(
"Patient", "Organization", "Encounter", "Procedure",
"Observation", "ResearchSubject", "Specimen",
"ResearchStudy", "Location", "Practitioner"
);
List<IBaseResource> queued = new ArrayList<>();
Set<String> sent = new HashSet<>();
for (String next : resType) {
copy(ctx, source, target, next, queued, sent);
}
while (queued.size() > 0) {
ourLog.info("Have {} queued resources to deliver", queued.size());
for (IBaseResource nextQueued : new ArrayList<>(queued)) {
String missingRef = null;
for (ResourceReferenceInfo nextRefInfo : ctx.newTerser().getAllResourceReferences(nextQueued)) {
String nextRef = nextRefInfo.getResourceReference().getReferenceElement().getValue();
if (isNotBlank(nextRef) && !sent.contains(nextRef)) {
missingRef = nextRef;
}
}
if (missingRef != null) {
ourLog.info("Can't send {} because of missing ref {}", nextQueued.getIdElement().getIdPart(), missingRef);
continue;
}
IIdType newId = target
.update()
.resource(nextQueued)
.execute()
.getId();
ourLog.info("Copied resource {} and got ID {}", nextQueued.getIdElement().getValue(), newId);
sent.add(nextQueued.getIdElement().toUnqualifiedVersionless().getValue());
queued.remove(nextQueued);
}
}
}
private static void copy(FhirContext theCtx, IGenericClient theSource, IGenericClient theTarget, String theResType, List<IBaseResource> theQueued, Set<String> theSent) {
Bundle received = theSource
.search()
.forResource(theResType)
.returnBundle(Bundle.class)
.execute();
copy(theCtx, theTarget, theResType, theQueued, theSent, received);
while (received.getLink("next") != null) {
ourLog.info("Fetching next page...");
received = theSource.loadPage().next(received).execute();
copy(theCtx, theTarget, theResType, theQueued, theSent, received);
}
}
private static void copy(FhirContext theCtx, IGenericClient theTarget, String theResType, List<IBaseResource> theQueued, Set<String> theSent, Bundle theReceived) {
for (Bundle.BundleEntryComponent nextEntry : theReceived.getEntry()) {
Resource nextResource = nextEntry.getResource();
nextResource.setId(theResType + "/" + "CR-" + nextResource.getIdElement().getIdPart());
boolean haveUnsentReference = false;
for (ResourceReferenceInfo nextRefInfo : theCtx.newTerser().getAllResourceReferences(nextResource)) {
IIdType nextRef = nextRefInfo.getResourceReference().getReferenceElement();
if (nextRef.hasIdPart()) {
String newRef = nextRef.getResourceType() + "/" + "CR-" + nextRef.getIdPart();
ourLog.info("Changing reference {} to {}", nextRef.getValue(), newRef);
nextRefInfo.getResourceReference().setReference(newRef);
if (!theSent.contains(newRef)) {
haveUnsentReference = true;
}
}
}
if (haveUnsentReference) {
ourLog.info("Queueing {} for delivery after", nextResource.getId());
theQueued.add(nextResource);
continue;
}
IIdType newId = theTarget
.update()
.resource(nextResource)
.execute()
.getId();
ourLog.info("Copied resource {} and got ID {}", nextResource.getId(), newId);
theSent.add(nextResource.getIdElement().toUnqualifiedVersionless().getValue());
}
}
}

View File

@ -1,7 +0,0 @@
package example;
import org.hl7.fhir.dstu3.model.Observation;
public class CustomObservation extends Observation {
}

View File

@ -1,67 +0,0 @@
package example;
import java.util.Collection;
import javax.servlet.ServletException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
@SuppressWarnings("serial")
public class Dstu2Examples {
private Collection<IResourceProvider> resourceProviderList;
public static void main(String[] args) {
new Dstu2Examples().getResourceTags();
}
@SuppressWarnings("unused")
public void getResourceTags() {
// START SNIPPET: context
// Create a DSTU2 context, which will use DSTU2 semantics
FhirContext ctx = FhirContext.forDstu2();
// This parser supports DSTU2
IParser parser = ctx.newJsonParser();
// This client supports DSTU2
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
// END SNIPPET: context
}
// START SNIPPET: server
public class MyServer extends RestfulServer
{
@Override
protected void initialize() throws ServletException {
// In your initialize method, assign a DSTU2 FhirContext. This
// is all that is required in order to put the server
// into DSTU2 mode
setFhirContext(FhirContext.forDstu2());
// Then set resource providers as normal, and do any other
// configuration you need to do.
setResourceProviders(resourceProviderList);
}
}
// END SNIPPET: server
public void upgrade() {
// START SNIPPET: client
FhirContext ctxDstu2 = FhirContext.forDstu2();
IGenericClient clientDstu2 = ctxDstu2.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
// END SNIPPET: client
}
}

View File

@ -1,82 +0,0 @@
package example;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.dstu3.model.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
import ca.uhn.fhir.rest.server.IResourceProvider;
@SuppressWarnings(value= {"serial"})
public class ExampleProviders {
//START SNIPPET: plainProvider
public class PlainProvider {
/**
* This method is a Patient search, but HAPI can not automatically
* determine the resource type so it must be explicitly stated.
*/
@Search(type=Patient.class)
public Bundle searchForPatients(@RequiredParam(name=Patient.SP_NAME) StringDt theName) {
Bundle retVal = new Bundle();
// perform search
return retVal;
}
}
//END SNIPPET: plainProvider
//START SNIPPET: plainProviderServer
public class ExampleServlet extends ca.uhn.fhir.rest.server.RestfulServer {
/**
* Constructor
*/
public ExampleServlet() {
/*
* Plain providers are passed to the server in the same way
* as resource providers. You may pass both resource providers
* and and plain providers to the same server if you like.
*/
List<Object> plainProviders=new ArrayList<Object>();
plainProviders.add(new PlainProvider());
registerProviders(plainProviders);
List<IResourceProvider> resourceProviders = new ArrayList<IResourceProvider>();
// ...add some resource providers...
registerProviders(resourceProviders);
}
}
//END SNIPPET: plainProviderServer
//START SNIPPET: addressStrategy
public class MyServlet extends ca.uhn.fhir.rest.server.RestfulServer {
/**
* Constructor
*/
public MyServlet() {
String serverBaseUrl = "http://foo.com/fhir";
setServerAddressStrategy(new HardcodedServerAddressStrategy(serverBaseUrl));
// ...add some resource providers, etc...
List<IResourceProvider> resourceProviders = new ArrayList<IResourceProvider>();
setResourceProviders(resourceProviders);
}
}
//END SNIPPET: addressStrategy
}

View File

@ -1,26 +0,0 @@
package example;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.StringDt;
@SuppressWarnings("unused")
public class ExampleRestfulClient {
//START SNIPPET: client
public static void main(String[] args) {
FhirContext ctx = FhirContext.forDstu2();
String serverBase = "http://foo.com/fhirServerBase";
// Create the client
IRestfulClient client = ctx.newRestfulClient(IRestfulClient.class, serverBase);
// Try the client out! This method will invoke the server
List<Patient> patients = client.getPatient(new StringDt("SMITH"));
}
//END SNIPPET: client
}

View File

@ -1,45 +0,0 @@
package example;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
//START SNIPPET: servlet
/**
* In this example, we are using Servlet 3.0 annotations to define
* the URL pattern for this servlet, but we could also
* define this in a web.xml file.
*/
@WebServlet(urlPatterns= {"/fhir/*"}, displayName="FHIR Server")
public class ExampleRestfulServlet extends RestfulServer {
private static final long serialVersionUID = 1L;
/**
* The initialize method is automatically called when the servlet is starting up, so it can
* be used to configure the servlet to define resource providers, or set up
* configuration, interceptors, etc.
*/
@Override
protected void initialize() throws ServletException {
/*
* The servlet defines any number of resource providers, and
* configures itself to use them by calling
* setResourceProviders()
*/
List<IResourceProvider> resourceProviders = new ArrayList<IResourceProvider>();
resourceProviders.add(new RestfulPatientResourceProvider());
resourceProviders.add(new RestfulObservationResourceProvider());
setResourceProviders(resourceProviders);
}
}
//END SNIPPET: servlet

View File

@ -1,106 +0,0 @@
package example;
import java.io.IOException;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.resource.Questionnaire.GroupQuestion;
import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException;
public class ExtensionsDstu2 {
@SuppressWarnings("unused")
public static void main(String[] args) throws DataFormatException, IOException {
{
Questionnaire q= new Questionnaire();
GroupQuestion item = q.getGroup().addQuestion();
item.setText("Hello");
ExtensionDt extension = new ExtensionDt(false, "http://hl7.org/fhir/StructureDefinition/translation");
item.getTextElement().addUndeclaredExtension(extension);
extension.addUndeclaredExtension(new ExtensionDt(false, "lang", new CodeDt("es")));
extension.addUndeclaredExtension(new ExtensionDt(false, "cont", new StringDt("hola")));
System.out.println(FhirContext.forDstu2().newJsonParser().setPrettyPrint(true).encodeResourceToString(q));
}
// START SNIPPET: resourceExtension
// Create an example patient
Patient patient = new Patient();
patient.addIdentifier().setUse(IdentifierUseEnum.OFFICIAL).setSystem("urn:example").setValue("7000135");
// Create an extension
ExtensionDt ext = new ExtensionDt();
ext.setModifier(false);
ext.setUrl("http://example.com/extensions#someext");
ext.setValue(new DateTimeDt("2011-01-02T11:13:15"));
// Add the extension to the resource
patient.addUndeclaredExtension(ext);
//END SNIPPET: resourceExtension
//START SNIPPET: resourceStringExtension
// Continuing the example from above, we will add a name to the patient, and then
// add an extension to part of that name
HumanNameDt name = patient.addName();
name.addFamily().setValue("Shmoe");
// Add a new "given name", which is of type StringDt
StringDt given = name.addGiven();
given.setValue("Joe");
// Create an extension and add it to the StringDt
ExtensionDt givenExt = new ExtensionDt(false, "http://examples.com#moreext", new StringDt("Hello"));
given.addUndeclaredExtension(givenExt);
//END SNIPPET: resourceStringExtension
FhirContext ctx = FhirContext.forDstu2();
String output = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(output);
//START SNIPPET: parseExtension
// Get all extensions (modifier or not) for a given URL
List<ExtensionDt> resourceExts = patient.getUndeclaredExtensionsByUrl("http://fooextensions.com#exts");
// Get all non-modifier extensions regardless of URL
List<ExtensionDt> nonModExts = patient.getUndeclaredExtensions();
//Get all non-modifier extensions regardless of URL
List<ExtensionDt> modExts = patient.getUndeclaredModifierExtensions();
//END SNIPPET: parseExtension
}
public void foo() {
//START SNIPPET: subExtension
Patient patient = new Patient();
// Add an extension (initially with no contents) to the resource
ExtensionDt parent = new ExtensionDt(false, "http://example.com#parent");
patient.addUndeclaredExtension(parent);
// Add two extensions as children to the parent extension
ExtensionDt child1 = new ExtensionDt(false, "http://example.com#childOne", new StringDt("value1"));
parent.addUndeclaredExtension(child1);
ExtensionDt child2 = new ExtensionDt(false, "http://example.com#childTwo", new StringDt("value1"));
parent.addUndeclaredExtension(child2);
//END SNIPPET: subExtension
}
}

View File

@ -1,160 +0,0 @@
package example;
import java.io.IOException;
import java.util.List;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Identifier.IdentifierUse;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.client.api.IGenericClient;
public class ExtensionsDstu3 {
public void customType() {
IGenericClient client = FhirContext.forDstu3().newRestfulGenericClient("http://foo");
//START SNIPPET: customTypeClientSimple
// Create an example patient
MyPatient custPatient = new MyPatient();
custPatient.addName().setFamily("Smith").addGiven("John");
custPatient.setPetName(new StringType("Rover")); // populate the extension
// Create the resource like normal
client.create().resource(custPatient).execute();
// You can also read the resource back like normal
custPatient = client.read().resource(MyPatient.class).withId("123").execute();
//END SNIPPET: customTypeClientSimple
//START SNIPPET: customTypeClientSearch
// Perform the search using the custom type
Bundle bundle = client
.search()
.forResource(MyPatient.class)
.returnBundle(Bundle.class)
.execute();
// Entries in the return bundle will use the given type
MyPatient pat0 = (MyPatient) bundle.getEntry().get(0).getResource();
//END SNIPPET: customTypeClientSearch
//START SNIPPET: customTypeClientSearch2
//Perform the search using the custom type
bundle = client
.history()
.onInstance(new IdType("Patient/123"))
.andReturnBundle(Bundle.class)
.preferResponseType(MyPatient.class)
.execute();
//Entries in the return bundle will use the given type
MyPatient historyPatient0 = (MyPatient) bundle.getEntry().get(0).getResource();
//END SNIPPET: customTypeClientSearch2
}
public void customTypeDeclared() {
//START SNIPPET: customTypeClientDeclared
FhirContext ctx = FhirContext.forDstu3();
// Instruct the context that if it receives a resource which
// claims to conform to the given profile (by URL), it should
// use the MyPatient type to parse this resource
ctx.setDefaultTypeForProfile("http://example.com/StructureDefinition/mypatient", MyPatient.class);
// You can declare as many default types as you like
ctx.setDefaultTypeForProfile("http://foo.com/anotherProfile", CustomObservation.class);
// Create a client
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu3");
// You can also read the resource back like normal
Patient patient = client.read().resource(Patient.class).withId("123").execute();
if (patient instanceof MyPatient) {
// If the server supplied a resource which declared to conform
// to the given profile, MyPatient will have been returned so
// process it differently..
}
//END SNIPPET: customTypeClientDeclared
}
@SuppressWarnings("unused")
public static void main(String[] args) throws DataFormatException, IOException {
// START SNIPPET: resourceExtension
// Create an example patient
Patient patient = new Patient();
patient.addIdentifier().setUse(IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135");
// Create an extension
Extension ext = new Extension();
ext.setUrl("http://example.com/extensions#someext");
ext.setValue(new DateTimeType("2011-01-02T11:13:15"));
// Add the extension to the resource
patient.addExtension(ext);
//END SNIPPET: resourceExtension
//START SNIPPET: resourceStringExtension
// Continuing the example from above, we will add a name to the patient, and then
// add an extension to part of that name
HumanName name = patient.addName();
name.setFamily("Shmoe");
// Add a new "given name", which is of type String
StringType given = name.addGivenElement();
given.setValue("Joe");
// Create an extension and add it to the String
Extension givenExt = new Extension("http://examples.com#moreext", new StringType("Hello"));
given.addExtension(givenExt);
//END SNIPPET: resourceStringExtension
FhirContext ctx = FhirContext.forDstu3();
String output = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(output);
//START SNIPPET: parseExtension
// Get all extensions (modifier or not) for a given URL
List<Extension> resourceExts = patient.getExtensionsByUrl("http://fooextensions.com#exts");
// Get all non-modifier extensions regardless of URL
List<Extension> nonModExts = patient.getExtension();
//Get all non-modifier extensions regardless of URL
List<Extension> modExts = patient.getModifierExtension();
//END SNIPPET: parseExtension
}
public void foo() {
//START SNIPPET: subExtension
Patient patient = new Patient();
// Add an extension (initially with no contents) to the resource
Extension parent = new Extension("http://example.com#parent");
patient.addExtension(parent);
// Add two extensions as children to the parent extension
Extension child1 = new Extension("http://example.com#childOne", new StringType("value1"));
parent.addExtension(child1);
Extension child2 = new Extension("http://example.com#chilwo", new StringType("value1"));
parent.addExtension(child2);
//END SNIPPET: subExtension
}
}

View File

@ -1,137 +0,0 @@
package example;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.NameUseEnum;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
public class FhirContextIntro {
@SuppressWarnings("unused")
public static void creatingContext() {
// START SNIPPET: creatingContext
// Create a context for DSTU2
FhirContext ctxDstu2 = FhirContext.forDstu2();
// Alternately, create a context for R4
FhirContext ctxR4 = FhirContext.forR4();
// END SNIPPET: creatingContext
}
@SuppressWarnings("unused")
public static void creatingContextHl7org() {
// START SNIPPET: creatingContextHl7org
// Create a context for DSTU3
FhirContext ctx = FhirContext.forDstu3();
// Working with RI structures is similar to how it works with the HAPI structures
org.hl7.fhir.dstu3.model.Patient patient = new org.hl7.fhir.dstu3.model.Patient();
patient.addName().addGiven("John").setFamily("Smith");
patient.getBirthDateElement().setValueAsString("1998-02-22");
// Parsing and encoding works the same way too
String encoded = ctx.newJsonParser().encodeResourceToString(patient);
// END SNIPPET: creatingContextHl7org
}
public static void main(String[] args) throws DataFormatException {
new FhirContextIntro().encodeMsg();
}
public void encodeMsg() throws DataFormatException {
FhirContext ctx = new FhirContext(Patient.class, Observation.class);
//START SNIPPET: encodeMsg
/**
* FHIR model types in HAPI are simple POJOs. To create a new
* one, invoke the default constructor and then
* start populating values.
*/
Patient patient = new Patient();
// Add an MRN (a patient identifier)
IdentifierDt id = patient.addIdentifier();
id.setSystem("http://example.com/fictitious-mrns");
id.setValue("MRN001");
// Add a name
HumanNameDt name = patient.addName();
name.setUse(NameUseEnum.OFFICIAL);
name.addFamily("Tester");
name.addGiven("John");
name.addGiven("Q");
// We can now use a parser to encode this resource into a string.
String encoded = ctx.newXmlParser().encodeResourceToString(patient);
System.out.println(encoded);
//END SNIPPET: encodeMsg
//START SNIPPET: encodeMsgJson
IParser jsonParser = ctx.newJsonParser();
jsonParser.setPrettyPrint(true);
encoded = jsonParser.encodeResourceToString(patient);
System.out.println(encoded);
//END SNIPPET: encodeMsgJson
}
public void fluent() throws DataFormatException {
FhirContext ctx = new FhirContext(Patient.class, Observation.class);
String encoded;
//START SNIPPET: encodeMsgFluent
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://example.com/fictitious-mrns").setValue("MRN001");
patient.addName().setUse(NameUseEnum.OFFICIAL).addFamily("Tester").addGiven("John").addGiven("Q");
encoded = ctx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(encoded);
//END SNIPPET: encodeMsgFluent
}
public static void parseMsg() {
FhirContext ctx = FhirContext.forR4();
//START SNIPPET: parseMsg
// The following is an example Patient resource
String msgString = "<Patient xmlns=\"http://hl7.org/fhir\">"
+ "<text><status value=\"generated\" /><div xmlns=\"http://www.w3.org/1999/xhtml\">John Cardinal</div></text>"
+ "<identifier><system value=\"http://orionhealth.com/mrn\" /><value value=\"PRP1660\" /></identifier>"
+ "<name><use value=\"official\" /><family value=\"Cardinal\" /><given value=\"John\" /></name>"
+ "<gender><coding><system value=\"http://hl7.org/fhir/v3/AdministrativeGender\" /><code value=\"M\" /></coding></gender>"
+ "<address><use value=\"home\" /><line value=\"2222 Home Street\" /></address><active value=\"true\" />"
+ "</Patient>";
// The hapi context object is used to create a new XML parser
// instance. The parser can then be used to parse (or unmarshall) the
// string message into a Patient object
IParser parser = ctx.newXmlParser();
Patient patient = parser.parseResource(Patient.class, msgString);
// The patient object has accessor methods to retrieve all of the
// data which has been parsed into the instance.
String patientId = patient.getIdentifier().get(0).getValue();
String familyName = patient.getName().get(0).getFamily().get(0).getValue();
String gender = patient.getGender();
System.out.println(patientId); // PRP1660
System.out.println(familyName); // Cardinal
System.out.println(gender); // M
//END SNIPPET: parseMsg
}
}

View File

@ -1,224 +0,0 @@
package example;
import java.util.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.dstu2.composite.*;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.*;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.client.api.IGenericClient;
public class FhirDataModel {
public static void datatypes() {
// START SNIPPET: datatypes
Observation obs = new Observation();
// These are all equivalent
obs.setIssued(new InstantDt(new Date()));
obs.setIssued(new Date(), TemporalPrecisionEnum.MILLI);
obs.setIssuedWithMillisPrecision(new Date());
// The InstantDt also lets you work with the instant as a Java Date
// object or as a FHIR String.
Date date = obs.getIssuedElement().getValue(); // A date object
String dateString = obs.getIssuedElement().getValueAsString(); // "2014-03-08T12:59:58.068-05:00"
// END SNIPPET: datatypes
System.out.println(date);
System.out.println(dateString);
}
@SuppressWarnings("unused")
public void nonNull() {
// START SNIPPET: nonNull
Observation observation = new Observation();
// None of these calls will not return null, but instead create their
// respective
// child elements.
List<IdentifierDt> identifierList = observation.getIdentifier();
CodeableConceptDt code = observation.getCode();
StringDt textElement = observation.getCode().getTextElement();
// DateTimeDt is a FHIR primitive however, so the following will return
// null
// unless a value has been placed there.
Date active = observation.addIdentifier().getPeriod().getStartElement().getValue();
// END SNIPPET: nonNull
}
@SuppressWarnings("unused")
public static void codes() {
// START SNIPPET: codes
Patient patient = new Patient();
// You can set this code using a String if you want. Note that
// for "closed" valuesets (such as the one used for Patient.gender)
// you must use one of the strings defined by the FHIR specification.
// You must not define your own.
patient.getGenderElement().setValue("male");
// HAPI also provides Java enumerated types which make it easier to
// deal with coded values. This code achieves the exact same result
// as the code above.
patient.setGender(AdministrativeGenderEnum.MALE);
// You can also retrieve coded values the same way
String genderString = patient.getGenderElement().getValueAsString();
AdministrativeGenderEnum genderEnum = patient.getGenderElement().getValueAsEnum();
// The following is a shortcut to create
patient.setMaritalStatus(MaritalStatusCodesEnum.M);
// END SNIPPET: codes
}
@SuppressWarnings("unused")
public static void codeableConcepts() {
// START SNIPPET: codeableConcepts
Patient patient = new Patient();
// Coded types can naturally be set using plain strings
CodingDt statusCoding = patient.getMaritalStatus().addCoding();
statusCoding.setSystem("http://hl7.org/fhir/v3/MaritalStatus");
statusCoding.setCode("M");
statusCoding.setDisplay("Married");
// You could add a second coding to the field if needed too. This
// can be useful if you want to convey the concept using different
// codesystems.
CodingDt secondStatus = patient.getMaritalStatus().addCoding();
secondStatus.setCode("H");
secondStatus.setSystem("http://example.com#maritalStatus");
secondStatus.setDisplay("Happily Married");
// CodeableConcept also has a text field meant to convey
// a user readable version of the concepts it conveys.
patient.getMaritalStatus().setText("Happily Married");
// There are also accessors for retrieving values
String firstCode = patient.getMaritalStatus().getCoding().get(0).getCode();
String secondCode = patient.getMaritalStatus().getCoding().get(1).getCode();
// END SNIPPET: codeableConcepts
}
@SuppressWarnings("unused")
public static void codeableConceptEnums() {
// START SNIPPET: codeableConceptEnums
Patient patient = new Patient();
// Set the CodeableConcept's first coding to use the code
// and codesystem associated with the M value.
patient.setMaritalStatus(MaritalStatusCodesEnum.M);
// If you need to set other fields (such as the display name) after
// using the Enum type, you may still do so.
patient.getMaritalStatus().getCodingFirstRep().setDisplay("Married");
patient.getMaritalStatus().getCodingFirstRep().setVersion("1.0");
patient.getMaritalStatus().getCodingFirstRep().setUserSelected(true);
// You can use accessors to retrieve values from CodeableConcept fields
// Returns "M"
String code = patient.getMaritalStatus().getCodingFirstRep().getCode();
// Returns "http://hl7.org/fhir/v3/MaritalStatus". This value was also
// populated via the enum above.
String codeSystem = patient.getMaritalStatus().getCodingFirstRep().getCode();
// In many cases, Enum types can be used to retrieve values as well. Note that
// the setter takes a single type, but the getter returns a Set, because the
// field can technicaly contain more than one code and codesystem. BE CAREFUL
// when using this method however, as no Enum will be returned in the case
// that the field contains only a code other than the ones defined by the Enum.
Set<MaritalStatusCodesEnum> status = patient.getMaritalStatus().getValueAsEnum();
// END SNIPPET: codeableConceptEnums
}
public static void main(String[] args) {
tmp();
datatypes();
// START SNIPPET: observation
// Create an Observation instance
Observation observation = new Observation();
// Give the observation a status
observation.setStatus(ObservationStatusEnum.FINAL);
// Give the observation a code (what kind of observation is this)
CodingDt coding = observation.getCode().addCoding();
coding.setCode("29463-7").setSystem("http://loinc.org").setDisplay("Body Weight");
// Create a quantity datatype
QuantityDt value = new QuantityDt();
value.setValue(83.9).setSystem("http://unitsofmeasure.org").setCode("kg");
observation.setValue(value);
// Set the reference range
SimpleQuantityDt low = new SimpleQuantityDt();
low.setValue(45).setSystem("http://unitsofmeasure.org").setCode("kg");
observation.getReferenceRangeFirstRep().setLow(low);
SimpleQuantityDt high = new SimpleQuantityDt();
low.setValue(90).setSystem("http://unitsofmeasure.org").setCode("kg");
observation.getReferenceRangeFirstRep().setHigh(high);
// END SNIPPET: observation
}
private static void tmp() {
// Create a FHIR Context
FhirContext ctx = FhirContext.forDstu2();
// Create a client
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
// Read a patient with the given ID
Patient patient = client
.read()
.resource(Patient.class)
.withId("952975")
.execute();
// Print the patient's name
String string = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(string);
}
public void namesHard() {
// START SNIPPET: namesHard
Patient patient = new Patient();
HumanNameDt name = patient.addName();
StringDt family = name.addFamily();
family.setValue("Smith");
StringDt firstName = name.addGiven();
firstName.setValue("Rob");
StringDt secondName = name.addGiven();
secondName.setValue("Bruce");
// END SNIPPET: namesHard
}
public void namesEasy() {
// START SNIPPET: namesEasy
Patient patient = new Patient();
patient.addName().addFamily("Smith").addGiven("Rob").addGiven("Bruce");
// END SNIPPET: namesEasy
}
}

View File

@ -1,536 +0,0 @@
package example;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.PerformanceOptionsEnum;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.*;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome.Issue;
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.primitive.*;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
public class GenericClientExample {
public static void deferModelScanning() {
// START SNIPPET: deferModelScanning
// Create a context and configure it for deferred child scanning
FhirContext ctx = FhirContext.forDstu2();
ctx.setPerformanceOptions(PerformanceOptionsEnum.DEFERRED_MODEL_SCANNING);
// Now create a client and use it
String serverBase = "http://fhirtest.uhn.ca/baseDstu2";
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
// END SNIPPET: deferModelScanning
}
public static void performance() {
// START SNIPPET: dontValidate
// Create a context
FhirContext ctx = FhirContext.forDstu2();
// Disable server validation (don't pull the server's metadata first)
ctx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
// Now create a client and use it
String serverBase = "http://fhirtest.uhn.ca/baseDstu2";
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
// END SNIPPET: dontValidate
}
public static void simpleExample() {
// START SNIPPET: simple
// We're connecting to a DSTU1 compliant server in this example
FhirContext ctx = FhirContext.forDstu2();
String serverBase = "http://fhirtest.uhn.ca/baseDstu2";
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
// Perform a search
Bundle results = client
.search()
.forResource(Patient.class)
.where(Patient.FAMILY.matches().value("duck"))
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
System.out.println("Found " + results.getEntry().size() + " patients named 'duck'");
// END SNIPPET: simple
}
@SuppressWarnings("unused")
public static void fluentSearch() {
FhirContext ctx = FhirContext.forDstu2();
IGenericClient client = ctx.newRestfulGenericClient("http://fhir.healthintersections.com.au/open");
{
// START SNIPPET: create
Patient patient = new Patient();
// ..populate the patient object..
patient.addIdentifier().setSystem("urn:system").setValue("12345");
patient.addName().addFamily("Smith").addGiven("John");
// Invoke the server create method (and send pretty-printed JSON
// encoding to the server
// instead of the default which is non-pretty printed XML)
MethodOutcome outcome = client.create()
.resource(patient)
.prettyPrint()
.encodedJson()
.execute();
// The MethodOutcome object will contain information about the
// response from the server, including the ID of the created
// resource, the OperationOutcome response, etc. (assuming that
// any of these things were provided by the server! They may not
// always be)
IdDt id = (IdDt) outcome.getId();
System.out.println("Got ID: " + id.getValue());
// END SNIPPET: create
}
{
Patient patient = new Patient();
// START SNIPPET: createConditional
// One form
MethodOutcome outcome = client.create()
.resource(patient)
.conditionalByUrl("Patient?identifier=system%7C00001")
.execute();
// Another form
MethodOutcome outcome2 = client.create()
.resource(patient)
.conditional()
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001"))
.execute();
// This will return Boolean.TRUE if the server responded with an HTTP 201 created,
// otherwise it will return null.
Boolean created = outcome.getCreated();
// The ID of the created, or the pre-existing resource
IdDt id = (IdDt) outcome.getId();
// END SNIPPET: createConditional
}
{
// START SNIPPET: validate
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://hospital.com").setValue("123445");
patient.addName().addFamily("Smith").addGiven("John");
// Validate the resource
MethodOutcome outcome = client.validate()
.resource(patient)
.execute();
// The returned object will contain an operation outcome resource
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
// If the OperationOutcome has any issues with a severity of ERROR or SEVERE,
// the validation failed.
for (Issue nextIssue : oo.getIssue()) {
if (nextIssue.getSeverityElement().getValueAsEnum().ordinal() >= IssueSeverityEnum.ERROR.ordinal()) {
System.out.println("We failed validation!");
}
}
// END SNIPPET: validate
}
{
// START SNIPPET: update
Patient patient = new Patient();
// ..populate the patient object..
patient.addIdentifier().setSystem("urn:system").setValue("12345");
patient.addName().addFamily("Smith").addGiven("John");
// To update a resource, it should have an ID set (if the resource
// object
// comes from the results of a previous read or search, it will already
// have one though)
patient.setId("Patient/123");
// Invoke the server update method
MethodOutcome outcome = client.update()
.resource(patient)
.execute();
// The MethodOutcome object will contain information about the
// response from the server, including the ID of the created
// resource, the OperationOutcome response, etc. (assuming that
// any of these things were provided by the server! They may not
// always be)
IdDt id = (IdDt) outcome.getId();
System.out.println("Got ID: " + id.getValue());
// END SNIPPET: update
}
{
Patient patient = new Patient();
// START SNIPPET: updateConditional
client.update()
.resource(patient)
.conditionalByUrl("Patient?identifier=system%7C00001")
.execute();
client.update()
.resource(patient)
.conditional()
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001"))
.execute();
// END SNIPPET: updateConditional
}
{
// START SNIPPET: etagupdate
// First, let's retrive the latest version of a resource
// from the server
Patient patient = client.read().resource(Patient.class).withId("123").execute();
// If the server is a version aware server, we should now know the latest version
// of the resource
System.out.println("Version ID: " + patient.getId().getVersionIdPart());
// Now let's make a change to the resource
patient.setGender(AdministrativeGenderEnum.FEMALE);
// Invoke the server update method - Because the resource has
// a version, it will be included in the request sent to
// the server
try {
MethodOutcome outcome = client
.update()
.resource(patient)
.execute();
} catch (PreconditionFailedException e) {
// If we get here, the latest version has changed
// on the server so our update failed.
}
// END SNIPPET: etagupdate
}
{
// START SNIPPET: conformance
// Retrieve the server's conformance statement and print its
// description
Conformance conf = client.fetchConformance().ofType(Conformance.class).execute();
System.out.println(conf.getDescriptionElement().getValue());
// END SNIPPET: conformance
}
{
// START SNIPPET: delete
IBaseOperationOutcome resp = client.delete().resourceById(new IdDt("Patient", "1234")).execute();
// outcome may be null if the server didn't return one
if (resp != null) {
OperationOutcome outcome = (OperationOutcome) resp;
System.out.println(outcome.getIssueFirstRep().getDetailsElement().getValue());
}
// END SNIPPET: delete
}
{
// START SNIPPET: deleteConditional
client.delete()
.resourceConditionalByUrl("Patient?identifier=system%7C00001")
.execute();
client.delete()
.resourceConditionalByType("Patient")
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001"))
.execute();
// END SNIPPET: deleteConditional
}
{
// START SNIPPET: search
ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search()
.forResource(Patient.class)
.where(Patient.BIRTHDATE.beforeOrEquals().day("2011-01-01"))
.and(Patient.CAREPROVIDER.hasChainedProperty(Organization.NAME.matches().value("Health")))
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
// END SNIPPET: search
// START SNIPPET: searchOr
response = client.search()
.forResource(Patient.class)
.where(Patient.FAMILY.matches().values("Smith", "Smyth"))
.returnBundle(Bundle.class)
.execute();
// END SNIPPET: searchOr
// START SNIPPET: searchAnd
response = client.search()
.forResource(Patient.class)
.where(Patient.ADDRESS.matches().values("Toronto"))
.and(Patient.ADDRESS.matches().values("Ontario"))
.and(Patient.ADDRESS.matches().values("Canada"))
.returnBundle(Bundle.class)
.execute();
// END SNIPPET: searchAnd
// START SNIPPET: searchCompartment
response = client.search()
.forResource(Patient.class)
.withIdAndCompartment("123", "condition")
.where(Patient.ADDRESS.matches().values("Toronto"))
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
// END SNIPPET: searchCompartment
// START SNIPPET: searchUrl
String searchUrl = "http://example.com/base/Patient?identifier=foo";
// Search URL can also be a relative URL in which case the client's base
// URL will be added to it
searchUrl = "Patient?identifier=foo";
response = client.search()
.byUrl(searchUrl)
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
// END SNIPPET: searchUrl
// START SNIPPET: searchSubsetSummary
response = client.search()
.forResource(Patient.class)
.where(Patient.ADDRESS.matches().values("Toronto"))
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.summaryMode(SummaryEnum.TRUE)
.execute();
// END SNIPPET: searchSubsetSummary
// START SNIPPET: searchSubsetElements
response = client.search()
.forResource(Patient.class)
.where(Patient.ADDRESS.matches().values("Toronto"))
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.elementsSubset("identifier", "name") // only include the identifier and name
.execute();
// END SNIPPET: searchSubsetElements
// START SNIPPET: searchAdv
response = client.search()
.forResource(Patient.class)
.encodedJson()
.where(Patient.BIRTHDATE.beforeOrEquals().day("2012-01-22"))
.and(Patient.BIRTHDATE.after().day("2011-01-01"))
.withTag("http://acme.org/codes", "needs-review")
.include(Patient.INCLUDE_ORGANIZATION.asRecursive())
.include(Patient.INCLUDE_CAREPROVIDER.asNonRecursive())
.revInclude(Provenance.INCLUDE_TARGET)
.lastUpdated(new DateRangeParam("2011-01-01", null))
.sort().ascending(Patient.BIRTHDATE)
.sort().descending(Patient.NAME).limitTo(123)
.returnBundle(Bundle.class)
.execute();
// END SNIPPET: searchAdv
// START SNIPPET: searchPost
response = client.search()
.forResource("Patient")
.where(Patient.NAME.matches().value("Tester"))
.usingStyle(SearchStyleEnum.POST)
.returnBundle(Bundle.class)
.execute();
// END SNIPPET: searchPost
// START SNIPPET: searchComposite
response = client.search()
.forResource("Observation")
.where(Observation.CODE_VALUE_DATE
.withLeft(Observation.CODE.exactly().code("FOO$BAR"))
.withRight(Observation.VALUE_DATE.exactly().day("2001-01-01")))
.returnBundle(Bundle.class)
.execute();
// END SNIPPET: searchComposite
}
{
// START SNIPPET: transaction
List<IResource> resources = new ArrayList<IResource>();
// .. populate this list - note that you can also pass in a populated
// Bundle if you want to create one manually ..
List<IBaseResource> response = client.transaction().withResources(resources).execute();
// END SNIPPET: transaction
}
{
// START SNIPPET: read
// search for patient 123
Patient patient = client.read()
.resource(Patient.class)
.withId("123")
.execute();
// END SNIPPET: read
}
{
// START SNIPPET: vread
// search for patient 123 (specific version 888)
Patient patient = client.read()
.resource(Patient.class)
.withIdAndVersion("123", "888")
.execute();
// END SNIPPET: vread
}
{
// START SNIPPET: readabsolute
// search for patient 123 on example.com
String url = "http://example.com/fhir/Patient/123";
Patient patient = client.read()
.resource(Patient.class)
.withUrl(url)
.execute();
// END SNIPPET: readabsolute
}
{
// START SNIPPET: etagread
// search for patient 123
Patient patient = client.read()
.resource(Patient.class)
.withId("123")
.ifVersionMatches("001").returnNull()
.execute();
if (patient == null) {
// resource has not changed
}
// END SNIPPET: etagread
}
}
@SuppressWarnings("unused")
public static void history() {
IGenericClient client = FhirContext.forDstu2().newRestfulGenericClient("");
{
ca.uhn.fhir.model.dstu2.resource.Bundle response;
// START SNIPPET: historyDstu2
response = client
.history()
.onServer()
.andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
// END SNIPPET: historyDstu2
}
{
ca.uhn.fhir.model.dstu2.resource.Bundle response;
// START SNIPPET: historyFeatures
response = client
.history()
.onServer()
.andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.since(new InstantDt("2012-01-01T12:22:32.038Z"))
.count(100)
.execute();
// END SNIPPET: historyFeatures
}
}
public static void main(String[] args) {
paging();
}
private static void paging() {
{
// START SNIPPET: searchPaging
FhirContext ctx = FhirContext.forDstu2();
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
// Perform a search
Bundle resultBundle = client.search()
.forResource(Patient.class)
.where(Patient.NAME.matches().value("Smith"))
.returnBundle(Bundle.class)
.execute();
if (resultBundle.getLink(Bundle.LINK_NEXT) != null) {
// load next page
Bundle nextPage = client.loadPage().next(resultBundle).execute();
}
// END SNIPPET: searchPaging
}
}
@SuppressWarnings("unused")
private static void operationHttpGet() {
// START SNIPPET: operationHttpGet
// Create a client to talk to the HeathIntersections server
FhirContext ctx = FhirContext.forDstu2();
IGenericClient client = ctx.newRestfulGenericClient("http://fhir-dev.healthintersections.com.au/open");
client.registerInterceptor(new LoggingInterceptor(true));
// Create the input parameters to pass to the server
Parameters inParams = new Parameters();
inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01"));
inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01"));
// Invoke $everything on "Patient/1"
Parameters outParams = client
.operation()
.onInstance(new IdDt("Patient", "1"))
.named("$everything")
.withParameters(inParams)
.useHttpGet() // Use HTTP GET instead of POST
.execute();
// END SNIPPET: operationHttpGet
}
@SuppressWarnings("unused")
private static void operation() {
// START SNIPPET: operation
// Create a client to talk to the HeathIntersections server
FhirContext ctx = FhirContext.forDstu2();
IGenericClient client = ctx.newRestfulGenericClient("http://fhir-dev.healthintersections.com.au/open");
client.registerInterceptor(new LoggingInterceptor(true));
// Create the input parameters to pass to the server
Parameters inParams = new Parameters();
inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01"));
inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01"));
// Invoke $everything on "Patient/1"
Parameters outParams = client
.operation()
.onInstance(new IdDt("Patient", "1"))
.named("$everything")
.withParameters(inParams)
.execute();
/*
* Note that the $everything operation returns a Bundle instead
* of a Parameters resource. The client operation methods return a
* Parameters instance however, so HAPI creates a Parameters object
* with a single parameter containing the value.
*/
Bundle responseBundle = (Bundle) outParams.getParameter().get(0).getResource();
// Print the response bundle
System.out.println(ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(responseBundle));
// END SNIPPET: operation
}
@SuppressWarnings("unused")
private static void operationNoIn() {
// START SNIPPET: operationNoIn
// Create a client to talk to the HeathIntersections server
FhirContext ctx = FhirContext.forDstu2();
IGenericClient client = ctx.newRestfulGenericClient("http://fhir-dev.healthintersections.com.au/open");
client.registerInterceptor(new LoggingInterceptor(true));
// Invoke $everything on "Patient/1"
Parameters outParams = client
.operation()
.onInstance(new IdDt("Patient", "1"))
.named("$everything")
.withNoParameters(Parameters.class) // No input parameters
.execute();
// END SNIPPET: operationNoIn
}
}

View File

@ -1,61 +0,0 @@
package example;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.SearchParameter;
public class GenomicsUploader {
public static void main(String[] theArgs) {
FhirContext ctx = FhirContext.forR4();
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseR4");
client.registerInterceptor(new LoggingInterceptor(false));
SearchParameter dnaSequenceVariantName = new SearchParameter();
dnaSequenceVariantName.setId("SearchParameter/dnaSequenceVariantName");
dnaSequenceVariantName.setStatus(Enumerations.PublicationStatus.ACTIVE);
dnaSequenceVariantName.addBase("Observation");
dnaSequenceVariantName.setCode("dnaSequenceVariantName");
dnaSequenceVariantName.setType(Enumerations.SearchParamType.TOKEN);
dnaSequenceVariantName.setTitle("DNASequenceVariantName");
dnaSequenceVariantName.setExpression("Observation.extension('http://hl7.org/fhir/StructureDefinition/observation-geneticsDNASequenceVariantName')");
dnaSequenceVariantName.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
client.update().resource(dnaSequenceVariantName).execute();
SearchParameter dNAVariantId = new SearchParameter();
dNAVariantId.setId("SearchParameter/dNAVariantId");
dNAVariantId.setStatus(Enumerations.PublicationStatus.ACTIVE);
dNAVariantId.addBase("Observation");
dNAVariantId.setCode("dnaVariantId");
dNAVariantId.setType(Enumerations.SearchParamType.TOKEN);
dNAVariantId.setTitle("DNAVariantId");
dNAVariantId.setExpression("Observation.extension('http://hl7.org/fhir/StructureDefinition/observation-geneticsDNAVariantId')");
dNAVariantId.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
client.update().resource(dNAVariantId).execute();
SearchParameter gene = new SearchParameter();
gene.setId("SearchParameter/gene");
gene.setStatus(Enumerations.PublicationStatus.ACTIVE);
gene.addBase("Observation");
gene.setCode("gene");
gene.setType(Enumerations.SearchParamType.TOKEN);
gene.setTitle("Gene");
gene.setExpression("Observation.extension('http://hl7.org/fhir/StructureDefinition/observation-geneticsGene')");
gene.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
client.update().resource(gene).execute();
SearchParameter alleleName = new SearchParameter();
alleleName.setId("SearchParameter/alleleName");
alleleName.setStatus(Enumerations.PublicationStatus.ACTIVE);
alleleName.addBase("Observation");
alleleName.setCode("alleleName");
alleleName.setType(Enumerations.SearchParamType.TOKEN);
alleleName.setTitle("AlleleName");
alleleName.setExpression("Observation.extension('http://hl7.org/fhir/StructureDefinition/observation-geneticsAlleleName')");
alleleName.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
client.update().resource(alleleName).execute();
}
}

View File

@ -1,48 +0,0 @@
package example;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.client.api.IGenericClient;
public class HttpProxy {
public static void main(String[] args) {
/*
* This is out ot date - Just keeping
* it in case it's helpful...
*/
final String authUser = "username";
final String authPassword = "password";
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope("10.10.10.10", 8080),
new UsernamePasswordCredentials(authUser, authPassword));
HttpHost myProxy = new HttpHost("10.10.10.10", 8080);
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
clientBuilder
.setProxy(myProxy)
.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy())
.setDefaultCredentialsProvider(credsProvider)
.disableCookieManagement();
CloseableHttpClient httpClient = clientBuilder.build();
FhirContext ctx = FhirContext.forDstu2();
String serverBase = "http://spark.furore.com/fhir/";
ctx.getRestfulClientFactory().setHttpClient(httpClient);
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
IdDt id = new IdDt("Patient", "123");
client.read(Patient.class, id);
}
}

View File

@ -1,56 +0,0 @@
package example;
import java.util.List;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.client.api.IBasicClient;
//START SNIPPET: provider
/**
* All RESTful clients must be an interface which extends IBasicClient
*/
public interface IRestfulClient extends IBasicClient {
/**
* The "@Read" annotation indicates that this method supports the
* read operation. Read operations should return a single resource
* instance.
*
* @param theId
* The read operation takes one parameter, which must be of type
* IdDt and must be annotated with the "@Read.IdParam" annotation.
* @return
* Returns a resource matching this identifier, or null if none exists.
*/
@Read()
public Patient getResourceById(@IdParam IdDt theId);
/**
* The "@Search" annotation indicates that this method supports the
* search operation. You may have many different methods annotated with
* this annotation, to support many different search criteria. This
* example searches by family name.
*
* @param theIdentifier
* This operation takes one parameter which is the search criteria. It is
* annotated with the "@Required" annotation. This annotation takes one argument,
* a string containing the name of the search criteria. The datatype here
* is StringDt, but there are other possible parameter types depending on the
* specific search criteria.
* @return
* This method returns a list of Patients. This list may contain multiple
* matching resources, or it may also be empty.
*/
@Search()
public List<Patient> getPatient(@RequiredParam(name = Patient.SP_FAMILY) StringDt theFamilyName);
}
//END SNIPPET: provider

View File

@ -1,58 +0,0 @@
package example;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.resource.Organization;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.server.provider.dstu2.Dstu2BundleFactory;
public class IncludesExamples {
public static void main(String[] args) {
testSearchForPatients();
}
private static void testSearchForPatients() {
List<IBaseResource> resources = new IncludesExamples().searchForPatients();
// Create a bundle with both
FhirContext ctx = FhirContext.forDstu2();
Dstu2BundleFactory bf = new Dstu2BundleFactory(ctx);
bf.addRootPropertiesToBundle(null, null, null, null, null, resources.size(), BundleTypeEnum.SEARCHSET, null);
bf.addResourcesToBundle(new ArrayList<>(resources), BundleTypeEnum.SEARCHSET, null, null, null);
IBaseResource b = bf.getResourceBundle();
// Encode the bundle
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(b);
System.out.println(encoded);
}
// START SNIPPET: addIncludes
@Search
private List<IBaseResource> searchForPatients() {
// Create an organization
Organization org = new Organization();
org.setId("Organization/65546");
org.setName("Test Organization");
// Create a patient
Patient patient = new Patient();
patient.setId("Patient/1333");
patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
patient.getManagingOrganization().setResource(org);
// Here we return only the patient object, which has links to other resources
List<IBaseResource> retVal = new ArrayList<IBaseResource>();
retVal.add(patient);
return retVal;
}
// END SNIPPET: addIncludes
}

View File

@ -1,28 +0,0 @@
package example;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.client.JaxRsRestfulClientFactory;
import ca.uhn.fhir.rest.client.api.IGenericClient;
@SuppressWarnings(value= {"serial"})
public class JaxRsClient {
public static void main(String[] args) {
//START SNIPPET: createClient
// Create a client
FhirContext ctx = FhirContext.forDstu2();
// Create an instance of the JAX RS client factory and
// set it on the context
JaxRsRestfulClientFactory clientFactory = new JaxRsRestfulClientFactory(ctx);
ctx.setRestfulClientFactory(clientFactory);
// This client uses JAX-RS!
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
//END SNIPPET: createClient
}
}

View File

@ -1,41 +0,0 @@
package example;
import java.util.concurrent.ConcurrentHashMap;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.IResourceProvider;
/**
* Conformance Rest Service
*
* @author Peter Van Houte
*/
// START SNIPPET: jax-rs-conformance
@Path("")
@Stateless
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
public class JaxRsConformanceProvider extends AbstractJaxRsConformanceProvider {
@EJB
private JaxRsPatientRestProvider provider;
public JaxRsConformanceProvider() {
super("My Server Description", "My Server Name", "My Server Version");
}
@Override
protected ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders() {
ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> map = new ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider>();
map.put(JaxRsConformanceProvider.class, this);
map.put(JaxRsPatientRestProvider.class, provider);
return map;
}
}
// END SNIPPET: jax-rs-conformance

View File

@ -1,68 +0,0 @@
package example;
import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.*;
/**
* A demo JaxRs Patient Rest Provider
*/
@Local
@Stateless
// START SNIPPET: jax-rs-provider-construction
@Path("/Patient")
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider<Patient> {
public JaxRsPatientRestProvider() {
super(JaxRsPatientRestProvider.class);
}
// END SNIPPET: jax-rs-provider-construction
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Create
public MethodOutcome create(@ResourceParam final Patient patient, @ConditionalUrlParam String theConditional) {
// create the patient ...
return new MethodOutcome(new IdDt(1L)).setCreated(true);
}
// START SNIPPET: jax-rs-provider-operation
@GET
@Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingGet(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.GET, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@Operation(name = "someCustomOperation", idempotent = true, returnParameters = {
@OperationParam(name = "return", type = StringDt.class) })
public Parameters someCustomOperation(@IdParam IdDt myId, @OperationParam(name = "dummy") StringDt dummyInput) {
Parameters parameters = new Parameters();
parameters.addParameter().setName("return").setValue(new StringDt("My Dummy Result"));
return parameters;
}
// END SNIPPET: jax-rs-provider-operation
@POST
@Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingPost(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.POST, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
}

View File

@ -1,50 +0,0 @@
package example;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Patient;
public class Multitenancy {
//START SNIPPET: enableUrlBaseTenantIdentificationStrategy
public class MyServer extends RestfulServer {
@Override
protected void initialize() {
setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy());
// ... do other initialization ...
}
}
//END SNIPPET: enableUrlBaseTenantIdentificationStrategy
//START SNIPPET: resourceProvider
public class MyPatientResourceProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Read
public Patient read(RequestDetails theRequestDetails, @IdParam IdType theId) {
String tenantId = theRequestDetails.getTenantId();
String resourceId = theId.getIdPart();
// Use these two values to fetch the patient
return new Patient();
}
}
//END SNIPPET: resourceProvider
}

View File

@ -1,99 +0,0 @@
package example;
//START SNIPPET: patientDef
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.StringType;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.util.ElementUtil;
/**
* Definition class for adding extensions to the built-in
* Patient resource type.
*
* Note the "profile" attribute below, which indicates the URL/ID of the
* profile implemented by this resource. You are not required to supply this,
* but if you do it will be automatically populated in the resource meta
* tag if the resource is returned by a server.
*/
@ResourceDef(name="Patient", profile="http://example.com/StructureDefinition/mypatient")
public class MyPatient extends Patient {
private static final long serialVersionUID = 1L;
/**
* Each extension is defined in a field. Any valid HAPI Data Type
* can be used for the field type. Note that the [name=""] attribute
* in the @Child annotation needs to match the name for the bean accessor
* and mutator methods.
*/
@Child(name="petName")
@Extension(url="http://example.com/dontuse#petname", definedLocally=false, isModifier=false)
@Description(shortDefinition="The name of the patient's favourite pet")
private StringType myPetName;
/**
* The second example extension uses a List type to provide
* repeatable values. Note that a [max=] value has been placed in
* the @Child annotation.
*
* Note also that this extension is a modifier extension
*/
@Child(name="importantDates", max=Child.MAX_UNLIMITED)
@Extension(url="http://example.com/dontuse#importantDates", definedLocally=false, isModifier=true)
@Description(shortDefinition="Some dates of note for this patient")
private List<DateTimeType> myImportantDates;
/**
* It is important to override the isEmpty() method, adding a check for any
* newly added fields.
*/
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(myPetName, myImportantDates);
}
/********
* Accessors and mutators follow
*
* IMPORTANT:
* Each extension is required to have an getter/accessor and a stter/mutator.
* You are highly recommended to create getters which create instances if they
* do not already exist, since this is how the rest of the HAPI FHIR API works.
********/
/** Getter for important dates */
public List<DateTimeType> getImportantDates() {
if (myImportantDates==null) {
myImportantDates = new ArrayList<DateTimeType>();
}
return myImportantDates;
}
/** Getter for pet name */
public StringType getPetName() {
if (myPetName == null) {
myPetName = new StringType();
}
return myPetName;
}
/** Setter for important dates */
public void setImportantDates(List<DateTimeType> theImportantDates) {
myImportantDates = theImportantDates;
}
/** Setter for pet name */
public void setPetName(StringType thePetName) {
myPetName = thePetName;
}
}
//END SNIPPET: patientDef

View File

@ -1,86 +0,0 @@
package example;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
public class MyPatientUse {
@ResourceDef()
public static class MyPatient extends Patient {
@Child(name="petName")
@Extension(url="http://example.com/dontuse#petname", definedLocally=false, isModifier=false)
@Description(shortDefinition="The name of the patient's favourite pet")
private StringDt myPetName;
public StringDt getPetName() {
if(myPetName==null) {
myPetName = new StringDt();
}
return myPetName;
}
public void setPetName(StringDt thePetName) {
myPetName = thePetName;
}
public List<DateTimeDt> getImportantDates() {
if (myImportantDates==null) {
myImportantDates=new ArrayList<DateTimeDt>();
}
return myImportantDates;
}
public void setImportantDates(List<DateTimeDt> theImportantDates) {
myImportantDates = theImportantDates;
}
@Child(name="importantDates", max=Child.MAX_UNLIMITED)
@Extension(url="http://example.com/dontuse#importantDates", definedLocally=false, isModifier=true)
@Description(shortDefinition="Some dates of note for the patient")
private List<DateTimeDt> myImportantDates;
}
@SuppressWarnings("unused")
public static void main(String[] args) throws DataFormatException, IOException {
//START SNIPPET: patientUse
MyPatient patient = new MyPatient();
patient.setPetName(new StringDt("Fido"));
patient.getImportantDates().add(new DateTimeDt("2010-01-02"));
patient.getImportantDates().add(new DateTimeDt("2014-01-26T11:11:11"));
patient.addName().addFamily("Smith").addGiven("John").addGiven("Quincy").addSuffix("Jr");
IParser p = FhirContext.forDstu2().newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
System.out.println(messageString);
//END SNIPPET: patientUse
//START SNIPPET: patientParse
IParser parser = FhirContext.forDstu2().newXmlParser();
MyPatient newPatient = parser.parseResource(MyPatient.class, messageString);
//END SNIPPET: patientParse
{
FhirContext ctx2 = FhirContext.forDstu2();
RuntimeResourceDefinition def = ctx2.getResourceDefinition(patient);
System.out.println(ctx2.newXmlParser().setPrettyPrint(true).encodeResourceToString(def.toProfile()));
}
}
}

View File

@ -1,40 +0,0 @@
package example;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.NarrativeStatusEnum;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.parser.DataFormatException;
@SuppressWarnings("unused")
public class Narrative {
public static void main(String[] args) throws DataFormatException {
//START SNIPPET: example1
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:foo").setValue("7000135");
patient.addName().addFamily("Smith").addGiven("John").addGiven("Edward");
patient.addAddress().addLine("742 Evergreen Terrace").setCity("Springfield").setState("ZZ");
FhirContext ctx = FhirContext.forDstu2();
// Use the narrative generator
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
// Encode the output, including the narrative
String output = ctx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(output);
//END SNIPPET: example1
}
public void simple() {
//START SNIPPET: simple
Patient pat = new Patient();
pat.getText().setStatus(NarrativeStatusEnum.GENERATED);
pat.getText().setDiv("<div>This is the narrative text<br/>this is line 2</div>");
//END SNIPPET: simple
}
}

View File

@ -1,21 +0,0 @@
package example;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.narrative.CustomThymeleafNarrativeGenerator;
@SuppressWarnings("unused")
public class NarrativeGenerator {
public void testGenerator() {
//START SNIPPET: gen
FhirContext ctx = FhirContext.forDstu2();
String propFile = "classpath:/com/foo/customnarrative.properties";
CustomThymeleafNarrativeGenerator gen = new CustomThymeleafNarrativeGenerator(propFile);
ctx.setNarrativeGenerator(gen);
//END SNIPPET: gen
}
}

View File

@ -1,4 +0,0 @@
package example;
public class NewInterceptors {
}

View File

@ -1,88 +0,0 @@
package example;
import java.util.List;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
import javax.annotation.Nonnull;
@SuppressWarnings("null")
// START SNIPPET: provider
public class PagingPatientProvider implements IResourceProvider {
/**
* Search for Patient resources matching a given family name
*/
@Search
public IBundleProvider search(@RequiredParam(name = Patient.SP_FAMILY) StringParam theFamily) {
final InstantDt searchTime = InstantDt.withCurrentTime();
/**
* First, we'll search the database for a set of database row IDs that
* match the given search criteria. That way we can keep just the row IDs
* around, and load the actual resources on demand later as the client
* pages through them.
*/
final List<Long> matchingResourceIds = null; // <-- implement this
/**
* Return a bundle provider which can page through the IDs and return the
* resources that go with them.
*/
return new IBundleProvider() {
@Override
public Integer size() {
return matchingResourceIds.size();
}
@Nonnull
@Override
public List<IBaseResource> getResources(int theFromIndex, int theToIndex) {
int end = Math.max(theToIndex, matchingResourceIds.size() - 1);
List<Long> idsToReturn = matchingResourceIds.subList(theFromIndex, end);
return loadResourcesByIds(idsToReturn);
}
@Override
public InstantDt getPublished() {
return searchTime;
}
@Override
public Integer preferredPageSize() {
// Typically this method just returns null
return null;
}
@Override
public String getUuid() {
return null;
}
};
}
/**
* Load a list of patient resources given their IDs
*/
private List<IBaseResource> loadResourcesByIds(List<Long> theIdsToReturn) {
// .. implement this search against the database ..
return null;
}
@Override
public Class<? extends IResource> getResourceType() {
return Patient.class;
}
}
// END SNIPPET: provider

View File

@ -1,33 +0,0 @@
package example;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
@SuppressWarnings({ "serial" })
//START SNIPPET: provider
public class PagingServer extends RestfulServer {
public PagingServer() {
/*
* Set the resource providers as always. Here we are using the paging
* provider from the example below, but it is not strictly neccesary
* to use a paging resource provider as well. If a normal resource
* provider is used (one which returns List<?> instead of IBundleProvider)
* then the loaded resources will be stored by the IPagingProvider.
*/
setResourceProviders(new PagingPatientProvider());
/*
* Set a paging provider. Here a simple in-memory implementation
* is used, but you may create your own.
*/
FifoMemoryPagingProvider pp = new FifoMemoryPagingProvider(10);
pp.setDefaultPageSize(10);
pp.setMaximumPageSize(100);
setPagingProvider(pp);
}
}
//END SNIPPET: provider

View File

@ -1,42 +0,0 @@
package example;
import java.io.IOException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
public class Parser {
public static void main(String[] args) throws DataFormatException, IOException {
{
//START SNIPPET: disableStripVersions
FhirContext ctx = FhirContext.forDstu2();
IParser parser = ctx.newJsonParser();
// Disable the automatic stripping of versions from references on the parser
parser.setStripVersionsFromReferences(false);
//END SNIPPET: disableStripVersions
//START SNIPPET: disableStripVersionsCtx
ctx.getParserOptions().setStripVersionsFromReferences(false);
//END SNIPPET: disableStripVersionsCtx
}
{
//START SNIPPET: disableStripVersionsField
FhirContext ctx = FhirContext.forDstu2();
IParser parser = ctx.newJsonParser();
// Preserve versions only on these two fields (for the given parser)
parser.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference", "Patient.managingOrganization");
// You can also apply this setting to the context so that it will
// flow to all parsers
ctx.getParserOptions().setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference", "Patient.managingOrganization");
//END SNIPPET: disableStripVersionsField
}
}
}

View File

@ -1,30 +0,0 @@
package example;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.OperationOutcome;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.PatchTypeEnum;
public class PatchExamples {
//START SNIPPET: patch
@Patch
public OperationOutcome patientPatch(@IdParam IdType theId, PatchTypeEnum thePatchType, @ResourceParam String theBody) {
if (thePatchType == PatchTypeEnum.JSON_PATCH) {
// do something
}
if (thePatchType == PatchTypeEnum.XML_PATCH) {
// do something
}
OperationOutcome retVal = new OperationOutcome();
retVal.getText().setDivAsString("<div>OK</div>");
return retVal;
}
//END SNIPPET: patch
}

View File

@ -1,54 +0,0 @@
package example;
import java.io.IOException;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
public class QuickUsage {
@SuppressWarnings("unused")
public static void main(String[] args) throws DataFormatException, IOException {
Patient patient = new Patient();
patient.addIdentifier().setUse(IdentifierUseEnum.OFFICIAL).setSystem("urn:fake:mrns").setValue("7000135");
patient.addIdentifier().setUse(IdentifierUseEnum.SECONDARY).setSystem("urn:fake:otherids").setValue("3287486");
patient.addName().addFamily("Smith").addGiven("John").addGiven("Q").addSuffix("Junior");
patient.setGender(AdministrativeGenderEnum.MALE);
FhirContext ctx = FhirContext.forDstu2();
String xmlEncoded = ctx.newXmlParser().encodeResourceToString(patient);
String jsonEncoded = ctx.newJsonParser().encodeResourceToString(patient);
MyClientInterface client = ctx.newRestfulClient(MyClientInterface.class, "http://foo/fhir");
IdentifierDt searchParam = new IdentifierDt("urn:someidentifiers", "7000135");
List<Patient> clients = client.findPatientsByIdentifier(searchParam);
}
public interface MyClientInterface extends IRestfulClient
{
/** A FHIR search */
@Search
public List<Patient> findPatientsByIdentifier(@RequiredParam(name="identifier") IdentifierDt theIdentifier);
/** A FHIR create */
@Create
public MethodOutcome createPatient(@ResourceParam Patient thePatient);
}
}

View File

@ -1,29 +0,0 @@
package example;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
//START SNIPPET: interceptor
public class RequestCounterInterceptor extends InterceptorAdapter
{
private int myRequestCount;
public int getRequestCount() {
return myRequestCount;
}
/**
* Override the incomingRequestPreProcessed method, which is called
* for each incoming request before any processing is done
*/
@Override
public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse) {
myRequestCount++;
return true;
}
}
//END SNIPPET: interceptor

View File

@ -1,36 +0,0 @@
package example;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
//START SNIPPET: interceptor
public class RequestExceptionInterceptor extends InterceptorAdapter
{
@Override
public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest,
HttpServletResponse theServletResponse) throws ServletException, IOException {
// HAPI's server exceptions know what the appropriate HTTP status code is
theServletResponse.setStatus(theException.getStatusCode());
// Provide a response ourself
theServletResponse.setContentType("text/plain");
theServletResponse.getWriter().append("Failed to process!");
theServletResponse.getWriter().close();
// Since we handled this response in the interceptor, we must return false
// to stop processing immediately
return false;
}
}
//END SNIPPET: interceptor

View File

@ -1,36 +0,0 @@
package example;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.resource.Organization;
import ca.uhn.fhir.model.dstu2.resource.Patient;
public class ResourceRefs {
private static FhirContext ourCtx = FhirContext.forDstu2();
public static void main(String[] args) {
manualContained();
}
public static void manualContained() {
// START SNIPPET: manualContained
// Create an organization, and give it a local ID
Organization org = new Organization();
org.setId("#localOrganization");
org.getNameElement().setValue("Contained Test Organization");
// Create a patient
Patient patient = new Patient();
patient.setId("Patient/1333");
patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
// Set the reference, and manually add the contained resource
patient.getManagingOrganization().setReference("#localOrganization");
patient.getContained().getContainedResources().add(org);
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(encoded);
// END SNIPPET: manualContained
}
}

View File

@ -1,87 +0,0 @@
package example;
import java.util.Collections;
import java.util.List;
import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.server.IResourceProvider;
//START SNIPPET: provider
/**
* All resource providers must implement IResourceProvider
*/
public class RestfulObservationResourceProvider implements IResourceProvider {
/**
* The getResourceType method comes from IResourceProvider, and must
* be overridden to indicate what type of resource this provider
* supplies.
*/
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
/**
* The "@Read" annotation indicates that this method supports the
* read operation. It takes one argument, the Resource type being returned.
*
* @param theId
* The read operation takes one parameter, which must be of type
* IdDt and must be annotated with the "@Read.IdParam" annotation.
* @return
* Returns a resource matching this identifier, or null if none exists.
*/
@Read()
public Patient getResourceById(@IdParam IdDt theId) {
Patient patient = new Patient();
patient.addIdentifier();
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
patient.getIdentifier().get(0).setValue("00002");
patient.addName().addFamily("Test");
patient.getName().get(0).addGiven("PatientOne");
patient.setGender(AdministrativeGenderEnum.FEMALE);
return patient;
}
/**
* The "@Search" annotation indicates that this method supports the
* search operation. You may have many different methods annotated with
* this annotation, to support many different search criteria. This
* example searches by family name.
*
* @param theIdentifier
* This operation takes one parameter which is the search criteria. It is
* annotated with the "@Required" annotation. This annotation takes one argument,
* a string containing the name of the search criteria. The datatype here
* is StringDt, but there are other possible parameter types depending on the
* specific search criteria.
* @return
* This method returns a list of Patients. This list may contain multiple
* matching resources, or it may also be empty.
*/
@Search()
public List<Patient> getPatient(@RequiredParam(name = Patient.SP_FAMILY) StringDt theFamilyName) {
Patient patient = new Patient();
patient.addIdentifier();
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
patient.getIdentifier().get(0).setValue("00001");
patient.addName();
patient.getName().get(0).addFamily("Test");
patient.getName().get(0).addGiven("PatientOne");
patient.setGender(AdministrativeGenderEnum.MALE);
return Collections.singletonList(patient);
}
}
//END SNIPPET: provider

View File

@ -1,90 +0,0 @@
package example;
import java.util.Collections;
import java.util.List;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
//START SNIPPET: provider
/**
* All resource providers must implement IResourceProvider
*/
public class RestfulPatientResourceProvider implements IResourceProvider {
/**
* The getResourceType method comes from IResourceProvider, and must
* be overridden to indicate what type of resource this provider
* supplies.
*/
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
/**
* The "@Read" annotation indicates that this method supports the
* read operation. Read operations should return a single resource
* instance.
*
* @param theId
* The read operation takes one parameter, which must be of type
* IdDt and must be annotated with the "@Read.IdParam" annotation.
* @return
* Returns a resource matching this identifier, or null if none exists.
*/
@Read()
public Patient getResourceById(@IdParam IdDt theId) {
Patient patient = new Patient();
patient.addIdentifier();
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
patient.getIdentifier().get(0).setValue("00002");
patient.addName().addFamily("Test");
patient.getName().get(0).addGiven("PatientOne");
patient.setGender(AdministrativeGenderEnum.FEMALE);
return patient;
}
/**
* The "@Search" annotation indicates that this method supports the
* search operation. You may have many different methods annotated with
* this annotation, to support many different search criteria. This
* example searches by family name.
*
* @param theFamilyName
* This operation takes one parameter which is the search criteria. It is
* annotated with the "@Required" annotation. This annotation takes one argument,
* a string containing the name of the search criteria. The datatype here
* is StringParam, but there are other possible parameter types depending on the
* specific search criteria.
* @return
* This method returns a list of Patients. This list may contain multiple
* matching resources, or it may also be empty.
*/
@Search()
public List<Patient> getPatient(@RequiredParam(name = Patient.SP_FAMILY) StringParam theFamilyName) {
Patient patient = new Patient();
patient.addIdentifier();
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
patient.getIdentifier().get(0).setValue("00001");
patient.addName();
patient.getName().get(0).addFamily(theFamilyName.getValue());
patient.getName().get(0).addGiven("PatientOne");
patient.setGender(AdministrativeGenderEnum.MALE);
return Collections.singletonList(patient);
}
}
//END SNIPPET: provider

View File

@ -1,66 +0,0 @@
package example;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
public class SecurityInterceptors {
// START SNIPPET: basicAuthInterceptor
public class BasicSecurityInterceptor extends InterceptorAdapter
{
/**
* This interceptor implements HTTP Basic Auth, which specifies that
* a username and password are provided in a header called Authorization.
*/
@Override
public boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
String authHeader = theRequest.getHeader("Authorization");
// The format of the header must be:
// Authorization: Basic [base64 of username:password]
if (authHeader == null || authHeader.startsWith("Basic ") == false) {
throw new AuthenticationException("Missing or invalid Authorization header");
}
String base64 = authHeader.substring("Basic ".length());
String base64decoded = new String(Base64.decodeBase64(base64));
String[] parts = base64decoded.split("\\:");
String username = parts[0];
String password = parts[1];
/*
* Here we test for a hardcoded username & password. This is
* not typically how you would implement this in a production
* system of course..
*/
if (!username.equals("someuser") || !password.equals("thepassword")) {
throw new AuthenticationException("Invalid username or password");
}
// Return true to allow the request to proceed
return true;
}
}
//END SNIPPET: basicAuthInterceptor
public void basicAuthInterceptorRealm() {
//START SNIPPET: basicAuthInterceptorRealm
AuthenticationException ex = new AuthenticationException();
ex.addAuthenticateHeaderForRealm("myRealm");
throw ex;
//END SNIPPET: basicAuthInterceptorRealm
}
}

View File

@ -1,29 +0,0 @@
package example;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import ca.uhn.fhir.rest.server.ETagSupportEnum;
import ca.uhn.fhir.rest.server.RestfulServer;
@SuppressWarnings("serial")
public class ServerETagExamples {
// START SNIPPET: disablingETags
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
public class RestfulServerWithLogging extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ... define your resource providers here ...
// ETag support is enabled by default
setETagSupport(ETagSupportEnum.ENABLED);
}
}
// END SNIPPET: disablingETags
}

View File

@ -1,33 +0,0 @@
package example;
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public abstract class ServerExceptionsExample implements IResourceProvider {
private boolean databaseIsDown;
//START SNIPPET: returnOO
@Read
public Patient read(@IdParam IdDt theId) {
if (databaseIsDown) {
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setSeverity(IssueSeverityEnum.FATAL).setDetails("Database is down");
throw new InternalErrorException("Database is down", oo);
}
Patient patient = new Patient(); // populate this
return patient;
}
//END SNIPPET: returnOO
}

View File

@ -1,80 +0,0 @@
package example;
import java.io.IOException;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum;
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException;
public class ServerInterceptors {
@SuppressWarnings("unused")
public static void main(String[] args) throws DataFormatException, IOException {
// START SNIPPET: resourceExtension
// Create an example patient
Patient patient = new Patient();
patient.addIdentifier().setUse(IdentifierUseEnum.OFFICIAL).setSystem("urn:example").setValue("7000135");
// Create an extension
ExtensionDt ext = new ExtensionDt();
ext.setModifier(false);
ext.setUrl("http://example.com/extensions#someext");
ext.setValue(new DateTimeDt("2011-01-02T11:13:15"));
// Add the extension to the resource
patient.addUndeclaredExtension(ext);
//END SNIPPET: resourceExtension
//START SNIPPET: resourceStringExtension
HumanNameDt name = patient.addName();
name.addFamily().setValue("Shmoe");
StringDt given = name.addGiven();
given.setValue("Joe");
ExtensionDt ext2 = new ExtensionDt(false, "http://examples.com#moreext", new StringDt("Hello"));
given.addUndeclaredExtension(ext2);
//END SNIPPET: resourceStringExtension
String output = FhirContext.forDstu2().newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(output);
//START SNIPPET: parseExtension
// Get all extensions (modifier or not) for a given URL
List<ExtensionDt> resourceExts = patient.getUndeclaredExtensionsByUrl("http://fooextensions.com#exts");
// Get all non-modifier extensions regardless of URL
List<ExtensionDt> nonModExts = patient.getUndeclaredExtensions();
//Get all non-modifier extensions regardless of URL
List<ExtensionDt> modExts = patient.getUndeclaredModifierExtensions();
//END SNIPPET: parseExtension
}
public void foo() {
//START SNIPPET: subExtension
Patient patient = new Patient();
ExtensionDt parent = new ExtensionDt(false, "http://example.com#parent");
patient.addUndeclaredExtension(parent);
ExtensionDt child1 = new ExtensionDt(false, "http://example.com#childOne", new StringDt("value1"));
parent.addUndeclaredExtension(child1);
ExtensionDt child2 = new ExtensionDt(false, "http://example.com#childTwo", new StringDt("value1"));
parent.addUndeclaredExtension(child2);
//END SNIPPET: subExtension
}
}

View File

@ -1,41 +0,0 @@
package example;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.rest.annotation.Search;
import org.hl7.fhir.r4.model.InstantType;
import org.hl7.fhir.r4.model.Patient;
import java.util.ArrayList;
import java.util.List;
public class ServerMetadataExamples {
// START SNIPPET: serverMethod
@Search
public List<Patient> getAllPatients() {
ArrayList<Patient> retVal = new ArrayList<Patient>();
// Create a patient to return
Patient patient = new Patient();
retVal.add(patient);
patient.setId("Patient/123");
patient.addName().setFamily("Smith").addGiven("John");
// Add tags
patient.getMeta().addTag()
.setSystem(Tag.HL7_ORG_FHIR_TAG)
.setCode("some_tag")
.setDisplay("Some tag");
patient.getMeta().addTag()
.setSystem(Tag.HL7_ORG_FHIR_TAG)
.setCode("another_tag")
.setDisplay("Another tag");
// Set the last updated date
patient.getMeta().setLastUpdatedElement(new InstantType("2011-02-22T11:22:00.0122Z"));
return retVal;
}
// END SNIPPET: serverMethod
}

View File

@ -1,115 +0,0 @@
package example;
import java.io.IOException;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.model.Parameters;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.ConceptMap;
import ca.uhn.fhir.model.primitive.DateDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServerOperations {
private static final Logger ourLog = LoggerFactory.getLogger(ServerOperations.class);
//START SNIPPET: manualInputAndOutput
@Operation(name="$manualInputAndOutput", manualResponse=true, manualRequest=true)
public void manualInputAndOutput(HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws IOException {
String contentType = theServletRequest.getContentType();
byte[] bytes = IOUtils.toByteArray(theServletRequest.getInputStream());
ourLog.info("Received call with content type {} and {} bytes", contentType, bytes.length);
// In a real example we might do something more interesting with the received bytes,
// here we'll just replace them with hardcoded ones
bytes = new byte[] { 0, 1, 2, 3 };
theServletResponse.setContentType(contentType);
theServletResponse.getOutputStream().write(bytes);
theServletResponse.getOutputStream().close();
}
//END SNIPPET: manualInputAndOutput
//START SNIPPET: searchParamBasic
@Operation(name="$find-matches", idempotent=true)
public Parameters findMatchesBasic(
@OperationParam(name="date") DateParam theDate,
@OperationParam(name="code") TokenParam theCode) {
Parameters retVal = new Parameters();
// Populate bundle with matching resources
return retVal;
}
//END SNIPPET: searchParamBasic
//START SNIPPET: searchParamAdvanced
@Operation(name="$find-matches", idempotent=true)
public Parameters findMatchesAdvanced(
@OperationParam(name="dateRange") DateRangeParam theDate,
@OperationParam(name="name") List<StringParam> theName,
@OperationParam(name="code") TokenAndListParam theEnd) {
Parameters retVal = new Parameters();
// Populate bundle with matching resources
return retVal;
}
//END SNIPPET: searchParamAdvanced
//START SNIPPET: patientTypeOperation
@Operation(name="$everything", idempotent=true)
public Bundle patientTypeOperation(
@OperationParam(name="start") DateDt theStart,
@OperationParam(name="end") DateDt theEnd) {
Bundle retVal = new Bundle();
// Populate bundle with matching resources
return retVal;
}
//END SNIPPET: patientTypeOperation
//START SNIPPET: patientInstanceOperation
@Operation(name="$everything", idempotent=true)
public Bundle patientInstanceOperation(
@IdParam IdDt thePatientId,
@OperationParam(name="start") DateDt theStart,
@OperationParam(name="end") DateDt theEnd) {
Bundle retVal = new Bundle();
// Populate bundle with matching resources
return retVal;
}
//END SNIPPET: patientInstanceOperation
//START SNIPPET: serverOperation
@Operation(name="$closure")
public ConceptMap closureOperation(
@OperationParam(name="name") StringDt theStart,
@OperationParam(name="concept") List<CodingDt> theEnd,
@OperationParam(name="version") IdDt theVersion) {
ConceptMap retVal = new ConceptMap();
// Populate bundle with matching resources
return retVal;
}
//END SNIPPET: serverOperation
}

View File

@ -1,155 +0,0 @@
package example;
import java.util.Arrays;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator;
import org.springframework.web.cors.CorsConfiguration;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.interceptor.*;
import ca.uhn.fhir.validation.ResultSeverityEnum;
@SuppressWarnings("serial")
public class ServletExamples {
// START SNIPPET: loggingInterceptor
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
public class RestfulServerWithLogging extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ... define your resource providers here ...
// Now register the logging interceptor
LoggingInterceptor loggingInterceptor = new LoggingInterceptor();
registerInterceptor(loggingInterceptor);
// The SLF4j logger "test.accesslog" will receive the logging events
loggingInterceptor.setLoggerName("test.accesslog");
// This is the format for each line. A number of substitution variables may
// be used here. See the JavaDoc for LoggingInterceptor for information on
// what is available.
loggingInterceptor.setMessageFormat("Source[${remoteAddr}] Operation[${operationType} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}]");
}
}
// END SNIPPET: loggingInterceptor
// START SNIPPET: validatingInterceptor
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
public class ValidatingServerWithLogging extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ... define your resource providers here ...
// Create an interceptor to validate incoming requests
RequestValidatingInterceptor requestInterceptor = new RequestValidatingInterceptor();
// Register a validator module (you could also use SchemaBaseValidator and/or SchematronBaseValidator)
requestInterceptor.addValidatorModule(new FhirInstanceValidator());
requestInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
requestInterceptor.setAddResponseHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
requestInterceptor.setResponseHeaderValue("Validation on ${line}: ${message} ${severity}");
requestInterceptor.setResponseHeaderValueNoIssues("No issues detected");
// Now register the validating interceptor
registerInterceptor(requestInterceptor);
// Create an interceptor to validate responses
// This is configured in the same way as above
ResponseValidatingInterceptor responseInterceptor = new ResponseValidatingInterceptor();
responseInterceptor.addValidatorModule(new FhirInstanceValidator());
responseInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
responseInterceptor.setAddResponseHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
responseInterceptor.setResponseHeaderValue("Validation on ${line}: ${message} ${severity}");
responseInterceptor.setResponseHeaderValueNoIssues("No issues detected");
registerInterceptor(responseInterceptor);
}
}
// END SNIPPET: validatingInterceptor
// START SNIPPET: exceptionInterceptor
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
public class RestfulServerWithExceptionHandling extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ... define your resource providers here ...
// Now register the interceptor
ExceptionHandlingInterceptor interceptor = new ExceptionHandlingInterceptor();
registerInterceptor(interceptor);
// Return the stack trace to the client for the following exception types
interceptor.setReturnStackTracesForExceptionTypes(InternalErrorException.class, NullPointerException.class);
}
}
// END SNIPPET: exceptionInterceptor
// START SNIPPET: responseHighlighterInterceptor
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
public class RestfulServerWithResponseHighlighter extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ... define your resource providers here ...
// Now register the interceptor
ResponseHighlighterInterceptor interceptor = new ResponseHighlighterInterceptor();
registerInterceptor(interceptor);
}
}
// END SNIPPET: responseHighlighterInterceptor
// START SNIPPET: corsInterceptor
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
public class RestfulServerWithCors extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ... define your resource providers here ...
// Define your CORS configuration. This is an example
// showing a typical setup. You should customize this
// to your specific needs
CorsConfiguration config = new CorsConfiguration();
config.addAllowedHeader("x-fhir-starter");
config.addAllowedHeader("Origin");
config.addAllowedHeader("Accept");
config.addAllowedHeader("X-Requested-With");
config.addAllowedHeader("Content-Type");
config.addAllowedOrigin("*");
config.addExposedHeader("Location");
config.addExposedHeader("Content-Location");
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"));
// Create the interceptor and register it
CorsInterceptor interceptor = new CorsInterceptor(config);
registerInterceptor(interceptor);
}
}
// END SNIPPET: corsInterceptor
}

View File

@ -1,69 +0,0 @@
package example;
import java.util.ArrayList;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.client.api.IGenericClient;
public class TagsExamples {
public static void main(String[] args) {
new TagsExamples().getResourceTags();
}
@SuppressWarnings("unused")
public void getResourceTags() {
// START SNIPPET: getResourceTags
IGenericClient client = FhirContext.forDstu2().newRestfulGenericClient("http://fhir.healthintersections.com.au/open");
Patient p = client.read(Patient.class, "1");
// Retrieve the list of tags from the resource metadata
TagList tags = ResourceMetadataKeyEnum.TAG_LIST.get(p);
// tags may be null if no tags were read in
if (tags == null) {
System.out.println("No tags!");
} else {
// You may iterate over all the tags
for (Tag next : tags) {
System.out.println(next.getScheme() + " - " + next.getTerm());
}
// You may also get a list of tags matching a given scheme
List<Tag> someTags = tags.getTagsWithScheme("http://hl7.org/fhir/tag");
// Or a specific tag (by scheme and term)
Tag specificTag = tags.getTag("http://hl7.org/fhir/tag", "http://foo");
}
// END SNIPPET: getResourceTags
}
// START SNIPPET: serverMethod
@Search
public List<Patient> getAllPatients() {
ArrayList<Patient> retVal = new ArrayList<Patient>();
// Create a patient to return
Patient patient = new Patient();
patient.setId("Patient/123");
patient.addName().addFamily("Smith").addGiven("John");
// Create a tag list and add it to the resource
TagList tags = new TagList();
ResourceMetadataKeyEnum.TAG_LIST.put(patient, tags);
// Add some tags to the list
tags.addTag(Tag.HL7_ORG_FHIR_TAG, "http://foo/tag1.html", "Some tag");
tags.addTag(Tag.HL7_ORG_FHIR_TAG, "http://foo/tag2.html", "Another tag");
return retVal;
}
// END SNIPPET: serverMethod
}

View File

@ -1,105 +0,0 @@
package example;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.File;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.hapi.validation.PrePopulatedValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
public class ValidateDirectory {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateDirectory.class);
public static void main(String[] args) throws Exception {
// Load all profiles in this directory
File profileDirectory = new File("/tmp/directory/with/profiles");
// Validate resources in this directory
File resourceDirectory = new File("/tmp/directory/with/resources/to/validate");
FhirContext ctx = FhirContext.forDstu3();
IParser xmlParser = ctx.newXmlParser();
IParser jsonParser = ctx.newJsonParser();
Map<String, StructureDefinition> structureDefinitions = new HashMap<String, StructureDefinition>();
Map<String, CodeSystem> codeSystems = new HashMap<String, CodeSystem>();
Map<String, ValueSet> valueSets = new HashMap<String, ValueSet>();
// Load all profile files
for (File nextFile : profileDirectory.listFiles()) {
IBaseResource parsedRes = null;
if (nextFile.getAbsolutePath().toLowerCase().endsWith(".xml")) {
parsedRes = xmlParser.parseResource(new FileReader(nextFile));
} else if (nextFile.getAbsolutePath().toLowerCase().endsWith(".json")) {
parsedRes = jsonParser.parseResource(new FileReader(nextFile));
} else {
ourLog.info("Ignoring file: {}", nextFile.getName());
}
if (parsedRes instanceof StructureDefinition) {
StructureDefinition res = (StructureDefinition) parsedRes;
if (isNotBlank(res.getUrl())) {
structureDefinitions.put(res.getUrl(), res);
}
} else if (parsedRes instanceof ValueSet) {
ValueSet res = (ValueSet) parsedRes;
if (isNotBlank(res.getUrl())) {
valueSets.put(res.getUrl(), res);
}
} else if (parsedRes instanceof CodeSystem) {
CodeSystem res = (CodeSystem) parsedRes;
if (isNotBlank(res.getUrl())) {
codeSystems.put(res.getUrl(), res);
}
}
}
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
ValidationSupportChain validationSupportChain = new ValidationSupportChain();
validationSupportChain.addValidationSupport(new DefaultProfileValidationSupport());
validationSupportChain.addValidationSupport(new PrePopulatedValidationSupport(structureDefinitions, valueSets, codeSystems));
instanceValidator.setValidationSupport(validationSupportChain);
FhirValidator val = ctx.newValidator();
val.registerValidatorModule(instanceValidator);
// Loop through the files in the validation directory and validate each one
for (File nextFile : resourceDirectory.listFiles()) {
if (nextFile.getAbsolutePath().toLowerCase().endsWith(".xml")) {
ourLog.info("Going to validate: {}", nextFile.getName());
} else if (nextFile.getAbsolutePath().toLowerCase().endsWith(".json")) {
ourLog.info("Going to validate: {}", nextFile.getName());
} else {
ourLog.info("Ignoring file: {}", nextFile.getName());
continue;
}
String input = IOUtils.toString(new FileReader(nextFile));
ValidationResult result = val.validateWithResult(input);
IBaseOperationOutcome oo = result.toOperationOutcome();
ourLog.info("Result:\n{}", xmlParser.setPrettyPrint(true).encodeResourceToString(oo));
}
}
}

View File

@ -1,43 +0,0 @@
package example;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import java.io.FileReader;
public class ValidateSimple {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateSimple.class);
public static void main(String[] args) throws Exception {
FhirContext ctx = FhirContext.forR4();
// Create a validator module
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
// We'll create a validation chain with only the DefaultProfileValidationupport registered
ValidationSupportChain validationSupportChain = new ValidationSupportChain();
validationSupportChain.addValidationSupport(new DefaultProfileValidationSupport());
instanceValidator.setValidationSupport(validationSupportChain);
// Create a validator and register the InstanceValidator module
FhirValidator val = ctx.newValidator();
val.registerValidatorModule(instanceValidator);
// Read in the file and validate it
String nextFile = args[0];
try (FileReader fileReader = new FileReader(nextFile)) {
String input = IOUtils.toString(fileReader);
ValidationResult result = val.validateWithResult(input);
IBaseOperationOutcome oo = result.toOperationOutcome();
ourLog.info("Result:\n{}", ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(oo));
}
}
}

View File

@ -1,331 +0,0 @@
package example;
import java.io.File;
import java.io.FileReader;
import java.util.List;
import javax.servlet.ServletException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.*;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.ContactPoint.ContactPointSystem;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.validation.*;
import ca.uhn.fhir.validation.schematron.SchematronBaseValidator;
@SuppressWarnings("serial")
public class ValidatorExamples {
public void validationIntro() {
// START SNIPPET: validationIntro
FhirContext ctx = FhirContext.forDstu3();
// Ask the context for a validator
FhirValidator validator = ctx.newValidator();
// Create some modules and register them
IValidatorModule module1 = new SchemaBaseValidator(ctx);
validator.registerValidatorModule(module1);
IValidatorModule module2 = new SchematronBaseValidator(ctx);
validator.registerValidatorModule(module2);
// Pass a resource in to be validated. The resource can
// be an IBaseResource instance, or can be a raw String
// containing a serialized resource as text.
Patient resource = new Patient();
ValidationResult result = validator.validateWithResult(resource);
String resourceText = "<Patient.....>";
ValidationResult result2 = validator.validateWithResult(resourceText);
// The result object now contains the validation results
for (SingleValidationMessage next : result.getMessages()) {
System.out.println(next.getLocationString() + " " + next.getMessage());
}
// END SNIPPET: validationIntro
}
// START SNIPPET: serverValidation
public class MyRestfulServer extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ...Configure resource providers, etc...
// Create a context, set the error handler and instruct
// the server to use it
FhirContext ctx = FhirContext.forDstu3();
ctx.setParserErrorHandler(new StrictErrorHandler());
setFhirContext(ctx);
}
}
// END SNIPPET: serverValidation
@SuppressWarnings("unused")
public void enableValidation() {
// START SNIPPET: clientValidation
FhirContext ctx = FhirContext.forDstu3();
ctx.setParserErrorHandler(new StrictErrorHandler());
// This client will have strict parser validation enabled
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu3");
// END SNIPPET: clientValidation
}
public void parserValidation() {
// START SNIPPET: parserValidation
FhirContext ctx = FhirContext.forDstu3();
// Create a parser and configure it to use the strict error handler
IParser parser = ctx.newXmlParser();
parser.setParserErrorHandler(new StrictErrorHandler());
// This example resource is invalid, as Patient.active can not repeat
String input = "<Patient><active value=\"true\"/><active value=\"false\"/></Patient>";
// The following will throw a DataFormatException because of the StrictErrorHandler
parser.parseResource(Patient.class, input);
// END SNIPPET: parserValidation
}
public void validateResource() {
// START SNIPPET: basicValidation
// As always, you need a context
FhirContext ctx = FhirContext.forDstu3();
// Create and populate a new patient object
Patient p = new Patient();
p.addName().setFamily("Smith").addGiven("John").addGiven("Q");
p.addIdentifier().setSystem("urn:foo:identifiers").setValue("12345");
p.addTelecom().setSystem(ContactPointSystem.PHONE).setValue("416 123-4567");
// Request a validator and apply it
FhirValidator val = ctx.newValidator();
// Create the Schema/Schematron modules and register them. Note that
// you might want to consider keeping these modules around as long-term
// objects: they parse and then store schemas, which can be an expensive
// operation.
IValidatorModule module1 = new SchemaBaseValidator(ctx);
IValidatorModule module2 = new SchematronBaseValidator(ctx);
val.registerValidatorModule(module1);
val.registerValidatorModule(module2);
ValidationResult result = val.validateWithResult(p);
if (result.isSuccessful()) {
System.out.println("Validation passed");
} else {
// We failed validation!
System.out.println("Validation failed");
}
// The result contains a list of "messages"
List<SingleValidationMessage> messages = result.getMessages();
for (SingleValidationMessage next : messages) {
System.out.println("Message:");
System.out.println(" * Location: " + next.getLocationString());
System.out.println(" * Severity: " + next.getSeverity());
System.out.println(" * Message : " + next.getMessage());
}
// You can also convert the results into an OperationOutcome resource
OperationOutcome oo = (OperationOutcome) result.toOperationOutcome();
String results = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(oo);
System.out.println(results);
// END SNIPPET: basicValidation
}
public static void main(String[] args) throws Exception {
instanceValidator();
}
private static void instanceValidator() throws Exception {
// START SNIPPET: instanceValidator
FhirContext ctx = FhirContext.forDstu3();
// Create a FhirInstanceValidator and register it to a validator
FhirValidator validator = ctx.newValidator();
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
validator.registerValidatorModule(instanceValidator);
/*
* If you want, you can configure settings on the validator to adjust
* its behaviour during validation
*/
instanceValidator.setAnyExtensionsAllowed(true);
/*
* Let's create a resource to validate. This Observation has some fields
* populated, but it is missing Observation.status, which is mandatory.
*/
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://loinc.org").setCode("12345-6");
obs.setValue(new StringType("This is a value"));
// Validate
ValidationResult result = validator.validateWithResult(obs);
/*
* Note: You can also explicitly declare a profile to validate against
* using the block below.
*/
// ValidationResult result = validator.validateWithResult(obs, new ValidationOptions().addProfile("http://myprofile.com"));
// Do we have any errors or fatal errors?
System.out.println(result.isSuccessful()); // false
// Show the issues
for (SingleValidationMessage next : result.getMessages()) {
System.out.println(" Next issue " + next.getSeverity() + " - " + next.getLocationString() + " - " + next.getMessage());
}
// Prints:
// Next issue ERROR - /f:Observation - Element '/f:Observation.status': minimum required = 1, but only found 0
// Next issue WARNING - /f:Observation/f:code - Unable to validate code "12345-6" in code system "http://loinc.org"
// You can also convert the result into an operation outcome if you
// need to return one from a server
OperationOutcome oo = (OperationOutcome) result.toOperationOutcome();
// END SNIPPET: instanceValidator
}
private static void instanceValidatorCustom() throws Exception {
// START SNIPPET: instanceValidatorCustom
FhirContext ctx = FhirContext.forDstu3();
// Create a FhirInstanceValidator and register it to a validator
FhirValidator validator = ctx.newValidator();
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
validator.registerValidatorModule(instanceValidator);
IValidationSupport valSupport = new IValidationSupport() {
@Override
public org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent expandValueSet(FhirContext theContext, org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent theInclude) {
// TODO: implement
return null;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
// TODO: implement
return null;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
// TODO: implement
return null;
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
// TODO: implement
return null;
}
@Override
public ValueSet fetchValueSet(FhirContext theContext, String theSystem) {
// TODO: implement
return null;
}
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
// TODO: implement
return null;
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
// TODO: implement
return null;
}
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
// TODO: implement
return false;
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSet) {
// TODO: implement
return null;
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
// TODO: implement
return null;
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
// TODO: implement
return null;
}
};
/*
* ValidationSupportChain strings multiple instances of IValidationSupport together. The
* code below is useful because it means that when the validator wants to load a
* StructureDefinition or a ValueSet, it will first use DefaultProfileValidationSupport,
* which loads the default HL7 versions. Any StructureDefinitions which are not found in
* the built-in set are delegated to your custom implementation.
*/
ValidationSupportChain support = new ValidationSupportChain(new DefaultProfileValidationSupport(), valSupport);
instanceValidator.setValidationSupport(support);
// END SNIPPET: instanceValidatorCustom
}
@SuppressWarnings("unused")
private static void validateFiles() throws Exception {
// START SNIPPET: validateFiles
FhirContext ctx = FhirContext.forDstu3();
// Create a validator and configure it
FhirValidator validator = ctx.newValidator();
validator.setValidateAgainstStandardSchema(true);
validator.setValidateAgainstStandardSchematron(true);
// Get a list of files in a given directory
String[] fileList = new File("/home/some/dir").list(new WildcardFileFilter("*.txt"));
for (String nextFile : fileList) {
// For each file, load the contents into a string
String nextFileContents = IOUtils.toString(new FileReader(nextFile));
// Parse that string (this example assumes JSON encoding)
IBaseResource resource = ctx.newJsonParser().parseResource(nextFileContents);
// Apply the validation. This will throw an exception on the first
// validation failure
ValidationResult result = validator.validateWithResult(resource);
if (result.isSuccessful() == false) {
throw new Exception("We failed!");
}
}
// END SNIPPET: validateFiles
}
}

View File

@ -1,49 +0,0 @@
package example;
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.FhirValidator;
public class ValidatorExamplesDstu3 {
public void validateProfileDstu3() {
// START SNIPPET: validateFiles
FhirContext ctx = FhirContext.forDstu3();
FhirValidator validator = ctx.newValidator();
// Typically if you are doing profile validation, you want to disable
// the schema/schematron validation since the profile will specify
// all the same rules (and more)
validator.setValidateAgainstStandardSchema(false);
validator.setValidateAgainstStandardSchematron(false);
// FhirInstanceValidator is the validation module that handles
// profile validation. So, create an InstanceValidator module
// and register it to the validator.
FhirInstanceValidator instanceVal = new FhirInstanceValidator();
validator.registerValidatorModule(instanceVal);
// FhirInstanceValidator requires an instance of "IValidationSupport" in
// order to function. This module is used by the validator to actually obtain
// all of the resources it needs in order to perform validation. Specifically,
// the validator uses it to fetch StructureDefinitions, ValueSets, CodeSystems,
// etc, as well as to perform terminology validation.
//
// The implementation used here (ValidationSupportChain) is allows for
// multiple implementations to be used in a chain, where if a specific resource
// is needed the whole chain is tried and the first module which is actually
// able to answer is used. The first entry in the chain that we register is
// the DefaultProfileValidationSupport, which supplies the "built-in" FHIR
// StructureDefinitions and ValueSets
ValidationSupportChain validationSupportChain = new ValidationSupportChain();
validationSupportChain.addValidationSupport(new DefaultProfileValidationSupport());
instanceVal.setValidationSupport(validationSupportChain);
// END SNIPPET: validateFiles
}
}

View File

@ -1,84 +0,0 @@
package example.customtype;
import org.hl7.fhir.dstu3.model.BackboneElement;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.StringType;
import ca.uhn.fhir.model.api.annotation.Block;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.util.ElementUtil;
//START SNIPPET: resource
@ResourceDef(name = "Patient")
public class CustomCompositeExtension extends Patient {
private static final long serialVersionUID = 1L;
/**
* A custom extension
*/
@Child(name = "foo")
@Extension(url="http://acme.org/fooParent", definedLocally = false, isModifier = false)
protected FooParentExtension fooParentExtension;
public FooParentExtension getFooParentExtension() {
return fooParentExtension;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(fooParentExtension);
}
public void setFooParentExtension(FooParentExtension theFooParentExtension) {
fooParentExtension = theFooParentExtension;
}
@Block
public static class FooParentExtension extends BackboneElement {
private static final long serialVersionUID = 4522090347756045145L;
@Child(name = "childA")
@Extension(url = "http://acme.org/fooChildA", definedLocally = false, isModifier = false)
private StringType myChildA;
@Child(name = "childB")
@Extension(url = "http://acme.org/fooChildB", definedLocally = false, isModifier = false)
private StringType myChildB;
@Override
public FooParentExtension copy() {
FooParentExtension copy = new FooParentExtension();
copy.myChildA = myChildA;
copy.myChildB = myChildB;
return copy;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(myChildA, myChildB);
}
public StringType getChildA() {
return myChildA;
}
public StringType getChildB() {
return myChildB;
}
public void setChildA(StringType theChildA) {
myChildA = theChildA;
}
public void setChildB(StringType theChildB) {
myChildB = theChildB;
}
}
}
//END SNIPPET: resource

View File

@ -1,63 +0,0 @@
package example.customtype;
//START SNIPPET: datatype
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.Type;
import org.hl7.fhir.instance.model.api.ICompositeType;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.util.ElementUtil;
/**
* This is an example of a custom datatype.
*
* This is an STU3 example so it extends Type and implements ICompositeType. For
* DSTU2 it would extend BaseIdentifiableElement and implement ICompositeDatatype.
*/
@DatatypeDef(name="CustomDatatype")
public class CustomDatatype extends Type implements ICompositeType {
private static final long serialVersionUID = 1L;
@Child(name = "date", order = 0, min = 1, max = 1)
private DateTimeType myDate;
@Child(name = "kittens", order = 1, min = 1, max = 1)
private StringType myKittens;
public DateTimeType getDate() {
if (myDate == null)
myDate = new DateTimeType();
return myDate;
}
public StringType getKittens() {
return myKittens;
}
@Override
public boolean isEmpty() {
return ElementUtil.isEmpty(myDate, myKittens);
}
public CustomDatatype setDate(DateTimeType theValue) {
myDate = theValue;
return this;
}
public CustomDatatype setKittens(StringType theKittens) {
myKittens = theKittens;
return this;
}
@Override
protected CustomDatatype typedCopy() {
CustomDatatype retVal = new CustomDatatype();
super.copyValues(retVal);
retVal.myDate = myDate;
return retVal;
}
}
//END SNIPPET: datatype

View File

@ -1,86 +0,0 @@
package example.customtype;
// START SNIPPET: resource
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.dstu3.model.DomainResource;
import org.hl7.fhir.dstu3.model.ResourceType;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.Type;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.util.ElementUtil;
/**
* This is an example of a custom resource that also uses a custom
* datatype.
*
* Note that we are extensing DomainResource for an STU3
* resource. For DSTU2 it would be BaseResource.
*/
@ResourceDef(name = "CustomResource", profile = "http://hl7.org/fhir/profiles/custom-resource")
public class CustomResource extends DomainResource {
private static final long serialVersionUID = 1L;
/**
* We give the resource a field with name "television". This field has no
* specific type, so it's a choice[x] field for any type.
*/
@Child(name="television", min=1, max=Child.MAX_UNLIMITED, order=0)
private List<Type> myTelevision;
/**
* We'll give it one more field called "dogs"
*/
@Child(name = "dogs", min=0, max=1, order=1)
private StringType myDogs;
@Override
public CustomResource copy() {
CustomResource retVal = new CustomResource();
super.copyValues(retVal);
retVal.myTelevision = myTelevision;
retVal.myDogs = myDogs;
return retVal;
}
public List<Type> getTelevision() {
if (myTelevision == null) {
myTelevision = new ArrayList<Type>();
}
return myTelevision;
}
public StringType getDogs() {
return myDogs;
}
@Override
public ResourceType getResourceType() {
return null;
}
@Override
public FhirVersionEnum getStructureFhirVersionEnum() {
return FhirVersionEnum.DSTU3;
}
@Override
public boolean isEmpty() {
return ElementUtil.isEmpty(myTelevision, myDogs);
}
public void setTelevision(List<Type> theValue) {
this.myTelevision = theValue;
}
public void setDogs(StringType theDogs) {
myDogs = theDogs;
}
}
// END SNIPPET: resource

View File

@ -1,44 +0,0 @@
package example.customtype;
import java.util.Date;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.DateType;
import org.hl7.fhir.dstu3.model.StringType;
import ca.uhn.fhir.context.FhirContext;
public class CustomUsage {
public static void main(String[] args) {
// START SNIPPET: usage
// Create a context. Note that we declare the custom types we'll be using
// on the context before actually using them
FhirContext ctx = FhirContext.forDstu3();
ctx.registerCustomType(CustomResource.class);
ctx.registerCustomType(CustomDatatype.class);
// Now let's create an instance of our custom resource type
// and populate it with some data
CustomResource res = new CustomResource();
// Add some values, including our custom datatype
DateType value0 = new DateType("2015-01-01");
res.getTelevision().add(value0);
CustomDatatype value1 = new CustomDatatype();
value1.setDate(new DateTimeType(new Date()));
value1.setKittens(new StringType("FOO"));
res.getTelevision().add(value1);
res.setDogs(new StringType("Some Dogs"));
// Now let's serialize our instance
String output = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
System.out.println(output);
// END SNIPPET: usage
}
}

View File

@ -1,25 +0,0 @@
package example.interceptor;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription;
import ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage;
/**
* Interceptor class
*/
@Interceptor
public class MyTestInterceptor {
@Hook(Pointcut.SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY)
public boolean beforeRestHookDelivery(ResourceDeliveryMessage theDeliveryMessage, CanonicalSubscription theSubscription) {
String header = "Authorization: Bearer 1234567";
theSubscription.addHeader(header);
return true;
}
}

View File

@ -1,16 +0,0 @@
<configuration scan="true" scanPeriod="30 seconds">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>4.3.0-SNAPSHOT</version>
<version>5.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -181,7 +181,16 @@
</reports>
<configuration>
<links>
<link>http://jamesagnew.github.io/hapi-fhir/apidocs/</link>
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-base</link>
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-dstu2</link>
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-dstu3</link>
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-r4</link>
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-r5</link>
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-client</link>
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-server</link>
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-jpaserver-base</link>
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-converter</link>
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-validation</link>
<link>https://docs.oracle.com/javaee/7/api/</link>
</links>
<additionalparam>-Xdoclint:none</additionalparam>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>4.3.0-SNAPSHOT</version>
<version>5.0.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>4.3.0-SNAPSHOT</version>
<version>5.0.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -20,8 +20,8 @@
<!-- JSON -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- XML -->

View File

@ -22,8 +22,11 @@ package ca.uhn.fhir.context;
import ca.uhn.fhir.util.UrlUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.util.*;
@ -33,11 +36,11 @@ public abstract class BaseRuntimeElementDefinition<T extends IBase> {
private final Class<? extends T> myImplementingClass;
private final String myName;
private final boolean myStandardType;
private Map<Class<?>, Constructor<T>> myConstructors = Collections.synchronizedMap(new HashMap<Class<?>, Constructor<T>>());
private List<RuntimeChildDeclaredExtensionDefinition> myExtensions = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsModifier = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsNonModifier = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToExtension = new HashMap<String, RuntimeChildDeclaredExtensionDefinition>();
private Map<Class<?>, Constructor<T>> myConstructors = Collections.synchronizedMap(new HashMap<>());
private List<RuntimeChildDeclaredExtensionDefinition> myExtensions = new ArrayList<>();
private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsModifier = new ArrayList<>();
private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsNonModifier = new ArrayList<>();
private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToExtension = new HashMap<>();
private BaseRuntimeElementDefinition<?> myRootParentDefinition;
public BaseRuntimeElementDefinition(String theName, Class<? extends T> theImplementingClass, boolean theStandardType) {
@ -56,19 +59,17 @@ public abstract class BaseRuntimeElementDefinition<T extends IBase> {
myImplementingClass = theImplementingClass;
}
public void addExtension(RuntimeChildDeclaredExtensionDefinition theExtension) {
if (theExtension == null) {
throw new NullPointerException();
}
public void addExtension(@Nonnull RuntimeChildDeclaredExtensionDefinition theExtension) {
Validate.notNull(theExtension, "theExtension must not be null");
myExtensions.add(theExtension);
}
public abstract ChildTypeEnum getChildType();
@SuppressWarnings("unchecked")
private Constructor<T> getConstructor(Object theArgument) {
private Constructor<T> getConstructor(@Nullable Object theArgument) {
Class<? extends Object> argumentType;
Class<?> argumentType;
if (theArgument == null) {
argumentType = VOID_CLASS;
} else {

View File

@ -1,15 +1,22 @@
package ca.uhn.fhir.context;
import ca.uhn.fhir.context.api.AddProfileTagEnum;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.fluentpath.IFluentPath;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.fhirpath.IFhirPath;
import ca.uhn.fhir.i18n.HapiLocalizer;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IFhirVersion;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.view.ViewGenerator;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.parser.*;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.IParserErrorHandler;
import ca.uhn.fhir.parser.JsonParser;
import ca.uhn.fhir.parser.LenientErrorHandler;
import ca.uhn.fhir.parser.RDFParser;
import ca.uhn.fhir.parser.XmlParser;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IGenericClient;
@ -20,7 +27,6 @@ import ca.uhn.fhir.util.ReflectionUtil;
import ca.uhn.fhir.util.VersionUtil;
import ca.uhn.fhir.validation.FhirValidator;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.jena.riot.Lang;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBundle;
@ -97,7 +103,7 @@ public class FhirContext {
private Collection<Class<? extends IBaseResource>> myResourceTypesToScan;
private volatile IRestfulClientFactory myRestfulClientFactory;
private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
private IContextValidationSupport<?, ?, ?, ?, ?, ?> myValidationSupport;
private IValidationSupport myValidationSupport;
private Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> myVersionToNameToResourceType = Collections.emptyMap();
/**
@ -371,15 +377,27 @@ public class FhirContext {
}
}
/**
* Sets the configured performance options
*
* @see PerformanceOptionsEnum for a list of available options
*/
public void setPerformanceOptions(final PerformanceOptionsEnum... thePerformanceOptions) {
Collection<PerformanceOptionsEnum> asList = null;
if (thePerformanceOptions != null) {
asList = Arrays.asList(thePerformanceOptions);
}
setPerformanceOptions(asList);
}
/**
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library.
*/
public RuntimeResourceDefinition getResourceDefinition(final Class<? extends IBaseResource> theResourceType) {
validateInitialized();
if (theResourceType == null) {
throw new NullPointerException("theResourceType can not be null");
}
Validate.notNull(theResourceType, "theResourceType can not be null");
if (Modifier.isAbstract(theResourceType.getModifiers())) {
throw new IllegalArgumentException("Can not scan abstract or interface class (resource definitions must be concrete classes): " + theResourceType.getName());
}
@ -544,16 +562,37 @@ public class FhirContext {
/**
* Returns the validation support module configured for this context, creating a default
* implementation if no module has been passed in via the {@link #setValidationSupport(IContextValidationSupport)}
* implementation if no module has been passed in via the {@link #setValidationSupport(IValidationSupport)}
* method
*
* @see #setValidationSupport(IContextValidationSupport)
* @see #setValidationSupport(IValidationSupport)
*/
public IContextValidationSupport<?, ?, ?, ?, ?, ?> getValidationSupport() {
if (myValidationSupport == null) {
myValidationSupport = myVersion.createValidationSupport();
public IValidationSupport getValidationSupport() {
IValidationSupport retVal = myValidationSupport;
if (retVal == null) {
retVal = new DefaultProfileValidationSupport(this);
/*
* If hapi-fhir-validation is on the classpath, we can create a much more robust
* validation chain using the classes found in that package
*/
String inMemoryTermSvcType = "org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport";
String commonCodeSystemsSupportType = "org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService";
if (ReflectionUtil.typeExists(inMemoryTermSvcType)) {
IValidationSupport inMemoryTermSvc = ReflectionUtil.newInstanceOrReturnNull(inMemoryTermSvcType, IValidationSupport.class, new Class<?>[]{FhirContext.class}, new Object[]{this});
IValidationSupport commonCodeSystemsSupport = ReflectionUtil.newInstanceOrReturnNull(commonCodeSystemsSupportType, IValidationSupport.class, new Class<?>[]{FhirContext.class}, new Object[]{this});
retVal = ReflectionUtil.newInstanceOrReturnNull("org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain", IValidationSupport.class, new Class<?>[]{IValidationSupport[].class}, new Object[]{new IValidationSupport[]{
retVal,
inMemoryTermSvc,
commonCodeSystemsSupport
}});
assert retVal != null : "Failed to instantiate " + "org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain";
}
return myValidationSupport;
myValidationSupport = retVal;
}
return retVal;
}
/**
@ -561,7 +600,7 @@ public class FhirContext {
* is used to supply underlying infrastructure such as conformance resources (StructureDefinition, ValueSet, etc)
* as well as to provide terminology services to modules such as the validator and FluentPath executor
*/
public void setValidationSupport(IContextValidationSupport<?, ?, ?, ?, ?, ?> theValidationSupport) {
public void setValidationSupport(IValidationSupport theValidationSupport) {
myValidationSupport = theValidationSupport;
}
@ -586,12 +625,21 @@ public class FhirContext {
}
/**
* Creates a new FluentPath engine which can be used to exvaluate
* @since 2.2
* @deprecated Deprecated in HAPI FHIR 5.0.0. Use {@link #newFhirPath()} instead.
*/
@Deprecated
public IFhirPath newFluentPath() {
return newFhirPath();
}
/**
* Creates a new FhirPath engine which can be used to evaluate
* path expressions over FHIR resources. Note that this engine will use the
* {@link IContextValidationSupport context validation support} module which is
* {@link IValidationSupport context validation support} module which is
* configured on the context at the time this method is called.
* <p>
* In other words, call {@link #setValidationSupport(IContextValidationSupport)} before
* In other words, you may wish to call {@link #setValidationSupport(IValidationSupport)} before
* calling {@link #newFluentPath()}
* </p>
* <p>
@ -601,10 +649,10 @@ public class FhirContext {
* {@link UnsupportedOperationException}
* </p>
*
* @since 2.2
* @since 5.0.0
*/
public IFluentPath newFluentPath() {
return myVersion.createFluentPathExecutor(this);
public IFhirPath newFhirPath() {
return myVersion.createFhirPathExecutor(this);
}
/**
@ -642,13 +690,12 @@ public class FhirContext {
return new RDFParser(this, myParserErrorHandler, Lang.TURTLE);
}
/**
* Instantiates a new client instance. This method requires an interface which is defined specifically for your use
* cases to contain methods for each of the RESTful operations you wish to implement (e.g. "read ImagingStudy",
* "search Patient by identifier", etc.). This interface must extend {@link IRestfulClient} (or commonly its
* sub-interface {@link IBasicClient}). See the <a
* href="http://jamesagnew.github.io/hapi-fhir/doc_rest_client.html">RESTful Client</a> documentation for more
* href="https://hapifhir.io/hapi-fhir/docs/client/introduction.html">RESTful Client</a> documentation for more
* information on how to define this interface.
*
* <p>
@ -862,19 +909,6 @@ public class FhirContext {
myParserErrorHandler = theParserErrorHandler;
}
/**
* Sets the configured performance options
*
* @see PerformanceOptionsEnum for a list of available options
*/
public void setPerformanceOptions(final PerformanceOptionsEnum... thePerformanceOptions) {
Collection<PerformanceOptionsEnum> asList = null;
if (thePerformanceOptions != null) {
asList = Arrays.asList(thePerformanceOptions);
}
setPerformanceOptions(asList);
}
@SuppressWarnings({"cast"})
private List<Class<? extends IElement>> toElementList(final Collection<Class<? extends IBaseResource>> theResourceTypes) {
if (theResourceTypes == null) {

View File

@ -0,0 +1,36 @@
package ca.uhn.fhir.context.support;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class ConceptValidationOptions {
public boolean isInferSystem() {
return myInferSystem;
}
public ConceptValidationOptions setInferSystem(boolean theInferSystem) {
myInferSystem = theInferSystem;
return this;
}
private boolean myInferSystem;
}

View File

@ -0,0 +1,354 @@
package ca.uhn.fhir.context.support;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.util.BundleUtil;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class DefaultProfileValidationSupport implements IValidationSupport {
private static final String URL_PREFIX_STRUCTURE_DEFINITION = "http://hl7.org/fhir/StructureDefinition/";
private static final String URL_PREFIX_STRUCTURE_DEFINITION_BASE = "http://hl7.org/fhir/";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultProfileValidationSupport.class);
private final FhirContext myCtx;
private Map<String, IBaseResource> myCodeSystems;
private Map<String, IBaseResource> myStructureDefinitions;
private Map<String, IBaseResource> myValueSets;
private List<String> myTerminologyResources;
private List<String> myStructureDefinitionResources;
/**
* Constructor
*
* @param theFhirContext The context to use
*/
public DefaultProfileValidationSupport(FhirContext theFhirContext) {
myCtx = theFhirContext;
}
private void initializeResourceLists() {
if (myTerminologyResources != null && myStructureDefinitionResources != null) {
return;
}
List<String> terminologyResources = new ArrayList<>();
List<String> structureDefinitionResources = new ArrayList<>();
switch (getFhirContext().getVersion().getVersion()) {
case DSTU2:
case DSTU2_HL7ORG:
terminologyResources.add("/org/hl7/fhir/instance/model/valueset/valuesets.xml");
terminologyResources.add("/org/hl7/fhir/instance/model/valueset/v2-tables.xml");
terminologyResources.add("/org/hl7/fhir/instance/model/valueset/v3-codesystems.xml");
Properties profileNameProperties = new Properties();
try {
profileNameProperties.load(DefaultProfileValidationSupport.class.getResourceAsStream("/org/hl7/fhir/instance/model/profile/profiles.properties"));
for (Object nextKey : profileNameProperties.keySet()) {
structureDefinitionResources.add("/org/hl7/fhir/instance/model/profile/" + nextKey);
}
} catch (IOException e) {
throw new ConfigurationException(e);
}
break;
case DSTU2_1:
terminologyResources.add("/org/hl7/fhir/dstu2016may/model/valueset/valuesets.xml");
terminologyResources.add("/org/hl7/fhir/dstu2016may/model/valueset/v2-tables.xml");
terminologyResources.add("/org/hl7/fhir/dstu2016may/model/valueset/v3-codesystems.xml");
structureDefinitionResources.add("/org/hl7/fhir/dstu2016may/model/profile/profiles-resources.xml");
structureDefinitionResources.add("/org/hl7/fhir/dstu2016may/model/profile/profiles-types.xml");
structureDefinitionResources.add("/org/hl7/fhir/dstu2016may/model/profile/profiles-others.xml");
break;
case DSTU3:
terminologyResources.add("/org/hl7/fhir/dstu3/model/valueset/valuesets.xml");
terminologyResources.add("/org/hl7/fhir/dstu3/model/valueset/v2-tables.xml");
terminologyResources.add("/org/hl7/fhir/dstu3/model/valueset/v3-codesystems.xml");
structureDefinitionResources.add("/org/hl7/fhir/dstu3/model/profile/profiles-resources.xml");
structureDefinitionResources.add("/org/hl7/fhir/dstu3/model/profile/profiles-types.xml");
structureDefinitionResources.add("/org/hl7/fhir/dstu3/model/profile/profiles-others.xml");
structureDefinitionResources.add("/org/hl7/fhir/dstu3/model/extension/extension-definitions.xml");
break;
case R4:
terminologyResources.add("/org/hl7/fhir/r4/model/valueset/valuesets.xml");
terminologyResources.add("/org/hl7/fhir/r4/model/valueset/v2-tables.xml");
terminologyResources.add("/org/hl7/fhir/r4/model/valueset/v3-codesystems.xml");
structureDefinitionResources.add("/org/hl7/fhir/r4/model/profile/profiles-resources.xml");
structureDefinitionResources.add("/org/hl7/fhir/r4/model/profile/profiles-types.xml");
structureDefinitionResources.add("/org/hl7/fhir/r4/model/profile/profiles-others.xml");
structureDefinitionResources.add("/org/hl7/fhir/r4/model/extension/extension-definitions.xml");
break;
case R5:
structureDefinitionResources.add("/org/hl7/fhir/r5/model/profile/profiles-resources.xml");
structureDefinitionResources.add("/org/hl7/fhir/r5/model/profile/profiles-types.xml");
structureDefinitionResources.add("/org/hl7/fhir/r5/model/profile/profiles-others.xml");
structureDefinitionResources.add("/org/hl7/fhir/r5/model/extension/extension-definitions.xml");
terminologyResources.add("/org/hl7/fhir/r5/model/valueset/valuesets.xml");
terminologyResources.add("/org/hl7/fhir/r5/model/valueset/v2-tables.xml");
terminologyResources.add("/org/hl7/fhir/r5/model/valueset/v3-codesystems.xml");
break;
}
myTerminologyResources = terminologyResources;
myStructureDefinitionResources = structureDefinitionResources;
}
@Override
public List<IBaseResource> fetchAllConformanceResources() {
ArrayList<IBaseResource> retVal = new ArrayList<>();
retVal.addAll(myCodeSystems.values());
retVal.addAll(myStructureDefinitions.values());
retVal.addAll(myValueSets.values());
return retVal;
}
@Override
public <T extends IBaseResource> List<T> fetchAllStructureDefinitions() {
return toList(provideStructureDefinitionMap());
}
@Override
public IBaseResource fetchCodeSystem(String theSystem) {
return fetchCodeSystemOrValueSet(theSystem, true);
}
private IBaseResource fetchCodeSystemOrValueSet(String theSystem, boolean codeSystem) {
synchronized (this) {
Map<String, IBaseResource> codeSystems = myCodeSystems;
Map<String, IBaseResource> valueSets = myValueSets;
if (codeSystems == null || valueSets == null) {
codeSystems = new HashMap<>();
valueSets = new HashMap<>();
initializeResourceLists();
for (String next : myTerminologyResources) {
loadCodeSystems(codeSystems, valueSets, next);
}
myCodeSystems = codeSystems;
myValueSets = valueSets;
}
// System can take the form "http://url|version"
String system = theSystem;
if (system.contains("|")) {
String version = system.substring(system.indexOf('|') + 1);
if (version.matches("^[0-9.]+$")) {
system = system.substring(0, system.indexOf('|'));
}
}
if (codeSystem) {
return codeSystems.get(system);
} else {
return valueSets.get(system);
}
}
}
@Override
public IBaseResource fetchStructureDefinition(String theUrl) {
String url = theUrl;
if (url.startsWith(URL_PREFIX_STRUCTURE_DEFINITION)) {
// no change
} else if (url.indexOf('/') == -1) {
url = URL_PREFIX_STRUCTURE_DEFINITION + url;
} else if (StringUtils.countMatches(url, '/') == 1) {
url = URL_PREFIX_STRUCTURE_DEFINITION_BASE + url;
}
Map<String, IBaseResource> structureDefinitionMap = provideStructureDefinitionMap();
IBaseResource retVal = structureDefinitionMap.get(url);
return retVal;
}
@Override
public IBaseResource fetchValueSet(String theUrl) {
IBaseResource retVal = fetchCodeSystemOrValueSet(theUrl, false);
return retVal;
}
public void flush() {
myCodeSystems = null;
myStructureDefinitions = null;
}
@Override
public FhirContext getFhirContext() {
return myCtx;
}
private Map<String, IBaseResource> provideStructureDefinitionMap() {
Map<String, IBaseResource> structureDefinitions = myStructureDefinitions;
if (structureDefinitions == null) {
structureDefinitions = new HashMap<>();
initializeResourceLists();
for (String next : myStructureDefinitionResources) {
loadStructureDefinitions(structureDefinitions, next);
}
myStructureDefinitions = structureDefinitions;
}
return structureDefinitions;
}
private void loadCodeSystems(Map<String, IBaseResource> theCodeSystems, Map<String, IBaseResource> theValueSets, String theClasspath) {
ourLog.info("Loading CodeSystem/ValueSet from classpath: {}", theClasspath);
InputStream inputStream = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath);
InputStreamReader reader = null;
if (inputStream != null) {
try {
reader = new InputStreamReader(inputStream, Constants.CHARSET_UTF8);
List<IBaseResource> resources = parseBundle(reader);
for (IBaseResource next : resources) {
RuntimeResourceDefinition nextDef = getFhirContext().getResourceDefinition(next);
Map<String, IBaseResource> map = null;
switch (nextDef.getName()) {
case "CodeSystem":
map = theCodeSystems;
break;
case "ValueSet":
map = theValueSets;
break;
}
if (map != null) {
String urlValueString = getConformanceResourceUrl(next);
if (isNotBlank(urlValueString)) {
map.put(urlValueString, next);
}
switch (myCtx.getVersion().getVersion()) {
case DSTU2:
case DSTU2_HL7ORG:
IPrimitiveType<?> codeSystem = myCtx.newTerser().getSingleValueOrNull(next, "ValueSet.codeSystem.system", IPrimitiveType.class);
if (codeSystem != null && isNotBlank(codeSystem.getValueAsString())) {
theCodeSystems.put(codeSystem.getValueAsString(), next);
}
break;
default:
case DSTU2_1:
case DSTU3:
case R4:
case R5:
break;
}
}
}
} finally {
try {
if (reader != null) {
reader.close();
}
inputStream.close();
} catch (IOException e) {
ourLog.warn("Failure closing stream", e);
}
}
} else {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
}
private void loadStructureDefinitions(Map<String, IBaseResource> theCodeSystems, String theClasspath) {
ourLog.info("Loading structure definitions from classpath: {}", theClasspath);
try (InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath)) {
if (valuesetText != null) {
try (InputStreamReader reader = new InputStreamReader(valuesetText, Constants.CHARSET_UTF8)) {
List<IBaseResource> resources = parseBundle(reader);
for (IBaseResource next : resources) {
String nextType = getFhirContext().getResourceDefinition(next).getName();
if ("StructureDefinition".equals(nextType)) {
String url = getConformanceResourceUrl(next);
if (isNotBlank(url)) {
theCodeSystems.put(url, next);
}
}
}
}
} else {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
} catch (IOException theE) {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
}
private String getConformanceResourceUrl(IBaseResource theResource) {
String urlValueString = null;
Optional<IBase> urlValue = getFhirContext().getResourceDefinition(theResource).getChildByName("url").getAccessor().getFirstValueOrNull(theResource);
if (urlValue.isPresent()) {
IPrimitiveType<?> urlValueType = (IPrimitiveType<?>) urlValue.get();
urlValueString = urlValueType.getValueAsString();
}
return urlValueString;
}
private List<IBaseResource> parseBundle(InputStreamReader theReader) {
IBaseResource parsedObject = getFhirContext().newXmlParser().parseResource(theReader);
if (parsedObject instanceof IBaseBundle) {
IBaseBundle bundle = (IBaseBundle) parsedObject;
return BundleUtil.toListOfResources(getFhirContext(), bundle);
} else {
return Collections.singletonList(parsedObject);
}
}
static <T extends IBaseResource> List<T> toList(Map<String, IBaseResource> theMap) {
ArrayList<IBaseResource> retVal = new ArrayList<>(theMap.values());
return (List<T>) Collections.unmodifiableList(retVal);
}
}

View File

@ -23,12 +23,14 @@ package ca.uhn.fhir.context.support;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.ParametersUtil;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -42,33 +44,63 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
* various functions that can be provided by validation and terminology
* services.
* <p>
* This interface is invoked directly by internal parts of the HAPI FHIR API, including the
* Validator and the FHIRPath evaluator. It is used to supply artifacts required for validation
* (e.g. StructureDefinition resources, ValueSet resources, etc.) and also to provide
* terminology functions such as code validation, ValueSet expansion, etc.
* </p>
* <p>
* Implementations are not required to implement all of the functions
* in this interface; in fact it is expected that most won't. Any
* methods which are not implemented may simply return <code>null</code>
* and calling code is expected to be able to handle this.
* and calling code is expected to be able to handle this. Generally, a
* series of implementations of this interface will be joined together using
* the
* <a href="https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-validation/org/hl7/fhir/common/hapi/validation/ValidationSupportChain2.html">ValidationSupportChain</a>
* class.
* </p>
* <p>
* See <a href="https://hapifhir.io/hapi-fhir/docs/validation/validation_support_modules.html">Validation Support Modules</a>
* for information on how to assemble and configure implementations of this interface. See also
* the <code>org.hl7.fhir.common.hapi.validation.support</code>
* <a href="https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-validation/org/hl7/fhir/common/hapi/validation/package-summary.html">package summary</a>
* in the <code>hapi-fhir-validation</code> module for many implementations of this interface.
* </p>
*
* @since 5.0.0
*/
public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST> {
public interface IValidationSupport {
String URL_PREFIX_VALUE_SET = "http://hl7.org/fhir/ValueSet/";
/**
* Expands the given portion of a ValueSet
*
* @param theInclude The portion to include
* @return The expansion
* @param theRootValidationSupport The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
* @param theExpansionOptions If provided (may be <code>null</code>), contains options controlling the expansion
* @param theValueSetToExpand The valueset that should be expanded
* @return The expansion, or null
*/
EVS_OUT expandValueSet(FhirContext theContext, EVS_IN theInclude);
default ValueSetExpansionOutcome expandValueSet(IValidationSupport theRootValidationSupport, @Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) {
return null;
}
/**
* Load and return all conformance resources associated with this
* validation support module. This method may return null if it doesn't
* make sense for a given module.
*/
List<IBaseResource> fetchAllConformanceResources(FhirContext theContext);
default List<IBaseResource> fetchAllConformanceResources() {
return null;
}
/**
* Load and return all possible structure definitions
*/
List<SDT> fetchAllStructureDefinitions(FhirContext theContext);
default <T extends IBaseResource> List<T> fetchAllStructureDefinitions() {
return null;
}
/**
* Fetch a code system by ID
@ -76,70 +108,146 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
* @param theSystem The code system
* @return The valueset (must not be null, but can be an empty ValueSet)
*/
CST fetchCodeSystem(FhirContext theContext, String theSystem);
default IBaseResource fetchCodeSystem(String theSystem) {
return null;
}
/**
* Loads a resource needed by the validation (a StructureDefinition, or a
* ValueSet)
*
* @param theContext The HAPI FHIR Context object current in use by the validator
* @param theClass The type of the resource to load
* @param theUri The resource URI
* @return Returns the resource, or <code>null</code> if no resource with the
* given URI can be found
*/
<T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri);
default <T extends IBaseResource> T fetchResource(Class<T> theClass, String theUri) {
Validate.notNull(theClass, "theClass must not be null or blank");
Validate.notBlank(theUri, "theUri must not be null or blank");
SDT fetchStructureDefinition(FhirContext theCtx, String theUrl);
switch (getFhirContext().getResourceDefinition(theClass).getName()) {
case "StructureDefinition":
return theClass.cast(fetchStructureDefinition(theUri));
case "ValueSet":
return theClass.cast(fetchValueSet(theUri));
case "CodeSystem":
return theClass.cast(fetchCodeSystem(theUri));
}
if (theUri.startsWith(URL_PREFIX_VALUE_SET)) {
return theClass.cast(fetchValueSet(theUri));
}
return null;
}
default IBaseResource fetchStructureDefinition(String theUrl) {
return null;
}
/**
* Returns <code>true</code> if codes in the given code system can be expanded
* or validated
*
* @param theRootValidationSupport The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
* @param theSystem The URI for the code system, e.g. <code>"http://loinc.org"</code>
* @return Returns <code>true</code> if codes in the given code system can be
* validated
*/
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
default boolean isCodeSystemSupported(IValidationSupport theRootValidationSupport, String theSystem) {
return false;
}
/**
* Fetch the given ValueSet by URL
*/
IBaseResource fetchValueSet(FhirContext theContext, String theValueSetUrl);
default IBaseResource fetchValueSet(String theValueSetUrl) {
return null;
}
/**
* Validates that the given code exists and if possible returns a display
* name. This method is called to check codes which are found in "example"
* binding fields (e.g. <code>Observation.code</code> in the default profile.
*
* @param theRootValidationSupport The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
* @param theOptions Provides options controlling the validation
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
* @param theCode The code, e.g. "<code>1234-5</code>"
* @param theDisplay The display name, if it should also be validated
* @return Returns a validation result object
*/
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl);
default CodeValidationResult validateCode(IValidationSupport theRootValidationSupport, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
return null;
}
/**
* Validates that the given code exists and if possible returns a display
* name. This method is called to check codes which are found in "example"
* binding fields (e.g. <code>Observation.code</code> in the default profile.
*
* @param theRootValidationSupport The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
* @param theCode The code, e.g. "<code>1234-5</code>"
* @param theDisplay The display name, if it should also be validated
* @param theValueSet The ValueSet to validate against. Must not be null, and must be a ValueSet resource.
* @return Returns a validation result object, or <code>null</code> if this validation support module can not handle this kind of request
*/
default CodeValidationResult validateCodeInValueSet(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { return null; }
default CodeValidationResult validateCodeInValueSet(IValidationSupport theRootValidationSupport, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
return null;
}
/**
* Look up a code using the system and code value
*
* @param theContext The FHIR context
* @param theRootValidationSupport The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
* @param theSystem The CodeSystem URL
* @param theCode The code
*/
LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode);
default LookupCodeResult lookupCode(IValidationSupport theRootValidationSupport, String theSystem, String theCode) {
return null;
}
/**
* Returns <code>true</code> if the given valueset can be validated by the given
* validation support module
*
* @param theRootValidationSupport The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
* @param theValueSetUrl The ValueSet canonical URL
*/
default boolean isValueSetSupported(IValidationSupport theRootValidationSupport, String theValueSetUrl) {
return false;
}
/**
* Generate a snapshot from the given differential profile.
*
* @param theRootValidationSupport The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
* @return Returns null if this module does not know how to handle this request
*/
default IBaseResource generateSnapshot(IValidationSupport theRootValidationSupport, IBaseResource theInput, String theUrl, String theWebUrl, String theProfileName) {
return null;
}
/**
* Returns the FHIR Context associated with this module
*/
FhirContext getFhirContext();
/**
* This method clears any temporary caches within the validation support. It is mainly intended for unit tests,
* but could be used in non-test scenarios as well.
*/
default void invalidateCaches() {
// nothing
}
class ConceptDesignation {
private String myLanguage;
@ -257,59 +365,83 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
}
}
enum IssueSeverity {
/**
* The issue caused the action to fail, and no further checking could be performed.
*/
FATAL,
/**
* The issue is sufficiently important to cause the action to fail.
*/
ERROR,
/**
* The issue is not important enough to cause the action to fail, but may cause it to be performed suboptimally or in a way that is not as desired.
*/
WARNING,
/**
* The issue has no relation to the degree of success of the action.
*/
INFORMATION
}
class CodeValidationResult {
private IBase myDefinition;
private String myCode;
private String myMessage;
private Enum mySeverity;
private IssueSeverity mySeverity;
private String myCodeSystemName;
private String myCodeSystemVersion;
private List<BaseConceptProperty> myProperties;
private String myDisplay;
public CodeValidationResult(IBase theDefinition) {
this.myDefinition = theDefinition;
}
public CodeValidationResult(Enum theSeverity, String message) {
this.mySeverity = theSeverity;
this.myMessage = message;
}
public CodeValidationResult(Enum theSeverity, String theMessage, IBase theDefinition, String theDisplay) {
this.mySeverity = theSeverity;
this.myMessage = theMessage;
this.myDefinition = theDefinition;
this.myDisplay = theDisplay;
public CodeValidationResult() {
super();
}
public String getDisplay() {
return myDisplay;
}
public IBase asConceptDefinition() {
return myDefinition;
public CodeValidationResult setDisplay(String theDisplay) {
myDisplay = theDisplay;
return this;
}
public String getCode() {
return myCode;
}
public CodeValidationResult setCode(String theCode) {
myCode = theCode;
return this;
}
String getCodeSystemName() {
return myCodeSystemName;
}
public void setCodeSystemName(String theCodeSystemName) {
public CodeValidationResult setCodeSystemName(String theCodeSystemName) {
myCodeSystemName = theCodeSystemName;
return this;
}
public String getCodeSystemVersion() {
return myCodeSystemVersion;
}
public void setCodeSystemVersion(String theCodeSystemVersion) {
public CodeValidationResult setCodeSystemVersion(String theCodeSystemVersion) {
myCodeSystemVersion = theCodeSystemVersion;
return this;
}
public String getMessage() {
return myMessage;
}
public CodeValidationResult setMessage(String theMessage) {
myMessage = theMessage;
return this;
}
public List<BaseConceptProperty> getProperties() {
return myProperties;
}
@ -318,12 +450,17 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
myProperties = theProperties;
}
public Enum getSeverity() {
public IssueSeverity getSeverity() {
return mySeverity;
}
public CodeValidationResult setSeverity(IssueSeverity theSeverity) {
mySeverity = theSeverity;
return this;
}
public boolean isOk() {
return myDefinition != null;
return isNotBlank(myCode);
}
public LookupCodeResult asLookupCodeResult(String theSearchedForSystem, String theSearchedForCode) {
@ -339,6 +476,49 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
return retVal;
}
/**
* Convenience method that returns {@link #getSeverity()} as an IssueSeverity code string
*/
public String getSeverityCode() {
String retVal = null;
if (getSeverity() != null) {
retVal = getSeverity().name().toLowerCase();
}
return retVal;
}
/**
* Sets an issue severity as a string code. Value must be the name of
* one of the enum values in {@link IssueSeverity}. Value is case-insensitive.
*/
public CodeValidationResult setSeverityCode(@Nonnull String theIssueSeverity) {
setSeverity(IssueSeverity.valueOf(theIssueSeverity.toUpperCase()));
return this;
}
}
class ValueSetExpansionOutcome {
private final IBaseResource myValueSet;
private final String myError;
public ValueSetExpansionOutcome(IBaseResource theValueSet, String theError) {
myValueSet = theValueSet;
myError = theError;
}
public ValueSetExpansionOutcome(IBaseResource theValueSet) {
myValueSet = theValueSet;
myError = null;
}
public String getError() {
return myError;
}
public IBaseResource getValueSet() {
return myValueSet;
}
}
class LookupCodeResult {
@ -350,7 +530,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
private boolean myFound;
private String mySearchedForCode;
private String mySearchedForSystem;
private List<IContextValidationSupport.BaseConceptProperty> myProperties;
private List<IValidationSupport.BaseConceptProperty> myProperties;
private List<ConceptDesignation> myDesignations;
/**
@ -367,7 +547,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
return myProperties;
}
public void setProperties(List<IContextValidationSupport.BaseConceptProperty> theProperties) {
public void setProperties(List<IValidationSupport.BaseConceptProperty> theProperties) {
myProperties = theProperties;
}
@ -466,7 +646,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
.collect(Collectors.toSet());
}
for (IContextValidationSupport.BaseConceptProperty next : myProperties) {
for (IValidationSupport.BaseConceptProperty next : myProperties) {
if (!properties.isEmpty()) {
if (!properties.contains(next.getPropertyName())) {
@ -477,11 +657,11 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property");
ParametersUtil.addPartCode(theContext, property, "code", next.getPropertyName());
if (next instanceof IContextValidationSupport.StringConceptProperty) {
IContextValidationSupport.StringConceptProperty prop = (IContextValidationSupport.StringConceptProperty) next;
if (next instanceof IValidationSupport.StringConceptProperty) {
IValidationSupport.StringConceptProperty prop = (IValidationSupport.StringConceptProperty) next;
ParametersUtil.addPartString(theContext, property, "value", prop.getValue());
} else if (next instanceof IContextValidationSupport.CodingConceptProperty) {
IContextValidationSupport.CodingConceptProperty prop = (IContextValidationSupport.CodingConceptProperty) next;
} else if (next instanceof IValidationSupport.CodingConceptProperty) {
IValidationSupport.CodingConceptProperty prop = (IValidationSupport.CodingConceptProperty) next;
ParametersUtil.addPartCoding(theContext, property, "value", prop.getCodeSystem(), prop.getCode(), prop.getDisplay());
} else {
throw new IllegalStateException("Don't know how to handle " + next.getClass());

View File

@ -0,0 +1,96 @@
package ca.uhn.fhir.context.support;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.apache.commons.lang3.Validate;
/**
* Options for ValueSet expansion
*
* @see IValidationSupport
*/
public class ValueSetExpansionOptions {
private boolean myFailOnMissingCodeSystem = true;
private int myCount = 1000;
private int myOffset = 0;
/**
* The number of codes to return.
* <p>
* Default is 1000
* </p>
*/
public int getCount() {
return myCount;
}
/**
* The number of codes to return.
* <p>
* Default is 1000
* </p>
*/
public ValueSetExpansionOptions setCount(int theCount) {
Validate.isTrue(theCount >= 0, "theCount must be >= 0");
myCount = theCount;
return this;
}
/**
* The code index to start at (i.e the individual code index, not the page number)
*/
public int getOffset() {
return myOffset;
}
/**
* The code index to start at (i.e the individual code index, not the page number)
*/
public ValueSetExpansionOptions setOffset(int theOffset) {
Validate.isTrue(theOffset >= 0, "theOffset must be >= 0");
myOffset = theOffset;
return this;
}
/**
* Should the expansion fail if a codesystem is referenced by the valueset, but
* it can not be found?
* <p>
* Default is <code>true</code>
* </p>
*/
public boolean isFailOnMissingCodeSystem() {
return myFailOnMissingCodeSystem;
}
/**
* Should the expansion fail if a codesystem is referenced by the valueset, but
* it can not be found?
* <p>
* Default is <code>true</code>
* </p>
*/
public ValueSetExpansionOptions setFailOnMissingCodeSystem(boolean theFailOnMissingCodeSystem) {
myFailOnMissingCodeSystem = theFailOnMissingCodeSystem;
return this;
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.fluentpath;
package ca.uhn.fhir.fhirpath;
/*
* #%L
@ -23,18 +23,18 @@ package ca.uhn.fhir.fluentpath;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
/**
* This exception is thrown if a FluentPath expression can not be executed successfully
* This exception is thrown if a FHIRPath expression can not be executed successfully
* for any reason
*/
public class FluentPathExecutionException extends InternalErrorException {
public class FhirPathExecutionException extends InternalErrorException {
private static final long serialVersionUID = 1L;
public FluentPathExecutionException(Throwable theCause) {
public FhirPathExecutionException(Throwable theCause) {
super(theCause);
}
public FluentPathExecutionException(String theMessage) {
public FhirPathExecutionException(String theMessage) {
super(theMessage);
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.fluentpath;
package ca.uhn.fhir.fhirpath;
/*
* #%L
@ -25,7 +25,7 @@ import java.util.Optional;
import org.hl7.fhir.instance.model.api.IBase;
public interface IFluentPath {
public interface IFhirPath {
/**
* Apply the given FluentPath expression against the given input and return

View File

@ -43,6 +43,7 @@ public class HapiLocalizer {
private List<ResourceBundle> myBundle = new ArrayList<>();
private final Map<String, String> myHardcodedMessages = new HashMap<>();
private String[] myBundleNames;
private Locale myLocale = Locale.getDefault();
public HapiLocalizer() {
this(HapiLocalizer.class.getPackage().getName() + ".hapi-messages");
@ -164,6 +165,10 @@ public class HapiLocalizer {
}
}
public Locale getLocale() {
return myLocale;
}
/**
* This <b>global setting</b> causes the localizer to fail if any attempts
* are made to retrieve a key that does not exist. This method is primarily for

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.interceptor.api;
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

View File

@ -23,6 +23,8 @@ package ca.uhn.fhir.interceptor.api;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
public interface IInterceptorService extends IInterceptorBroadcaster {
@ -90,4 +92,8 @@ public interface IInterceptorService extends IInterceptorBroadcaster {
void registerInterceptors(@Nullable Collection<?> theInterceptors);
/**
* Unregisters all interceptors that are indicated by the given callback function returning <code>true</code>
*/
void unregisterInterceptorsIf(Predicate<Object> theShouldUnregisterFunction);
}

View File

@ -27,7 +27,12 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import javax.annotation.Nonnull;
import java.util.*;
import java.io.Writer;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Value for {@link Hook#value()}
@ -60,12 +65,16 @@ public enum Pointcut {
* <li>
* ca.uhn.fhir.rest.client.api.IHttpRequest - The details of the request
* </li>
* <li>
* ca.uhn.fhir.rest.client.api.IRestfulClient - The client object making the request
* </li>
* </ul>
* </p>
* Hook methods must return <code>void</code>.
*/
CLIENT_REQUEST(void.class,
"ca.uhn.fhir.rest.client.api.IHttpRequest"
"ca.uhn.fhir.rest.client.api.IHttpRequest",
"ca.uhn.fhir.rest.client.api.IRestfulClient"
),
/**
@ -77,7 +86,12 @@ public enum Pointcut {
* <ul>
* <li>
* ca.uhn.fhir.rest.client.api.IHttpRequest - The details of the request
* ca.uhn.fhir.rest.client.api.IHttpRequest - The details of the response
* </li>
* <li>
* ca.uhn.fhir.rest.client.api.IHttpResponse - The details of the response
* </li>
* <li>
* ca.uhn.fhir.rest.client.api.IRestfulClient - The client object making the request
* </li>
* </ul>
* </p>
@ -85,7 +99,8 @@ public enum Pointcut {
*/
CLIENT_RESPONSE(void.class,
"ca.uhn.fhir.rest.client.api.IHttpRequest",
"ca.uhn.fhir.rest.client.api.IHttpResponse"
"ca.uhn.fhir.rest.client.api.IHttpResponse",
"ca.uhn.fhir.rest.client.api.IRestfulClient"
),
/**
@ -374,6 +389,44 @@ public enum Pointcut {
),
/**
* <b>Server Hook:</b>
* This method is called when a stream writer is generated that will be used to stream a non-binary response to
* a client. Hooks may return a wrapped writer which adds additional functionality as needed.
*
* <p>
* Hooks may accept the following parameters:
* <ul>
* <li>
* java.io.Writer - The response writing Writer. Typically a hook will wrap this writer and layer additional functionality
* into the wrapping writer.
* </li>
* <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request.
* </li>
* <li>
* ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
* </li>
* </ul>
* </p>
* <p>
* Hook methods should return a {@link Writer} instance that will be used to stream the response. Hook methods
* should not throw any exception.
* </p>
*
* @since 5.0.0
*/
SERVER_OUTGOING_WRITER_CREATED(Writer.class,
"java.io.Writer",
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
),
/**
* <b>Server Hook:</b>
@ -551,7 +604,7 @@ public enum Pointcut {
* <p>
* Hooks may accept the following parameters:
* <ul>
* <li>ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage - Hooks may modify this parameter. This will affect the checking process.</li>
* <li>ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage - Hooks may modify this parameter. This will affect the checking process.</li>
* </ul>
* </p>
* <p>
@ -560,7 +613,7 @@ public enum Pointcut {
* returns <code>false</code>, subscription processing will not proceed for the given resource;
* </p>
*/
SUBSCRIPTION_RESOURCE_MODIFIED(boolean.class, "ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage"),
SUBSCRIPTION_RESOURCE_MODIFIED(boolean.class, "ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage"),
/**
@ -574,8 +627,8 @@ public enum Pointcut {
* </p>
* Hooks may accept the following parameters:
* <ul>
* <li>ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription</li>
* <li>ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage</li>
* <li>ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription</li>
* <li>ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage</li>
* <li>ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult</li>
* </ul>
* <p>
@ -584,7 +637,7 @@ public enum Pointcut {
* returns <code>false</code>, delivery will be aborted.
* </p>
*/
SUBSCRIPTION_RESOURCE_MATCHED(boolean.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage", "ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult"),
SUBSCRIPTION_RESOURCE_MATCHED(boolean.class, "ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage", "ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult"),
/**
* <b>Subscription Hook:</b>
@ -593,14 +646,14 @@ public enum Pointcut {
* <p>
* Hooks may accept the following parameters:
* <ul>
* <li>ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage - Hooks should not modify this parameter as changes will not have any effect.</li>
* <li>ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage - Hooks should not modify this parameter as changes will not have any effect.</li>
* </ul>
* </p>
* <p>
* Hooks should return <code>void</code>.
* </p>
*/
SUBSCRIPTION_RESOURCE_DID_NOT_MATCH_ANY_SUBSCRIPTIONS(void.class, "ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage"),
SUBSCRIPTION_RESOURCE_DID_NOT_MATCH_ANY_SUBSCRIPTIONS(void.class, "ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage"),
/**
* <b>Subscription Hook:</b>
@ -613,8 +666,8 @@ public enum Pointcut {
* </p>
* Hooks may accept the following parameters:
* <ul>
* <li>ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription</li>
* <li>ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage</li>
* <li>ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription</li>
* <li>ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage</li>
* </ul>
* <p>
* Hooks may return <code>void</code> or may return a <code>boolean</code>. If the method returns
@ -622,7 +675,7 @@ public enum Pointcut {
* returns <code>false</code>, processing will be aborted.
* </p>
*/
SUBSCRIPTION_BEFORE_DELIVERY(boolean.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
SUBSCRIPTION_BEFORE_DELIVERY(boolean.class, "ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage"),
/**
* <b>Subscription Hook:</b>
@ -632,14 +685,14 @@ public enum Pointcut {
* Hooks may accept the following parameters:
* </p>
* <ul>
* <li>ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription</li>
* <li>ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage</li>
* <li>ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription</li>
* <li>ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage</li>
* </ul>
* <p>
* Hooks should return <code>void</code>.
* </p>
*/
SUBSCRIPTION_AFTER_DELIVERY(void.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
SUBSCRIPTION_AFTER_DELIVERY(void.class, "ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage"),
/**
@ -651,7 +704,7 @@ public enum Pointcut {
* </p>
* <ul>
* <li>java.lang.Exception - The exception that caused the failure. Note this could be an exception thrown by a SUBSCRIPTION_BEFORE_DELIVERY or SUBSCRIPTION_AFTER_DELIVERY interceptor</li>
* <li>ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage - the message that triggered the exception</li>
* <li>ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage - the message that triggered the exception</li>
* <li>java.lang.Exception</li>
* </ul>
* <p>
@ -663,7 +716,7 @@ public enum Pointcut {
* taken for the delivery.
* </p>
*/
SUBSCRIPTION_AFTER_DELIVERY_FAILED(boolean.class, "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage", "java.lang.Exception"),
SUBSCRIPTION_AFTER_DELIVERY_FAILED(boolean.class, "ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage", "java.lang.Exception"),
/**
* <b>Subscription Hook:</b>
@ -674,14 +727,14 @@ public enum Pointcut {
* </p>
* Hooks may accept the following parameters:
* <ul>
* <li>ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription</li>
* <li>ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage</li>
* <li>ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription</li>
* <li>ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage</li>
* </ul>
* <p>
* Hooks should return <code>void</code>.
* </p>
*/
SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY(void.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY(void.class, "ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage"),
/**
* <b>Subscription Hook:</b>
@ -693,8 +746,8 @@ public enum Pointcut {
* </p>
* Hooks may accept the following parameters:
* <ul>
* <li>ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription</li>
* <li>ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage</li>
* <li>ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription</li>
* <li>ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage</li>
* </ul>
* <p>
* Hooks may return <code>void</code> or may return a <code>boolean</code>. If the method returns
@ -702,7 +755,7 @@ public enum Pointcut {
* returns <code>false</code>, processing will be aborted.
* </p>
*/
SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY(boolean.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY(boolean.class, "ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage"),
/**
* <b>Subscription Hook:</b>
@ -712,7 +765,7 @@ public enum Pointcut {
* <p>
* Hooks may accept the following parameters:
* <ul>
* <li>ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage - Hooks may modify this parameter. This will affect the checking process.</li>
* <li>ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage - Hooks may modify this parameter. This will affect the checking process.</li>
* </ul>
* </p>
* <p>
@ -721,7 +774,7 @@ public enum Pointcut {
* returns <code>false</code>, processing will be aborted.
* </p>
*/
SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED(boolean.class, "ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage"),
SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED(boolean.class, "ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage"),
/**
@ -732,14 +785,14 @@ public enum Pointcut {
* <p>
* Hooks may accept the following parameters:
* <ul>
* <li>ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage - This parameter should not be modified as processing is complete when this hook is invoked.</li>
* <li>ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage - This parameter should not be modified as processing is complete when this hook is invoked.</li>
* </ul>
* </p>
* <p>
* Hooks should return <code>void</code>.
* </p>
*/
SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED(void.class, "ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage"),
SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED(void.class, "ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage"),
/**
@ -753,13 +806,13 @@ public enum Pointcut {
* </p>
* Hooks may accept the following parameters:
* <ul>
* <li>ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription</li>
* <li>ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription</li>
* </ul>
* <p>
* Hooks should return <code>void</code>.
* </p>
*/
SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED(void.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription"),
SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED(void.class, "ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription"),
/**
@ -804,7 +857,7 @@ public enum Pointcut {
void.class,
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
"ca.uhn.fhir.jpa.delete.DeleteConflictList",
"ca.uhn.fhir.jpa.api.model.DeleteConflictList",
"org.hl7.fhir.instance.model.api.IBaseResource"
),
@ -1205,13 +1258,13 @@ public enum Pointcut {
/**
* <b>Storage Hook:</b>
* Invoked when a resource delete operation is about to fail due to referential integrity conflicts.
* Invoked when a resource delete operation is about to fail due to referential integrity hcts.
* <p>
* Hooks will have access to the list of resources that have references to the resource being deleted.
* </p>
* Hooks may accept the following parameters:
* <ul>
* <li>ca.uhn.fhir.jpa.delete.DeleteConflictList - The list of delete conflicts</li>
* <li>ca.uhn.fhir.jpa.api.model.DeleteConflictList - The list of delete conflicts</li>
* <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
@ -1236,7 +1289,7 @@ public enum Pointcut {
// Return type
"ca.uhn.fhir.jpa.delete.DeleteConflictOutcome",
// Params
"ca.uhn.fhir.jpa.delete.DeleteConflictList",
"ca.uhn.fhir.jpa.api.model.DeleteConflictList",
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
),
@ -1321,6 +1374,83 @@ public enum Pointcut {
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
),
/**
* <b>Storage Hook:</b>
* Invoked before FHIR <b>create</b> operation to request the identification of the partition ID to be associated
* with the resource being created. This hook will only be called if partitioning is enabled in the JPA
* server.
* <p>
* Hooks may accept the following parameters:
* </p>
* <ul>
* <li>
* org.hl7.fhir.instance.model.api.IBaseResource - The resource that will be created and needs a tenant ID assigned.
* </li>
* <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request. Note that the bean
* properties are not all guaranteed to be populated, depending on how early during processing the
* exception occurred.
* </li>
* <li>
* ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
* </li>
* </ul>
* <p>
* Hooks should return an instance of <code>ca.uhn.fhir.jpa.api.model.RequestPartitionId</code> or <code>null</code>.
* </p>
*/
STORAGE_PARTITION_IDENTIFY_CREATE(
// Return type
"ca.uhn.fhir.interceptor.model.RequestPartitionId",
// Params
"org.hl7.fhir.instance.model.api.IBaseResource",
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
),
/**
* <b>Storage Hook:</b>
* Invoked before FHIR read/access operation (e.g. <b>read/vread</b>, <b>search</b>, <b>history</b>, etc.) operation to request the
* identification of the partition ID to be associated with the resource(s) being searched for, read, etc.
* <p>
* This hook will only be called if
* partitioning is enabled in the JPA server.
* </p>
* <p>
* Hooks may accept the following parameters:
* </p>
* <ul>
* <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request. Note that the bean
* properties are not all guaranteed to be populated, depending on how early during processing the
* exception occurred.
* </li>
* <li>
* ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
* </li>
* </ul>
* <p>
* Hooks should return an instance of <code>ca.uhn.fhir.jpa.api.model.RequestPartitionId</code> or <code>null</code>.
* </p>
*/
STORAGE_PARTITION_IDENTIFY_READ(
// Return type
"ca.uhn.fhir.interceptor.model.RequestPartitionId",
// Params
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
),
/**
* <b>Performance Tracing Hook:</b>
* This hook is invoked when any informational messages generated by the
@ -1722,9 +1852,7 @@ public enum Pointcut {
* This pointcut is used only for unit tests. Do not use in production code as it may be changed or
* removed at any time.
*/
TEST_RO(BaseServerResponseException.class, String.class.getName(), String.class.getName())
;
TEST_RO(BaseServerResponseException.class, String.class.getName(), String.class.getName());
private final List<String> myParameterTypes;
private final Class<?> myReturnType;

View File

@ -20,7 +20,13 @@ package ca.uhn.fhir.interceptor.executor;
* #L%
*/
import ca.uhn.fhir.interceptor.api.*;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ArrayListMultimap;
@ -41,6 +47,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class InterceptorService implements IInterceptorService, IInterceptorBroadcaster {
@ -145,6 +152,16 @@ public class InterceptorService implements IInterceptorService, IInterceptorBroa
}
}
@Override
public void unregisterInterceptorsIf(Predicate<Object> theShouldUnregisterFunction) {
unregisterInterceptorsIf(theShouldUnregisterFunction, myGlobalInvokers);
unregisterInterceptorsIf(theShouldUnregisterFunction, myAnonymousInvokers);
}
private void unregisterInterceptorsIf(Predicate<Object> theShouldUnregisterFunction, ListMultimap<Pointcut, BaseInvoker> theGlobalInvokers) {
theGlobalInvokers.entries().removeIf(t->theShouldUnregisterFunction.test(t.getValue().getInterceptor()));
}
@Override
public boolean registerThreadLocalInterceptor(Object theInterceptor) {
if (!myThreadlocalInvokersEnabled) {

View File

@ -0,0 +1,106 @@
package ca.uhn.fhir.interceptor.model;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.LocalDate;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
public class RequestPartitionId {
private final Integer myPartitionId;
private final LocalDate myPartitionDate;
private final String myPartitionName;
/**
* Constructor
*/
private RequestPartitionId(@Nullable String thePartitionName, @Nullable Integer thePartitionId, @Nullable LocalDate thePartitionDate) {
myPartitionName = thePartitionName;
myPartitionId = thePartitionId;
myPartitionDate = thePartitionDate;
}
public String getPartitionName() {
return myPartitionName;
}
@Nullable
public Integer getPartitionId() {
return myPartitionId;
}
@Nullable
public LocalDate getPartitionDate() {
return myPartitionDate;
}
@Override
public String toString() {
return getPartitionIdStringOrNullString();
}
/**
* Returns the partition ID (numeric) as a string, or the string "null"
*/
public String getPartitionIdStringOrNullString() {
return defaultIfNull(myPartitionId, "null").toString();
}
/**
* Create a string representation suitable for use as a cache key. Null aware.
*/
public static String stringifyForKey(RequestPartitionId theRequestPartitionId) {
String retVal = "(null)";
if (theRequestPartitionId != null) {
retVal = theRequestPartitionId.getPartitionIdStringOrNullString();
}
return retVal;
}
@Nonnull
public static RequestPartitionId fromPartitionId(@Nullable Integer thePartitionId) {
return fromPartitionId(thePartitionId, null);
}
@Nonnull
public static RequestPartitionId fromPartitionId(@Nullable Integer thePartitionId, @Nullable LocalDate thePartitionDate) {
return new RequestPartitionId(null, thePartitionId, thePartitionDate);
}
@Nonnull
public static RequestPartitionId fromPartitionName(@Nullable String thePartitionName) {
return fromPartitionName(thePartitionName, null);
}
@Nonnull
public static RequestPartitionId fromPartitionName(@Nullable String thePartitionName, @Nullable LocalDate thePartitionDate) {
return new RequestPartitionId(thePartitionName, null, thePartitionDate);
}
@Nonnull
public static RequestPartitionId forPartitionNameAndId(@Nullable String thePartitionName, @Nullable Integer thePartitionId, @Nullable LocalDate thePartitionDate) {
return new RequestPartitionId(thePartitionName, thePartitionId, thePartitionDate);
}
}

View File

@ -23,11 +23,10 @@ package ca.uhn.fhir.model.api;
import java.io.InputStream;
import java.util.Date;
import ca.uhn.fhir.fhirpath.IFhirPath;
import org.hl7.fhir.instance.model.api.*;
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.fluentpath.IFluentPath;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
/**
@ -39,9 +38,7 @@ import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
*/
public interface IFhirVersion {
IFluentPath createFluentPathExecutor(FhirContext theFhirContext);
IContextValidationSupport<?, ?, ?, ?, ?, ?> createValidationSupport();
IFhirPath createFhirPathExecutor(FhirContext theFhirContext);
IBaseResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition, String theServerBase);

View File

@ -0,0 +1,29 @@
package ca.uhn.fhir.model.api;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
public interface IModelJson {
}

View File

@ -24,7 +24,7 @@ import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.fluentpath.IFluentPath;
import ca.uhn.fhir.fhirpath.IFhirPath;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.instance.model.api.IBase;
@ -120,7 +120,7 @@ public abstract class BaseNarrativeGenerator implements INarrativeGenerator {
if (theFhirContext.getVersion().getVersion().isOlderThan(FhirVersionEnum.DSTU3)) {
return Collections.singletonList(theResource);
}
IFluentPath fhirPath = theFhirContext.newFluentPath();
IFhirPath fhirPath = theFhirContext.newFluentPath();
return fhirPath.evaluate(theResource, theContextPath, IBase.class);
}

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.jpa.subscription;
package ca.uhn.fhir.parser;
/*-
* #%L
* HAPI FHIR JPA Server
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
@ -20,8 +20,15 @@ package ca.uhn.fhir.jpa.subscription;
* #L%
*/
import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage;
class BaseErrorHandler {
public interface IResourceModifiedConsumer {
void submitResourceModified(ResourceModifiedMessage theMsg);
String describeLocation(IParserErrorHandler.IParseLocation theLocation) {
if (theLocation == null) {
return "";
} else {
return theLocation.toString() + " ";
}
}
}

View File

@ -58,6 +58,14 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
@SuppressWarnings("WeakerAccess")
public abstract class BaseParser implements IParser {
/**
* Any resources that were created by the parser (i.e. by parsing a serialized resource) will have
* a {@link IBaseResource#getUserData(String) user data} property with this key.
*
* @since 5.0.0
*/
public static final String RESOURCE_CREATED_BY_PARSER = BaseParser.class.getName() + "_" + "RESOURCE_CREATED_BY_PARSER";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParser.class);
private static final Set<String> notEncodeForContainedResource = new HashSet<>(Arrays.asList("security", "versionId", "lastUpdated"));

View File

@ -22,20 +22,29 @@ package ca.uhn.fhir.parser;
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.composite.BaseContainedDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.parser.json.*;
import ca.uhn.fhir.parser.json.JsonLikeArray;
import ca.uhn.fhir.parser.json.JsonLikeObject;
import ca.uhn.fhir.parser.json.JsonLikeStructure;
import ca.uhn.fhir.parser.json.JsonLikeValue;
import ca.uhn.fhir.parser.json.JsonLikeValue.ScalarType;
import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
import ca.uhn.fhir.parser.json.JsonLikeWriter;
import ca.uhn.fhir.parser.json.jackson.JacksonStructure;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.util.ElementUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.text.WordUtils;
@ -45,11 +54,17 @@ import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.math.BigDecimal;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.ID_DATATYPE;
import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.PRIMITIVE_DATATYPE;
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/**
* This class is the FHIR JSON parser/encoder. Users should not interact with this class directly, but should use
@ -147,10 +162,9 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
theEventWriter.beginObject(arrayName);
}
private JsonLikeWriter createJsonWriter(Writer theWriter) {
JsonLikeStructure jsonStructure = new GsonStructure();
JsonLikeWriter retVal = jsonStructure.getJsonLikeWriter(theWriter);
return retVal;
private JsonLikeWriter createJsonWriter(Writer theWriter) throws IOException {
JsonLikeStructure jsonStructure = new JacksonStructure();
return jsonStructure.getJsonLikeWriter(theWriter);
}
public void doEncodeResourceToJsonLikeWriter(IBaseResource theResource, JsonLikeWriter theEventWriter, EncodeContext theEncodeContext) throws IOException {
@ -168,11 +182,12 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
protected void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter, EncodeContext theEncodeContext) throws IOException {
JsonLikeWriter eventWriter = createJsonWriter(theWriter);
doEncodeResourceToJsonLikeWriter(theResource, eventWriter, theEncodeContext);
eventWriter.close();
}
@Override
public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) {
JsonLikeStructure jsonStructure = new GsonStructure();
JsonLikeStructure jsonStructure = new JacksonStructure();
jsonStructure.load(theReader);
T retVal = doParseResource(theResourceType, jsonStructure);
@ -302,19 +317,14 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
}
case CONTAINED_RESOURCE_LIST:
case CONTAINED_RESOURCES: {
/*
* Disabled per #103 ContainedDt value = (ContainedDt) theNextValue; for (IResource next :
* value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) { continue; }
* encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true,
* fixContainedResourceId(next.getId().getValue())); }
*/
List<IBaseResource> containedResources = getContainedResources().getContainedResources();
if (containedResources.size() > 0) {
beginArray(theEventWriter, theChildName);
for (IBaseResource next : containedResources) {
IIdType resourceId = getContainedResources().getResourceId(next);
encodeResourceToJsonStreamWriter(theResDef, next, theEventWriter, null, true, fixContainedResourceId(resourceId.getValue()), theEncodeContext);
String value = resourceId.getValue();
encodeResourceToJsonStreamWriter(theResDef, next, theEventWriter, null, true, fixContainedResourceId(value), theEncodeContext);
}
theEventWriter.endArray();
@ -344,7 +354,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
RuntimeResourceDefinition def = myContext.getResourceDefinition(resource);
theEncodeContext.pushPath(def.getName(), true);
encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, false, theEncodeContext);
encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, theContainedResource, theEncodeContext);
theEncodeContext.popPath();
break;
@ -418,10 +428,10 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
String currentChildName = null;
boolean inArray = false;
ArrayList<ArrayList<HeldExtension>> extensions = new ArrayList<ArrayList<HeldExtension>>(0);
ArrayList<ArrayList<HeldExtension>> modifierExtensions = new ArrayList<ArrayList<HeldExtension>>(0);
ArrayList<ArrayList<String>> comments = new ArrayList<ArrayList<String>>(0);
ArrayList<String> ids = new ArrayList<String>(0);
ArrayList<ArrayList<HeldExtension>> extensions = new ArrayList<>(0);
ArrayList<ArrayList<HeldExtension>> modifierExtensions = new ArrayList<>(0);
ArrayList<ArrayList<String>> comments = new ArrayList<>(0);
ArrayList<String> ids = new ArrayList<>(0);
int valueIdx = 0;
for (IBase nextValue : values) {
@ -1107,7 +1117,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
} else {
// must be a SCALAR
theState.enteringNewElement(null, theName);
theState.attributeValue("value", theJsonVal.getAsString());
String asString = theJsonVal.getAsString();
theState.attributeValue("value", asString);
parseAlternates(theAlternateVal, theState, theAlternateName, theAlternateName);
theState.endingElement();
}
@ -1376,15 +1387,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
}
}
public static Gson newGson() {
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
return gson;
}
private static void write(JsonLikeWriter theWriter, String theName, String theValue) throws IOException {
theWriter.write(theName, theValue);
}
private class HeldExtension implements Comparable<HeldExtension> {
private CompositeChildElement myChildElem;
@ -1568,4 +1570,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
theEventWriter.endObject();
}
}
private static void write(JsonLikeWriter theWriter, String theName, String theValue) throws IOException {
theWriter.write(theName, theValue);
}
}

View File

@ -38,7 +38,7 @@ import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
* @see IParser#setParserErrorHandler(IParserErrorHandler)
* @see FhirContext#setParserErrorHandler(IParserErrorHandler)
*/
public class LenientErrorHandler implements IParserErrorHandler {
public class LenientErrorHandler extends BaseErrorHandler implements IParserErrorHandler {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LenientErrorHandler.class);
private static final StrictErrorHandler STRICT_ERROR_HANDLER = new StrictErrorHandler();
@ -84,7 +84,7 @@ public class LenientErrorHandler implements IParserErrorHandler {
public void invalidValue(IParseLocation theLocation, String theValue, String theError) {
if (isBlank(theValue) || myErrorOnInvalidValue == false) {
if (myLogErrors) {
ourLog.warn("Invalid attribute value \"{}\": {}", theValue, theError);
ourLog.warn("{}Invalid attribute value \"{}\": {}", describeLocation(theLocation), theValue, theError);
}
} else {
STRICT_ERROR_HANDLER.invalidValue(theLocation, theValue, theError);
@ -133,35 +133,35 @@ public class LenientErrorHandler implements IParserErrorHandler {
@Override
public void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName) {
if (myLogErrors) {
ourLog.warn("Multiple repetitions of non-repeatable element '{}' found while parsing", theElementName);
ourLog.warn("{}Multiple repetitions of non-repeatable element '{}' found while parsing", describeLocation(theLocation), theElementName);
}
}
@Override
public void unknownAttribute(IParseLocation theLocation, String theElementName) {
if (myLogErrors) {
ourLog.warn("Unknown attribute '{}' found while parsing", theElementName);
ourLog.warn("{}Unknown attribute '{}' found while parsing",describeLocation(theLocation), theElementName);
}
}
@Override
public void unknownElement(IParseLocation theLocation, String theElementName) {
if (myLogErrors) {
ourLog.warn("Unknown element '{}' found while parsing", theElementName);
ourLog.warn("{}Unknown element '{}' found while parsing", describeLocation(theLocation), theElementName);
}
}
@Override
public void unknownReference(IParseLocation theLocation, String theReference) {
if (myLogErrors) {
ourLog.warn("Resource has invalid reference: {}", theReference);
ourLog.warn("{}Resource has invalid reference: {}", describeLocation(theLocation), theReference);
}
}
@Override
public void extensionContainsValueAndNestedExtensions(IParseLocation theLocation) {
if (myLogErrors) {
ourLog.warn("Extension contains both a value and nested extensions: {}", theLocation);
ourLog.warn("{}Extension contains both a value and nested extensions", describeLocation(theLocation));
}
}

View File

@ -54,6 +54,13 @@ class ParseLocation implements IParseLocation {
@Override
public String toString() {
return defaultString(myParentElementName);
return "[element=\"" + defaultString(myParentElementName) + "\"]";
}
/**
* Factory method
*/
static ParseLocation fromElementName(String theChildName) {
return new ParseLocation(theChildName);
}
}

View File

@ -43,6 +43,7 @@ import ca.uhn.fhir.util.ReflectionUtil;
import ca.uhn.fhir.util.XmlUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.instance.model.api.*;
@ -191,7 +192,7 @@ class ParserState<T> {
return thePrimitiveTarget.newInstance(theDefinition.getInstanceConstructorArguments());
}
public IPrimitiveType<?> getPrimitiveInstance(BaseRuntimeChildDefinition theChild, RuntimePrimitiveDatatypeDefinition thePrimitiveTarget) {
public IPrimitiveType<?> getPrimitiveInstance(BaseRuntimeChildDefinition theChild, RuntimePrimitiveDatatypeDefinition thePrimitiveTarget, String theChildName) {
return thePrimitiveTarget.newInstance(theChild.getInstanceConstructorArguments());
}
@ -456,7 +457,7 @@ class ParserState<T> {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance = newPrimitiveInstance(myDefinition, primitiveTarget);
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance, theLocalPart, primitiveTarget.getName());
push(newState);
return;
}
@ -495,10 +496,10 @@ class ParserState<T> {
private class ElementCompositeState extends BaseState {
private BaseRuntimeElementCompositeDefinition<?> myDefinition;
private IBase myInstance;
private Set<String> myParsedNonRepeatableNames = new HashSet<>();
private String myElementName;
private final BaseRuntimeElementCompositeDefinition<?> myDefinition;
private final IBase myInstance;
private final Set<String> myParsedNonRepeatableNames = new HashSet<>();
private final String myElementName;
ElementCompositeState(PreResourceState thePreResourceState, String theElementName, BaseRuntimeElementCompositeDefinition<?> theDef, IBase theInstance) {
super(thePreResourceState);
@ -583,9 +584,9 @@ class ParserState<T> {
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance;
newChildInstance = getPrimitiveInstance(child, primitiveTarget);
newChildInstance = getPrimitiveInstance(child, primitiveTarget, theChildName);
child.getMutator().addValue(myInstance, newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance, theChildName, primitiveTarget.getName());
push(newState);
return;
}
@ -668,7 +669,7 @@ class ParserState<T> {
public class ElementIdState extends BaseState {
private IBaseElement myElement;
private final IBaseElement myElement;
ElementIdState(ParserState<T>.PreResourceState thePreResourceState, IBaseElement theElement) {
super(thePreResourceState);
@ -689,7 +690,7 @@ class ParserState<T> {
private class ExtensionState extends BaseState {
private IBaseExtension<?, ?> myExtension;
private final IBaseExtension<?, ?> myExtension;
ExtensionState(PreResourceState thePreResourceState, IBaseExtension<?, ?> theExtension) {
super(thePreResourceState);
@ -752,7 +753,7 @@ class ParserState<T> {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance = newInstance(primitiveTarget);
myExtension.setValue(newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance, theLocalPart, primitiveTarget.getName());
push(newState);
return;
}
@ -782,7 +783,7 @@ class ParserState<T> {
public class IdentifiableElementIdState extends BaseState {
private IIdentifiableElement myElement;
private final IIdentifiableElement myElement;
public IdentifiableElementIdState(ParserState<T>.PreResourceState thePreResourceState, IIdentifiableElement theElement) {
super(thePreResourceState);
@ -802,7 +803,7 @@ class ParserState<T> {
}
private class MetaElementState extends BaseState {
private ResourceMetadataMap myMap;
private final ResourceMetadataMap myMap;
public MetaElementState(ParserState<T>.PreResourceState thePreResourceState, ResourceMetadataMap theMap) {
super(thePreResourceState);
@ -824,7 +825,7 @@ class ParserState<T> {
break;
case "lastUpdated":
InstantDt updated = new InstantDt();
push(new PrimitiveState(getPreResourceState(), updated));
push(new PrimitiveState(getPreResourceState(), updated, theLocalPart, "instant"));
myMap.put(ResourceMetadataKeyEnum.UPDATED, updated);
break;
case "security":
@ -850,7 +851,7 @@ class ParserState<T> {
newProfiles = new ArrayList<>(1);
}
IdDt profile = new IdDt();
push(new PrimitiveState(getPreResourceState(), profile));
push(new PrimitiveState(getPreResourceState(), profile, theLocalPart, "id"));
newProfiles.add(profile);
myMap.put(ResourceMetadataKeyEnum.PROFILES, Collections.unmodifiableList(newProfiles));
break;
@ -891,7 +892,7 @@ class ParserState<T> {
private class MetaVersionElementState extends BaseState {
private ResourceMetadataMap myMap;
private final ResourceMetadataMap myMap;
MetaVersionElementState(ParserState<T>.PreResourceState thePreResourceState, ResourceMetadataMap theMap) {
super(thePreResourceState);
@ -1048,6 +1049,8 @@ class ParserState<T> {
}
}
myInstance.setUserData(BaseParser.RESOURCE_CREATED_BY_PARSER, Boolean.TRUE);
populateTarget();
}
@ -1074,7 +1077,7 @@ class ParserState<T> {
*/
for (IBaseResource next : myGlobalResources) {
IIdType id = next.getIdElement();
if (id != null && id.isEmpty() == false) {
if (id != null && !id.isEmpty()) {
String resName = myContext.getResourceDefinition(next).getName();
IIdType idType = id.withResourceType(resName).toUnqualifiedVersionless();
idToResource.put(idType.getValueAsString(), next);
@ -1082,10 +1085,11 @@ class ParserState<T> {
}
for (IBaseReference nextRef : myGlobalReferences) {
if (nextRef.isEmpty() == false && nextRef.getReferenceElement() != null) {
if (!nextRef.isEmpty() && nextRef.getReferenceElement() != null) {
IIdType unqualifiedVersionless = nextRef.getReferenceElement().toUnqualifiedVersionless();
IBaseResource target = idToResource.get(unqualifiedVersionless.getValueAsString());
if (target != null) {
// resource can already be filled with local contained resource by populateTarget()
if (target != null && nextRef.getResource() == null) {
nextRef.setResource(target);
}
}
@ -1267,37 +1271,63 @@ class ParserState<T> {
}
private class PrimitiveState extends BaseState {
private final String myChildName;
private final String myTypeName;
private IPrimitiveType<?> myInstance;
PrimitiveState(PreResourceState thePreResourceState, IPrimitiveType<?> theInstance) {
PrimitiveState(PreResourceState thePreResourceState, IPrimitiveType<?> theInstance, String theChildName, String theTypeName) {
super(thePreResourceState);
myInstance = theInstance;
myChildName = theChildName;
myTypeName = theTypeName;
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
String value = theValue;
if ("value".equals(theName)) {
if ("".equals(theValue)) {
myErrorHandler.invalidValue(null, theValue, "Attribute values must not be empty (\"\")");
if ("".equals(value)) {
ParseLocation location = ParseLocation.fromElementName(myChildName);
myErrorHandler.invalidValue(location, value, "Attribute value must not be empty (\"\")");
} else {
/*
* It may be possible to clean this up somewhat once the following PR is hopefully merged:
* https://github.com/FasterXML/jackson-core/pull/611
*
* See TolerantJsonParser
*/
if ("decimal".equals(myTypeName)) {
if (value != null)
if (value.startsWith(".") && NumberUtils.isDigits(value.substring(1))) {
value = "0" + value;
} else {
while (value.startsWith("00")) {
value = value.substring(1);
}
}
}
try {
myInstance.setValueAsString(theValue);
myInstance.setValueAsString(value);
} catch (DataFormatException | IllegalArgumentException e) {
myErrorHandler.invalidValue(null, theValue, e.getMessage());
ParseLocation location = ParseLocation.fromElementName(myChildName);
myErrorHandler.invalidValue(location, value, e.getMessage());
}
}
} else if ("id".equals(theName)) {
if (myInstance instanceof IIdentifiableElement) {
((IIdentifiableElement) myInstance).setElementSpecificId(theValue);
((IIdentifiableElement) myInstance).setElementSpecificId(value);
} else if (myInstance instanceof IBaseElement) {
((IBaseElement) myInstance).setId(theValue);
((IBaseElement) myInstance).setId(value);
} else if (myInstance instanceof IBaseResource) {
new IdDt(theValue).applyTo((org.hl7.fhir.instance.model.api.IBaseResource) myInstance);
new IdDt(value).applyTo((org.hl7.fhir.instance.model.api.IBaseResource) myInstance);
} else {
myErrorHandler.unknownAttribute(null, theName);
ParseLocation location = ParseLocation.fromElementName(myChildName);
myErrorHandler.unknownAttribute(location, theName);
}
} else {
super.attributeValue(theName, theValue);
super.attributeValue(theName, value);
}
}
@ -1306,17 +1336,6 @@ class ParserState<T> {
pop();
}
// @Override
// public void enteringNewElementExtension(StartElement theElement,
// String theUrlAttr) {
// if (myInstance instanceof ISupportsUndeclaredExtensions) {
// UndeclaredExtension ext = new UndeclaredExtension(theUrlAttr);
// ((ISupportsUndeclaredExtensions)
// myInstance).getUndeclaredExtensions().add(ext);
// push(new ExtensionState(ext));
// }
// }
@Override
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
super.enteringNewElement(theNamespaceUri, theLocalPart);
@ -1342,7 +1361,7 @@ class ParserState<T> {
@Override
public void enteringNewElement(String theNamespace, String theChildName) throws DataFormatException {
if ("id".equals(theChildName)) {
push(new PrimitiveState(getPreResourceState(), myInstance.getId()));
push(new PrimitiveState(getPreResourceState(), myInstance.getId(), theChildName, "id"));
} else if ("meta".equals(theChildName)) {
push(new MetaElementState(getPreResourceState(), myInstance.getResourceMetadata()));
} else {

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.parser;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.json.JsonLikeValue.ScalarType;
import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
import ca.uhn.fhir.util.UrlUtil;
/*
* #%L
@ -31,7 +32,7 @@ import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
* @see IParser#setParserErrorHandler(IParserErrorHandler)
* @see FhirContext#setParserErrorHandler(IParserErrorHandler)
*/
public class StrictErrorHandler implements IParserErrorHandler {
public class StrictErrorHandler extends BaseErrorHandler implements IParserErrorHandler {
@Override
public void containedResourceWithNoId(IParseLocation theLocation) {
@ -46,7 +47,7 @@ public class StrictErrorHandler implements IParserErrorHandler {
@Override
public void invalidValue(IParseLocation theLocation, String theValue, String theError) {
throw new DataFormatException("Invalid attribute value \"" + theValue + "\": " + theError);
throw new DataFormatException(describeLocation(theLocation) + "Invalid attribute value \"" + UrlUtil.sanitizeUrlPart(theValue) + "\": " + theError);
}
@Override
@ -65,27 +66,27 @@ public class StrictErrorHandler implements IParserErrorHandler {
@Override
public void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName) {
throw new DataFormatException("Multiple repetitions of non-repeatable element '" + theElementName + "' found during parse");
throw new DataFormatException(describeLocation(theLocation) + "Multiple repetitions of non-repeatable element '" + theElementName + "' found during parse");
}
@Override
public void unknownAttribute(IParseLocation theLocation, String theAttributeName) {
throw new DataFormatException("Unknown attribute '" + theAttributeName + "' found during parse");
throw new DataFormatException(describeLocation(theLocation) + "Unknown attribute '" + theAttributeName + "' found during parse");
}
@Override
public void unknownElement(IParseLocation theLocation, String theElementName) {
throw new DataFormatException("Unknown element '" + theElementName + "' found during parse");
throw new DataFormatException(describeLocation(theLocation) + "Unknown element '" + theElementName + "' found during parse");
}
@Override
public void unknownReference(IParseLocation theLocation, String theReference) {
throw new DataFormatException("Resource has invalid reference: " + theReference);
throw new DataFormatException(describeLocation(theLocation) + "Resource has invalid reference: " + theReference);
}
@Override
public void extensionContainsValueAndNestedExtensions(IParseLocation theLocation) {
throw new DataFormatException("Extension contains both a value and nested extensions: " + theLocation);
throw new DataFormatException(describeLocation(theLocation) + "Extension contains both a value and nested extensions");
}
}

View File

@ -287,7 +287,8 @@ public class XmlParser extends BaseParser {
for (IBaseResource next : getContainedResources().getContainedResources()) {
IIdType resourceId = getContainedResources().getResourceId(next);
theEventWriter.writeStartElement("contained");
encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(resourceId.getValue()), theEncodeContext);
String value = resourceId.getValue();
encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(value), theEncodeContext);
theEventWriter.writeEndElement();
}
break;
@ -300,7 +301,7 @@ public class XmlParser extends BaseParser {
}
theEventWriter.writeStartElement(theChildName);
theEncodeContext.pushPath(resourceName, true);
encodeResourceToXmlStreamWriter(resource, theEventWriter, false, theEncodeContext);
encodeResourceToXmlStreamWriter(resource, theEventWriter, theIncludedResource, theEncodeContext);
theEncodeContext.popPath();
theEventWriter.writeEndElement();
break;

View File

@ -1,379 +0,0 @@
package ca.uhn.fhir.parser.json;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.io.PushbackReader;
import java.io.Reader;
import java.io.Writer;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import ca.uhn.fhir.parser.DataFormatException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
public class GsonStructure implements JsonLikeStructure {
private enum ROOT_TYPE {OBJECT, ARRAY};
private ROOT_TYPE rootType = null;
private JsonElement nativeRoot = null;
private JsonLikeValue jsonLikeRoot = null;
private GsonWriter jsonLikeWriter = null;
public GsonStructure() {
super();
}
public void setNativeObject (JsonObject json) {
this.rootType = ROOT_TYPE.OBJECT;
this.nativeRoot = json;
}
public void setNativeArray (JsonArray json) {
this.rootType = ROOT_TYPE.ARRAY;
this.nativeRoot = json;
}
@Override
public JsonLikeStructure getInstance() {
return new GsonStructure();
}
@Override
public void load(Reader theReader) throws DataFormatException {
this.load(theReader, false);
}
@Override
public void load(Reader theReader, boolean allowArray) throws DataFormatException {
PushbackReader pbr = new PushbackReader(theReader);
int nextInt;
try {
while(true) {
nextInt = pbr.read();
if (nextInt == -1) {
throw new DataFormatException("Did not find any content to parse");
}
if (nextInt == '{') {
pbr.unread(nextInt);
break;
}
if (Character.isWhitespace(nextInt)) {
continue;
}
if (allowArray) {
if (nextInt == '[') {
pbr.unread(nextInt);
break;
}
throw new DataFormatException("Content does not appear to be FHIR JSON, first non-whitespace character was: '" + (char)nextInt + "' (must be '{' or '[')");
}
throw new DataFormatException("Content does not appear to be FHIR JSON, first non-whitespace character was: '" + (char)nextInt + "' (must be '{')");
}
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
if (nextInt == '{') {
JsonObject root = gson.fromJson(pbr, JsonObject.class);
setNativeObject(root);
} else if (nextInt == '[') {
JsonArray root = gson.fromJson(pbr, JsonArray.class);
setNativeArray(root);
}
} catch (JsonSyntaxException e) {
if (e.getMessage().startsWith("Unexpected char 39")) {
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage() + " - This may indicate that single quotes are being used as JSON escapes where double quotes are required", e);
}
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage(), e);
} catch (Exception e) {
throw new DataFormatException("Failed to parse JSON content, error was: " + e.getMessage(), e);
}
}
@Override
public JsonLikeWriter getJsonLikeWriter (Writer writer) {
if (null == jsonLikeWriter) {
jsonLikeWriter = new GsonWriter(writer);
}
return jsonLikeWriter;
}
@Override
public JsonLikeWriter getJsonLikeWriter () {
if (null == jsonLikeWriter) {
jsonLikeWriter = new GsonWriter();
}
return jsonLikeWriter;
}
@Override
public JsonLikeObject getRootObject() throws DataFormatException {
if (rootType == ROOT_TYPE.OBJECT) {
if (null == jsonLikeRoot) {
jsonLikeRoot = new GsonJsonObject((JsonObject)nativeRoot);
}
return jsonLikeRoot.getAsObject();
}
throw new DataFormatException("Content must be a valid JSON Object. It must start with '{'.");
}
@Override
public JsonLikeArray getRootArray() throws DataFormatException {
if (rootType == ROOT_TYPE.ARRAY) {
if (null == jsonLikeRoot) {
jsonLikeRoot = new GsonJsonArray((JsonArray)nativeRoot);
}
return jsonLikeRoot.getAsArray();
}
throw new DataFormatException("Content must be a valid JSON Array. It must start with '['.");
}
private static class GsonJsonObject extends JsonLikeObject {
private JsonObject nativeObject;
private Set<String> keySet = null;
private Map<String,JsonLikeValue> jsonLikeMap = new LinkedHashMap<String,JsonLikeValue>();
public GsonJsonObject (JsonObject json) {
this.nativeObject = json;
}
@Override
public Object getValue() {
return null;
}
@Override
public Set<String> keySet() {
if (null == keySet) {
Set<Entry<String, JsonElement>> entrySet = nativeObject.entrySet();
keySet = new EntryOrderedSet<String>(entrySet.size());
for (Entry<String,?> entry : entrySet) {
keySet.add(entry.getKey());
}
}
return keySet;
}
@Override
public JsonLikeValue get(String key) {
JsonLikeValue result = null;
if (jsonLikeMap.containsKey(key)) {
result = jsonLikeMap.get(key);
} else {
JsonElement child = nativeObject.get(key);
if (child != null) {
result = new GsonJsonValue(child);
}
jsonLikeMap.put(key, result);
}
return result;
}
}
private static class GsonJsonArray extends JsonLikeArray {
private JsonArray nativeArray;
private Map<Integer,JsonLikeValue> jsonLikeMap = new LinkedHashMap<Integer,JsonLikeValue>();
public GsonJsonArray (JsonArray json) {
this.nativeArray = json;
}
@Override
public Object getValue() {
return null;
}
@Override
public int size() {
return nativeArray.size();
}
@Override
public JsonLikeValue get(int index) {
Integer key = Integer.valueOf(index);
JsonLikeValue result = null;
if (jsonLikeMap.containsKey(key)) {
result = jsonLikeMap.get(key);
} else {
JsonElement child = nativeArray.get(index);
if (child != null) {
result = new GsonJsonValue(child);
}
jsonLikeMap.put(key, result);
}
return result;
}
}
private static class GsonJsonValue extends JsonLikeValue {
private JsonElement nativeValue;
private JsonLikeObject jsonLikeObject = null;
private JsonLikeArray jsonLikeArray = null;
public GsonJsonValue (JsonElement json) {
this.nativeValue = json;
}
@Override
public Object getValue() {
if (nativeValue != null && nativeValue.isJsonPrimitive()) {
if (((JsonPrimitive)nativeValue).isNumber()) {
return nativeValue.getAsNumber();
}
if (((JsonPrimitive)nativeValue).isBoolean()) {
return Boolean.valueOf(nativeValue.getAsBoolean());
}
return nativeValue.getAsString();
}
return null;
}
@Override
public ValueType getJsonType() {
if (null == nativeValue || nativeValue.isJsonNull()) {
return ValueType.NULL;
}
if (nativeValue.isJsonObject()) {
return ValueType.OBJECT;
}
if (nativeValue.isJsonArray()) {
return ValueType.ARRAY;
}
if (nativeValue.isJsonPrimitive()) {
return ValueType.SCALAR;
}
return null;
}
@Override
public ScalarType getDataType() {
if (nativeValue != null && nativeValue.isJsonPrimitive()) {
if (((JsonPrimitive)nativeValue).isNumber()) {
return ScalarType.NUMBER;
}
if (((JsonPrimitive)nativeValue).isString()) {
return ScalarType.STRING;
}
if (((JsonPrimitive)nativeValue).isBoolean()) {
return ScalarType.BOOLEAN;
}
}
return null;
}
@Override
public JsonLikeArray getAsArray() {
if (nativeValue != null && nativeValue.isJsonArray()) {
if (null == jsonLikeArray) {
jsonLikeArray = new GsonJsonArray((JsonArray)nativeValue);
}
}
return jsonLikeArray;
}
@Override
public JsonLikeObject getAsObject() {
if (nativeValue != null && nativeValue.isJsonObject()) {
if (null == jsonLikeObject) {
jsonLikeObject = new GsonJsonObject((JsonObject)nativeValue);
}
}
return jsonLikeObject;
}
@Override
public Number getAsNumber() {
return nativeValue != null ? nativeValue.getAsNumber() : null;
}
@Override
public String getAsString() {
return nativeValue != null ? nativeValue.getAsString() : null;
}
@Override
public boolean getAsBoolean() {
if (nativeValue != null && nativeValue.isJsonPrimitive() && ((JsonPrimitive)nativeValue).isBoolean()) {
return nativeValue.getAsBoolean();
}
return super.getAsBoolean();
}
}
private static class EntryOrderedSet<T> extends AbstractSet<T> {
private transient ArrayList<T> data = null;
public EntryOrderedSet (int initialCapacity) {
data = new ArrayList<T>(initialCapacity);
}
@SuppressWarnings("unused")
public EntryOrderedSet () {
data = new ArrayList<T>();
}
@Override
public int size() {
return data.size();
}
@Override
public boolean contains(Object o) {
return data.contains(o);
}
@SuppressWarnings("unused") // not really.. just not here
public T get(int index) {
return data.get(index);
}
@Override
public boolean add(T element) {
if (data.contains(element)) {
return false;
}
return data.add(element);
}
@Override
public boolean remove(Object o) {
return data.remove(o);
}
@Override
public void clear() {
data.clear();
}
@Override
public Iterator<T> iterator() {
return data.iterator();
}
}
}

View File

@ -1,263 +0,0 @@
package ca.uhn.fhir.parser.json;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.io.IOException;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Stack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.stream.JsonWriter;
public class GsonWriter extends JsonLikeWriter {
private static final Logger log = LoggerFactory.getLogger(GsonWriter.class);
private JsonWriter eventWriter;
private enum BlockType {
NONE, OBJECT, ARRAY
}
private BlockType blockType = BlockType.NONE;
private Stack<BlockType> blockStack = new Stack<BlockType>();
public GsonWriter () {
super();
}
public GsonWriter (Writer writer) {
setWriter(writer);
}
@Override
public JsonLikeWriter init() throws IOException {
eventWriter = new JsonWriter(getWriter());
eventWriter.setSerializeNulls(true);
if (isPrettyPrint()) {
eventWriter.setIndent(" ");
}
blockType = BlockType.NONE;
blockStack.clear();
return this;
}
@Override
public JsonLikeWriter flush() throws IOException {
if (blockType != BlockType.NONE) {
log.error("JsonLikeStreamWriter.flush() called but JSON document is not finished");
}
eventWriter.flush();
getWriter().flush();
return this;
}
@Override
public void close() throws IOException {
eventWriter.close();
getWriter().close();
}
@Override
public JsonLikeWriter beginObject() throws IOException {
blockStack.push(blockType);
blockType = BlockType.OBJECT;
eventWriter.beginObject();
return this;
}
@Override
public JsonLikeWriter beginArray() throws IOException {
blockStack.push(blockType);
blockType = BlockType.ARRAY;
eventWriter.beginArray();
return this;
}
@Override
public JsonLikeWriter beginObject(String name) throws IOException {
blockStack.push(blockType);
blockType = BlockType.OBJECT;
eventWriter.name(name);
eventWriter.beginObject();
return this;
}
@Override
public JsonLikeWriter beginArray(String name) throws IOException {
blockStack.push(blockType);
blockType = BlockType.ARRAY;
eventWriter.name(name);
eventWriter.beginArray();
return this;
}
@Override
public JsonLikeWriter write(String value) throws IOException {
eventWriter.value(value);
return this;
}
@Override
public JsonLikeWriter write(BigInteger value) throws IOException {
eventWriter.value(value);
return this;
}
@Override
public JsonLikeWriter write(BigDecimal value) throws IOException {
eventWriter.value(value);
return this;
}
@Override
public JsonLikeWriter write(long value) throws IOException {
eventWriter.value(value);
return this;
}
@Override
public JsonLikeWriter write(double value) throws IOException {
eventWriter.value(value);
return this;
}
@Override
public JsonLikeWriter write(Boolean value) throws IOException {
eventWriter.value(value);
return this;
}
@Override
public JsonLikeWriter write(boolean value) throws IOException {
eventWriter.value(value);
return this;
}
@Override
public JsonLikeWriter writeNull() throws IOException {
eventWriter.nullValue();
return this;
}
@Override
public JsonLikeWriter write(String name, String value) throws IOException {
eventWriter.name(name);
eventWriter.value(value);
return this;
}
@Override
public JsonLikeWriter write(String name, BigInteger value) throws IOException {
eventWriter.name(name);
eventWriter.value(value);
return this;
}
@Override
public JsonLikeWriter write(String name, BigDecimal value) throws IOException {
eventWriter.name(name);
eventWriter.value(value);
return this;
}
@Override
public JsonLikeWriter write(String name, long value) throws IOException {
eventWriter.name(name);
eventWriter.value(value);
return this;
}
@Override
public JsonLikeWriter write(String name, double value) throws IOException {
eventWriter.name(name);
eventWriter.value(value);
return this;
}
@Override
public JsonLikeWriter write(String name, Boolean value) throws IOException {
eventWriter.name(name);
eventWriter.value(value);
return this;
}
@Override
public JsonLikeWriter write(String name, boolean value) throws IOException {
eventWriter.name(name);
eventWriter.value(value);
return this;
}
@Override
public JsonLikeWriter writeNull(String name) throws IOException {
eventWriter.name(name);
eventWriter.nullValue();
return this;
}
@Override
public JsonLikeWriter endObject() throws IOException {
if (blockType == BlockType.NONE) {
log.error("JsonLikeStreamWriter.endObject(); called with no active JSON document");
} else {
if (blockType != BlockType.OBJECT) {
log.error("JsonLikeStreamWriter.endObject(); called outside a JSON object. (Use endArray() instead?)");
eventWriter.endArray();
} else {
eventWriter.endObject();
}
blockType = blockStack.pop();
}
return this;
}
@Override
public JsonLikeWriter endArray() throws IOException {
if (blockType == BlockType.NONE) {
log.error("JsonLikeStreamWriter.endArray(); called with no active JSON document");
} else {
if (blockType != BlockType.ARRAY) {
log.error("JsonLikeStreamWriter.endArray(); called outside a JSON array. (Use endObject() instead?)");
eventWriter.endObject();
} else {
eventWriter.endArray();
}
blockType = blockStack.pop();
}
return this;
}
@Override
public JsonLikeWriter endBlock() throws IOException {
if (blockType == BlockType.NONE) {
log.error("JsonLikeStreamWriter.endBlock(); called with no active JSON document");
} else {
if (blockType == BlockType.ARRAY) {
eventWriter.endArray();
} else {
eventWriter.endObject();
}
blockType = blockStack.pop();
}
return this;
}
}

View File

@ -53,20 +53,4 @@ public abstract class JsonLikeObject extends JsonLikeValue {
public abstract JsonLikeValue get (String key);
public String getString (String key) {
JsonLikeValue value = this.get(key);
if (null == value) {
throw new NullPointerException("Json object missing element named \""+key+"\"");
}
return value.getAsString();
}
public String getString (String key, String defaultValue) {
String result = defaultValue;
JsonLikeValue value = this.get(key);
if (value != null) {
result = value.getAsString();
}
return result;
}
}

View File

@ -19,11 +19,12 @@
*/
package ca.uhn.fhir.parser.json;
import ca.uhn.fhir.parser.DataFormatException;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import ca.uhn.fhir.parser.DataFormatException;
/**
* This interface is the generic representation of any sort of data
* structure that looks and smells like JSON. These data structures
@ -33,7 +34,7 @@ import ca.uhn.fhir.parser.DataFormatException;
* @author Bill.Denton
*/
public interface JsonLikeStructure {
public JsonLikeStructure getInstance();
JsonLikeStructure getInstance();
/**
* Parse the JSON document into the Json-like structure
@ -43,10 +44,13 @@ public interface JsonLikeStructure {
* process the JSON input stream
* @throws DataFormatException when invalid JSON is received
*/
public void load (Reader theReader) throws DataFormatException;
public void load (Reader theReader, boolean allowArray) throws DataFormatException;
public JsonLikeObject getRootObject () throws DataFormatException;
public JsonLikeArray getRootArray () throws DataFormatException;
public JsonLikeWriter getJsonLikeWriter ();
public JsonLikeWriter getJsonLikeWriter (Writer writer);
void load(Reader theReader) throws DataFormatException;
void load(Reader theReader, boolean allowArray) throws DataFormatException;
JsonLikeObject getRootObject() throws DataFormatException;
JsonLikeWriter getJsonLikeWriter();
JsonLikeWriter getJsonLikeWriter(Writer writer) throws IOException;
}

Some files were not shown because too many files have changed in this diff Show More