Merge branch 'master' into ja_20200206_multitenancy

This commit is contained in:
jamesagnew 2020-03-27 05:50:08 -04:00
commit ec6fe70acb
667 changed files with 12049 additions and 49301 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

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> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>4.3.0-SNAPSHOT</version> <version>5.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -181,7 +181,16 @@
</reports> </reports>
<configuration> <configuration>
<links> <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> <link>https://docs.oracle.com/javaee/7/api/</link>
</links> </links>
<additionalparam>-Xdoclint:none</additionalparam> <additionalparam>-Xdoclint:none</additionalparam>

View File

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

View File

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

View File

@ -1,15 +1,22 @@
package ca.uhn.fhir.context; package ca.uhn.fhir.context;
import ca.uhn.fhir.context.api.AddProfileTagEnum; import ca.uhn.fhir.context.api.AddProfileTagEnum;
import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.fluentpath.IFluentPath; import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.fhirpath.IFhirPath;
import ca.uhn.fhir.i18n.HapiLocalizer; import ca.uhn.fhir.i18n.HapiLocalizer;
import ca.uhn.fhir.model.api.IElement; import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.api.IFhirVersion;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.view.ViewGenerator; import ca.uhn.fhir.model.view.ViewGenerator;
import ca.uhn.fhir.narrative.INarrativeGenerator; 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.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.client.api.IBasicClient; import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IGenericClient; 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.util.VersionUtil;
import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.FhirValidator;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.jena.riot.Lang; import org.apache.jena.riot.Lang;
import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseBundle;
@ -97,7 +103,7 @@ public class FhirContext {
private Collection<Class<? extends IBaseResource>> myResourceTypesToScan; private Collection<Class<? extends IBaseResource>> myResourceTypesToScan;
private volatile IRestfulClientFactory myRestfulClientFactory; private volatile IRestfulClientFactory myRestfulClientFactory;
private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition; private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
private IContextValidationSupport<?, ?, ?, ?, ?, ?> myValidationSupport; private IValidationSupport myValidationSupport;
private Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> myVersionToNameToResourceType = Collections.emptyMap(); private Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> myVersionToNameToResourceType = Collections.emptyMap();
/** /**
@ -371,6 +377,19 @@ 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 * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library. * for extending the core library.
@ -543,16 +562,37 @@ public class FhirContext {
/** /**
* Returns the validation support module configured for this context, creating a default * 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 * method
* *
* @see #setValidationSupport(IContextValidationSupport) * @see #setValidationSupport(IValidationSupport)
*/ */
public IContextValidationSupport<?, ?, ?, ?, ?, ?> getValidationSupport() { public IValidationSupport getValidationSupport() {
if (myValidationSupport == null) { IValidationSupport retVal = myValidationSupport;
myValidationSupport = myVersion.createValidationSupport(); 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";
}
myValidationSupport = retVal;
} }
return myValidationSupport; return retVal;
} }
/** /**
@ -560,7 +600,7 @@ public class FhirContext {
* is used to supply underlying infrastructure such as conformance resources (StructureDefinition, ValueSet, etc) * 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 * 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; myValidationSupport = theValidationSupport;
} }
@ -585,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 * 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. * configured on the context at the time this method is called.
* <p> * <p>
* In other words, call {@link #setValidationSupport(IContextValidationSupport)} before * In other words, you may wish to call {@link #setValidationSupport(IValidationSupport)} before
* calling {@link #newFluentPath()} * calling {@link #newFluentPath()}
* </p> * </p>
* <p> * <p>
@ -600,10 +649,10 @@ public class FhirContext {
* {@link UnsupportedOperationException} * {@link UnsupportedOperationException}
* </p> * </p>
* *
* @since 2.2 * @since 5.0.0
*/ */
public IFluentPath newFluentPath() { public IFhirPath newFhirPath() {
return myVersion.createFluentPathExecutor(this); return myVersion.createFhirPathExecutor(this);
} }
/** /**
@ -641,7 +690,6 @@ public class FhirContext {
return new RDFParser(this, myParserErrorHandler, Lang.TURTLE); return new RDFParser(this, myParserErrorHandler, Lang.TURTLE);
} }
/** /**
* Instantiates a new client instance. This method requires an interface which is defined specifically for your use * 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", * cases to contain methods for each of the RESTful operations you wish to implement (e.g. "read ImagingStudy",
@ -861,19 +909,6 @@ public class FhirContext {
myParserErrorHandler = theParserErrorHandler; 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"}) @SuppressWarnings({"cast"})
private List<Class<? extends IElement>> toElementList(final Collection<Class<? extends IBaseResource>> theResourceTypes) { private List<Class<? extends IElement>> toElementList(final Collection<Class<? extends IBaseResource>> theResourceTypes) {
if (theResourceTypes == null) { if (theResourceTypes == null) {

View File

@ -1,10 +1,8 @@
package ca.uhn.fhir.jpa.dao.r5; package ca.uhn.fhir.context.support;
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport; /*-
/*
* #%L * #%L
* HAPI FHIR JPA Server * HAPI FHIR - Core Library
* %% * %%
* Copyright (C) 2014 - 2020 University Health Network * Copyright (C) 2014 - 2020 University Health Network
* %% * %%
@ -22,6 +20,17 @@ import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
* #L% * #L%
*/ */
public interface IJpaValidationSupportR5 extends IValidationSupport { public class ConceptValidationOptions {
// nothing yet
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.context.FhirContext;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.ParametersUtil; 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.IBase;
import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; 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 * various functions that can be provided by validation and terminology
* services. * services.
* <p> * <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 * Implementations are not required to implement all of the functions
* in this interface; in fact it is expected that most won't. Any * in this interface; in fact it is expected that most won't. Any
* methods which are not implemented may simply return <code>null</code> * 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>
* <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 * Expands the given portion of a ValueSet
* *
* @param theInclude The portion to include * @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
* @return The expansion * 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 * Load and return all conformance resources associated with this
* validation support module. This method may return null if it doesn't * validation support module. This method may return null if it doesn't
* make sense for a given module. * make sense for a given module.
*/ */
List<IBaseResource> fetchAllConformanceResources(FhirContext theContext); default List<IBaseResource> fetchAllConformanceResources() {
return null;
}
/** /**
* Load and return all possible structure definitions * 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 * 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 * @param theSystem The code system
* @return The valueset (must not be null, but can be an empty ValueSet) * @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 * Loads a resource needed by the validation (a StructureDefinition, or a
* ValueSet) * ValueSet)
* *
* @param theContext The HAPI FHIR Context object current in use by the validator * @param theClass The type of the resource to load
* @param theClass The type of the resource to load * @param theUri The resource URI
* @param theUri The resource URI
* @return Returns the resource, or <code>null</code> if no resource with the * @return Returns the resource, or <code>null</code> if no resource with the
* given URI can be found * 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 * Returns <code>true</code> if codes in the given code system can be expanded
* or validated * or validated
* *
* @param theSystem The URI for the code system, e.g. <code>"http://loinc.org"</code> * @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 * @return Returns <code>true</code> if codes in the given code system can be
* validated * validated
*/ */
boolean isCodeSystemSupported(FhirContext theContext, String theSystem); default boolean isCodeSystemSupported(IValidationSupport theRootValidationSupport, String theSystem) {
return false;
}
/** /**
* Fetch the given ValueSet by URL * 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 * 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" * 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. * binding fields (e.g. <code>Observation.code</code> in the default profile.
* *
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>" * @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
* @param theCode The code, e.g. "<code>1234-5</code>" * 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 theDisplay The display name, if it should also be validated * @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 * @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 * 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" * 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. * binding fields (e.g. <code>Observation.code</code> in the default profile.
* *
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>" * @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
* @param theCode The code, e.g. "<code>1234-5</code>" * 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 theDisplay The display name, if it should also be validated * @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
* @param theValueSet The ValueSet to validate against. Must not be null, and must be a ValueSet resource. * @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 * @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 * 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
* @param theSystem The CodeSystem URL * 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 theCode The code * @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 { class ConceptDesignation {
private String myLanguage; 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 { class CodeValidationResult {
private IBase myDefinition; private String myCode;
private String myMessage; private String myMessage;
private Enum mySeverity; private IssueSeverity mySeverity;
private String myCodeSystemName; private String myCodeSystemName;
private String myCodeSystemVersion; private String myCodeSystemVersion;
private List<BaseConceptProperty> myProperties; private List<BaseConceptProperty> myProperties;
private String myDisplay; private String myDisplay;
public CodeValidationResult(IBase theDefinition) { public CodeValidationResult() {
this.myDefinition = theDefinition; super();
}
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 String getDisplay() { public String getDisplay() {
return myDisplay; return myDisplay;
} }
public IBase asConceptDefinition() { public CodeValidationResult setDisplay(String theDisplay) {
return myDefinition; myDisplay = theDisplay;
return this;
}
public String getCode() {
return myCode;
}
public CodeValidationResult setCode(String theCode) {
myCode = theCode;
return this;
} }
String getCodeSystemName() { String getCodeSystemName() {
return myCodeSystemName; return myCodeSystemName;
} }
public void setCodeSystemName(String theCodeSystemName) { public CodeValidationResult setCodeSystemName(String theCodeSystemName) {
myCodeSystemName = theCodeSystemName; myCodeSystemName = theCodeSystemName;
return this;
} }
public String getCodeSystemVersion() { public String getCodeSystemVersion() {
return myCodeSystemVersion; return myCodeSystemVersion;
} }
public void setCodeSystemVersion(String theCodeSystemVersion) { public CodeValidationResult setCodeSystemVersion(String theCodeSystemVersion) {
myCodeSystemVersion = theCodeSystemVersion; myCodeSystemVersion = theCodeSystemVersion;
return this;
} }
public String getMessage() { public String getMessage() {
return myMessage; return myMessage;
} }
public CodeValidationResult setMessage(String theMessage) {
myMessage = theMessage;
return this;
}
public List<BaseConceptProperty> getProperties() { public List<BaseConceptProperty> getProperties() {
return myProperties; return myProperties;
} }
@ -318,12 +450,17 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
myProperties = theProperties; myProperties = theProperties;
} }
public Enum getSeverity() { public IssueSeverity getSeverity() {
return mySeverity; return mySeverity;
} }
public CodeValidationResult setSeverity(IssueSeverity theSeverity) {
mySeverity = theSeverity;
return this;
}
public boolean isOk() { public boolean isOk() {
return myDefinition != null; return isNotBlank(myCode);
} }
public LookupCodeResult asLookupCodeResult(String theSearchedForSystem, String theSearchedForCode) { public LookupCodeResult asLookupCodeResult(String theSearchedForSystem, String theSearchedForCode) {
@ -339,6 +476,49 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
return retVal; 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 { class LookupCodeResult {
@ -350,7 +530,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
private boolean myFound; private boolean myFound;
private String mySearchedForCode; private String mySearchedForCode;
private String mySearchedForSystem; private String mySearchedForSystem;
private List<IContextValidationSupport.BaseConceptProperty> myProperties; private List<IValidationSupport.BaseConceptProperty> myProperties;
private List<ConceptDesignation> myDesignations; private List<ConceptDesignation> myDesignations;
/** /**
@ -367,7 +547,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
return myProperties; return myProperties;
} }
public void setProperties(List<IContextValidationSupport.BaseConceptProperty> theProperties) { public void setProperties(List<IValidationSupport.BaseConceptProperty> theProperties) {
myProperties = theProperties; myProperties = theProperties;
} }
@ -466,7 +646,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
for (IContextValidationSupport.BaseConceptProperty next : myProperties) { for (IValidationSupport.BaseConceptProperty next : myProperties) {
if (!properties.isEmpty()) { if (!properties.isEmpty()) {
if (!properties.contains(next.getPropertyName())) { 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"); IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property");
ParametersUtil.addPartCode(theContext, property, "code", next.getPropertyName()); ParametersUtil.addPartCode(theContext, property, "code", next.getPropertyName());
if (next instanceof IContextValidationSupport.StringConceptProperty) { if (next instanceof IValidationSupport.StringConceptProperty) {
IContextValidationSupport.StringConceptProperty prop = (IContextValidationSupport.StringConceptProperty) next; IValidationSupport.StringConceptProperty prop = (IValidationSupport.StringConceptProperty) next;
ParametersUtil.addPartString(theContext, property, "value", prop.getValue()); ParametersUtil.addPartString(theContext, property, "value", prop.getValue());
} else if (next instanceof IContextValidationSupport.CodingConceptProperty) { } else if (next instanceof IValidationSupport.CodingConceptProperty) {
IContextValidationSupport.CodingConceptProperty prop = (IContextValidationSupport.CodingConceptProperty) next; IValidationSupport.CodingConceptProperty prop = (IValidationSupport.CodingConceptProperty) next;
ParametersUtil.addPartCoding(theContext, property, "value", prop.getCodeSystem(), prop.getCode(), prop.getDisplay()); ParametersUtil.addPartCoding(theContext, property, "value", prop.getCodeSystem(), prop.getCode(), prop.getDisplay());
} else { } else {
throw new IllegalStateException("Don't know how to handle " + next.getClass()); 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 * #%L
@ -23,18 +23,18 @@ package ca.uhn.fhir.fluentpath;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 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 * for any reason
*/ */
public class FluentPathExecutionException extends InternalErrorException { public class FhirPathExecutionException extends InternalErrorException {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public FluentPathExecutionException(Throwable theCause) { public FhirPathExecutionException(Throwable theCause) {
super(theCause); super(theCause);
} }
public FluentPathExecutionException(String theMessage) { public FhirPathExecutionException(String theMessage) {
super(theMessage); super(theMessage);
} }

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.fluentpath; package ca.uhn.fhir.fhirpath;
/* /*
* #%L * #%L
@ -25,7 +25,7 @@ import java.util.Optional;
import org.hl7.fhir.instance.model.api.IBase; 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 * 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 List<ResourceBundle> myBundle = new ArrayList<>();
private final Map<String, String> myHardcodedMessages = new HashMap<>(); private final Map<String, String> myHardcodedMessages = new HashMap<>();
private String[] myBundleNames; private String[] myBundleNames;
private Locale myLocale = Locale.getDefault();
public HapiLocalizer() { public HapiLocalizer() {
this(HapiLocalizer.class.getPackage().getName() + ".hapi-messages"); this(HapiLocalizer.class.getPackage().getName() + ".hapi-messages");
@ -164,7 +165,11 @@ public class HapiLocalizer {
} }
} }
/** public Locale getLocale() {
return myLocale;
}
/**
* This <b>global setting</b> causes the localizer to fail if any attempts * 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 * are made to retrieve a key that does not exist. This method is primarily for
* unit tests. * unit tests.

View File

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

View File

@ -1,11 +1,8 @@
package ca.uhn.fhir.jpa.dao.r4; package ca.uhn.fhir.model.api;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport; /*-
import org.hl7.fhir.r4.model.StructureDefinition;
/*
* #%L * #%L
* HAPI FHIR JPA Server * HAPI FHIR - Core Library
* %% * %%
* Copyright (C) 2014 - 2020 University Health Network * Copyright (C) 2014 - 2020 University Health Network
* %% * %%
@ -23,6 +20,10 @@ import org.hl7.fhir.r4.model.StructureDefinition;
* #L% * #L%
*/ */
public interface IJpaValidationSupportR4 extends IValidationSupport { import com.fasterxml.jackson.annotation.JsonAutoDetect;
// nothing yet 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.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum; 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.narrative.INarrativeGenerator;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.instance.model.api.IBase; 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)) { if (theFhirContext.getVersion().getVersion().isOlderThan(FhirVersionEnum.DSTU3)) {
return Collections.singletonList(theResource); return Collections.singletonList(theResource);
} }
IFluentPath fhirPath = theFhirContext.newFluentPath(); IFhirPath fhirPath = theFhirContext.newFluentPath();
return fhirPath.evaluate(theResource, theContextPath, IBase.class); return fhirPath.evaluate(theResource, theContextPath, IBase.class);
} }

View File

@ -22,20 +22,29 @@ package ca.uhn.fhir.parser;
import ca.uhn.fhir.context.*; import ca.uhn.fhir.context.*;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum; 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.api.annotation.Child;
import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.composite.BaseContainedDt; import ca.uhn.fhir.model.base.composite.BaseContainedDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.narrative.INarrativeGenerator; 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.ScalarType;
import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType; 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.rest.api.EncodingEnum;
import ca.uhn.fhir.util.ElementUtil; 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.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.apache.commons.text.WordUtils; import org.apache.commons.text.WordUtils;
@ -45,11 +54,17 @@ import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.io.Writer; import java.io.Writer;
import java.math.BigDecimal; 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.ID_DATATYPE;
import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.PRIMITIVE_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 * 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); theEventWriter.beginObject(arrayName);
} }
private JsonLikeWriter createJsonWriter(Writer theWriter) { private JsonLikeWriter createJsonWriter(Writer theWriter) throws IOException {
JsonLikeStructure jsonStructure = new GsonStructure(); JsonLikeStructure jsonStructure = new JacksonStructure();
JsonLikeWriter retVal = jsonStructure.getJsonLikeWriter(theWriter); return jsonStructure.getJsonLikeWriter(theWriter);
return retVal;
} }
public void doEncodeResourceToJsonLikeWriter(IBaseResource theResource, JsonLikeWriter theEventWriter, EncodeContext theEncodeContext) throws IOException { 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 { protected void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter, EncodeContext theEncodeContext) throws IOException {
JsonLikeWriter eventWriter = createJsonWriter(theWriter); JsonLikeWriter eventWriter = createJsonWriter(theWriter);
doEncodeResourceToJsonLikeWriter(theResource, eventWriter, theEncodeContext); doEncodeResourceToJsonLikeWriter(theResource, eventWriter, theEncodeContext);
eventWriter.close();
} }
@Override @Override
public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) { public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) {
JsonLikeStructure jsonStructure = new GsonStructure(); JsonLikeStructure jsonStructure = new JacksonStructure();
jsonStructure.load(theReader); jsonStructure.load(theReader);
T retVal = doParseResource(theResourceType, jsonStructure); T retVal = doParseResource(theResourceType, jsonStructure);
@ -302,19 +317,14 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
} }
case CONTAINED_RESOURCE_LIST: case CONTAINED_RESOURCE_LIST:
case CONTAINED_RESOURCES: { 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(); List<IBaseResource> containedResources = getContainedResources().getContainedResources();
if (containedResources.size() > 0) { if (containedResources.size() > 0) {
beginArray(theEventWriter, theChildName); beginArray(theEventWriter, theChildName);
for (IBaseResource next : containedResources) { for (IBaseResource next : containedResources) {
IIdType resourceId = getContainedResources().getResourceId(next); 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(); theEventWriter.endArray();
@ -344,7 +354,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
RuntimeResourceDefinition def = myContext.getResourceDefinition(resource); RuntimeResourceDefinition def = myContext.getResourceDefinition(resource);
theEncodeContext.pushPath(def.getName(), true); theEncodeContext.pushPath(def.getName(), true);
encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, false, theEncodeContext); encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, theContainedResource, theEncodeContext);
theEncodeContext.popPath(); theEncodeContext.popPath();
break; break;
@ -418,10 +428,10 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
String currentChildName = null; String currentChildName = null;
boolean inArray = false; boolean inArray = false;
ArrayList<ArrayList<HeldExtension>> extensions = new ArrayList<ArrayList<HeldExtension>>(0); ArrayList<ArrayList<HeldExtension>> extensions = new ArrayList<>(0);
ArrayList<ArrayList<HeldExtension>> modifierExtensions = new ArrayList<ArrayList<HeldExtension>>(0); ArrayList<ArrayList<HeldExtension>> modifierExtensions = new ArrayList<>(0);
ArrayList<ArrayList<String>> comments = new ArrayList<ArrayList<String>>(0); ArrayList<ArrayList<String>> comments = new ArrayList<>(0);
ArrayList<String> ids = new ArrayList<String>(0); ArrayList<String> ids = new ArrayList<>(0);
int valueIdx = 0; int valueIdx = 0;
for (IBase nextValue : values) { for (IBase nextValue : values) {
@ -1107,7 +1117,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
} else { } else {
// must be a SCALAR // must be a SCALAR
theState.enteringNewElement(null, theName); theState.enteringNewElement(null, theName);
theState.attributeValue("value", theJsonVal.getAsString()); String asString = theJsonVal.getAsString();
theState.attributeValue("value", asString);
parseAlternates(theAlternateVal, theState, theAlternateName, theAlternateName); parseAlternates(theAlternateVal, theState, theAlternateName, theAlternateName);
theState.endingElement(); theState.endingElement();
} }
@ -1376,11 +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 { private static void write(JsonLikeWriter theWriter, String theName, String theValue) throws IOException {
theWriter.write(theName, theValue); theWriter.write(theName, theValue);
} }

View File

@ -1074,7 +1074,7 @@ class ParserState<T> {
*/ */
for (IBaseResource next : myGlobalResources) { for (IBaseResource next : myGlobalResources) {
IIdType id = next.getIdElement(); IIdType id = next.getIdElement();
if (id != null && id.isEmpty() == false) { if (id != null && !id.isEmpty()) {
String resName = myContext.getResourceDefinition(next).getName(); String resName = myContext.getResourceDefinition(next).getName();
IIdType idType = id.withResourceType(resName).toUnqualifiedVersionless(); IIdType idType = id.withResourceType(resName).toUnqualifiedVersionless();
idToResource.put(idType.getValueAsString(), next); idToResource.put(idType.getValueAsString(), next);
@ -1082,10 +1082,11 @@ class ParserState<T> {
} }
for (IBaseReference nextRef : myGlobalReferences) { for (IBaseReference nextRef : myGlobalReferences) {
if (nextRef.isEmpty() == false && nextRef.getReferenceElement() != null) { if (!nextRef.isEmpty() && nextRef.getReferenceElement() != null) {
IIdType unqualifiedVersionless = nextRef.getReferenceElement().toUnqualifiedVersionless(); IIdType unqualifiedVersionless = nextRef.getReferenceElement().toUnqualifiedVersionless();
IBaseResource target = idToResource.get(unqualifiedVersionless.getValueAsString()); 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); nextRef.setResource(target);
} }
} }

View File

@ -287,7 +287,8 @@ public class XmlParser extends BaseParser {
for (IBaseResource next : getContainedResources().getContainedResources()) { for (IBaseResource next : getContainedResources().getContainedResources()) {
IIdType resourceId = getContainedResources().getResourceId(next); IIdType resourceId = getContainedResources().getResourceId(next);
theEventWriter.writeStartElement("contained"); theEventWriter.writeStartElement("contained");
encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(resourceId.getValue()), theEncodeContext); String value = resourceId.getValue();
encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(value), theEncodeContext);
theEventWriter.writeEndElement(); theEventWriter.writeEndElement();
} }
break; break;
@ -300,7 +301,7 @@ public class XmlParser extends BaseParser {
} }
theEventWriter.writeStartElement(theChildName); theEventWriter.writeStartElement(theChildName);
theEncodeContext.pushPath(resourceName, true); theEncodeContext.pushPath(resourceName, true);
encodeResourceToXmlStreamWriter(resource, theEventWriter, false, theEncodeContext); encodeResourceToXmlStreamWriter(resource, theEventWriter, theIncludedResource, theEncodeContext);
theEncodeContext.popPath(); theEncodeContext.popPath();
theEventWriter.writeEndElement(); theEventWriter.writeEndElement();
break; 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 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,34 +19,38 @@
*/ */
package ca.uhn.fhir.parser.json; package ca.uhn.fhir.parser.json;
import ca.uhn.fhir.parser.DataFormatException;
import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.io.Writer; import java.io.Writer;
import ca.uhn.fhir.parser.DataFormatException;
/** /**
* This interface is the generic representation of any sort of data * This interface is the generic representation of any sort of data
* structure that looks and smells like JSON. These data structures * structure that looks and smells like JSON. These data structures
* can be abstractly viewed as a <code.Map</code> or <code>List</code> * can be abstractly viewed as a <code.Map</code> or <code>List</code>
* whose members are other Maps, Lists, or scalars (Strings, Numbers, Boolean) * whose members are other Maps, Lists, or scalars (Strings, Numbers, Boolean)
* *
* @author Bill.Denton * @author Bill.Denton
*/ */
public interface JsonLikeStructure { public interface JsonLikeStructure {
public JsonLikeStructure getInstance(); JsonLikeStructure getInstance();
/** /**
* Parse the JSON document into the Json-like structure * Parse the JSON document into the Json-like structure
* so that it can be navigated. * so that it can be navigated.
* *
* @param theReader a <code>Reader</code> that will * @param theReader a <code>Reader</code> that will
* process the JSON input stream * process the JSON input stream
* @throws DataFormatException when invalid JSON is received * @throws DataFormatException when invalid JSON is received
*/ */
public void load (Reader theReader) throws DataFormatException; void load(Reader theReader) throws DataFormatException;
public void load (Reader theReader, boolean allowArray) throws DataFormatException;
public JsonLikeObject getRootObject () throws DataFormatException; void load(Reader theReader, boolean allowArray) throws DataFormatException;
public JsonLikeArray getRootArray () throws DataFormatException;
public JsonLikeWriter getJsonLikeWriter (); JsonLikeObject getRootObject() throws DataFormatException;
public JsonLikeWriter getJsonLikeWriter (Writer writer);
JsonLikeWriter getJsonLikeWriter();
JsonLikeWriter getJsonLikeWriter(Writer writer) throws IOException;
} }

View File

@ -29,55 +29,73 @@ public abstract class JsonLikeWriter {
private boolean prettyPrint; private boolean prettyPrint;
private Writer writer; private Writer writer;
public void setPrettyPrint (boolean tf) {
prettyPrint = tf;
}
public boolean isPrettyPrint () {
return prettyPrint;
}
public void setWriter (Writer writer) {
this.writer = writer;
}
public Writer getWriter () {
return writer;
}
public abstract JsonLikeWriter init () throws IOException;
public abstract JsonLikeWriter flush () throws IOException;
public abstract void close () throws IOException;
public abstract JsonLikeWriter beginObject () throws IOException;
public abstract JsonLikeWriter beginArray () throws IOException;
public abstract JsonLikeWriter beginObject (String name) throws IOException;
public abstract JsonLikeWriter beginArray (String name) throws IOException;
public abstract JsonLikeWriter write (String value) throws IOException;
public abstract JsonLikeWriter write (BigInteger value) throws IOException;
public abstract JsonLikeWriter write (BigDecimal value) throws IOException;
public abstract JsonLikeWriter write (long value) throws IOException;
public abstract JsonLikeWriter write (double value) throws IOException;
public abstract JsonLikeWriter write (Boolean value) throws IOException;
public abstract JsonLikeWriter write (boolean value) throws IOException;
public abstract JsonLikeWriter writeNull () throws IOException;
public abstract JsonLikeWriter write (String name, String value) throws IOException;
public abstract JsonLikeWriter write (String name, BigInteger value) throws IOException;
public abstract JsonLikeWriter write (String name, BigDecimal value) throws IOException;
public abstract JsonLikeWriter write (String name, long value) throws IOException;
public abstract JsonLikeWriter write (String name, double value) throws IOException;
public abstract JsonLikeWriter write (String name, Boolean value) throws IOException;
public abstract JsonLikeWriter write (String name, boolean value) throws IOException;
public abstract JsonLikeWriter writeNull (String name) throws IOException;
public abstract JsonLikeWriter endObject () throws IOException;
public abstract JsonLikeWriter endArray () throws IOException;
public abstract JsonLikeWriter endBlock () throws IOException;
public JsonLikeWriter() { public JsonLikeWriter() {
super(); super();
} }
public boolean isPrettyPrint() {
return prettyPrint;
}
public void setPrettyPrint(boolean tf) {
prettyPrint = tf;
}
public Writer getWriter() {
return writer;
}
public void setWriter(Writer writer) {
this.writer = writer;
}
public abstract JsonLikeWriter init() throws IOException;
public abstract JsonLikeWriter flush() throws IOException;
public abstract void close() throws IOException;
public abstract JsonLikeWriter beginObject() throws IOException;
public abstract JsonLikeWriter beginObject(String name) throws IOException;
public abstract JsonLikeWriter beginArray(String name) throws IOException;
public abstract JsonLikeWriter write(String value) throws IOException;
public abstract JsonLikeWriter write(BigInteger value) throws IOException;
public abstract JsonLikeWriter write(BigDecimal value) throws IOException;
public abstract JsonLikeWriter write(long value) throws IOException;
public abstract JsonLikeWriter write(double value) throws IOException;
public abstract JsonLikeWriter write(Boolean value) throws IOException;
public abstract JsonLikeWriter write(boolean value) throws IOException;
public abstract JsonLikeWriter writeNull() throws IOException;
public abstract JsonLikeWriter write(String name, String value) throws IOException;
public abstract JsonLikeWriter write(String name, BigInteger value) throws IOException;
public abstract JsonLikeWriter write(String name, BigDecimal value) throws IOException;
public abstract JsonLikeWriter write(String name, long value) throws IOException;
public abstract JsonLikeWriter write(String name, double value) throws IOException;
public abstract JsonLikeWriter write(String name, Boolean value) throws IOException;
public abstract JsonLikeWriter write(String name, boolean value) throws IOException;
public abstract JsonLikeWriter endObject() throws IOException;
public abstract JsonLikeWriter endArray() throws IOException;
public abstract JsonLikeWriter endBlock() throws IOException;
} }

View File

@ -0,0 +1,392 @@
package ca.uhn.fhir.parser.json.jackson;
/*-
* #%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.parser.DataFormatException;
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.JsonLikeWriter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.DecimalNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.jena.tdb.setup.BuilderStdDB;
import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
import java.io.Writer;
import java.math.BigDecimal;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class JacksonStructure implements JsonLikeStructure {
private static final ObjectMapper OBJECT_MAPPER = createObjectMapper();
private JacksonWriter jacksonWriter;
private ROOT_TYPE rootType = null;
private JsonNode nativeRoot = null;
private JsonNode jsonLikeRoot = null;
public void setNativeObject(ObjectNode objectNode) {
this.rootType = ROOT_TYPE.OBJECT;
this.nativeRoot = objectNode;
}
public void setNativeArray(ArrayNode arrayNode) {
this.rootType = ROOT_TYPE.ARRAY;
this.nativeRoot = arrayNode;
}
@Override
public JsonLikeStructure getInstance() {
return new JacksonStructure();
}
@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 '{')");
}
if (nextInt == '{') {
setNativeObject((ObjectNode) OBJECT_MAPPER.readTree(pbr));
} else {
setNativeArray((ArrayNode) OBJECT_MAPPER.readTree(pbr));
}
} catch (Exception 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);
}
}
@Override
public JsonLikeWriter getJsonLikeWriter(Writer writer) throws IOException {
if (null == jacksonWriter) {
jacksonWriter = new JacksonWriter(OBJECT_MAPPER.getFactory(), writer);
}
return jacksonWriter;
}
@Override
public JsonLikeWriter getJsonLikeWriter() {
if (null == jacksonWriter) {
jacksonWriter = new JacksonWriter();
}
return jacksonWriter;
}
@Override
public JsonLikeObject getRootObject() throws DataFormatException {
if (rootType == ROOT_TYPE.OBJECT) {
if (null == jsonLikeRoot) {
jsonLikeRoot = nativeRoot;
}
return new JacksonJsonObject((ObjectNode) jsonLikeRoot);
}
throw new DataFormatException("Content must be a valid JSON Object. It must start with '{'.");
}
private enum ROOT_TYPE {OBJECT, ARRAY}
private static class JacksonJsonObject extends JsonLikeObject {
private final ObjectNode nativeObject;
private final Map<String, JsonLikeValue> jsonLikeMap = new LinkedHashMap<>();
private Set<String> keySet = null;
public JacksonJsonObject(ObjectNode json) {
this.nativeObject = json;
}
@Override
public Object getValue() {
return null;
}
@Override
public Set<String> keySet() {
if (null == keySet) {
final Iterable<Map.Entry<String, JsonNode>> iterable = nativeObject::fields;
keySet = StreamSupport.stream(iterable.spliterator(), false)
.map(Map.Entry::getKey)
.collect(Collectors.toCollection(EntryOrderedSet::new));
}
return keySet;
}
@Override
public JsonLikeValue get(String key) {
JsonLikeValue result = null;
if (jsonLikeMap.containsKey(key)) {
result = jsonLikeMap.get(key);
} else {
JsonNode child = nativeObject.get(key);
if (child != null) {
result = new JacksonJsonValue(child);
}
jsonLikeMap.put(key, result);
}
return result;
}
}
private static class EntryOrderedSet<T> extends AbstractSet<T> {
private final transient ArrayList<T> data;
public EntryOrderedSet() {
data = new ArrayList<>();
}
@Override
public int size() {
return data.size();
}
@Override
public boolean contains(Object o) {
return data.contains(o);
}
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();
}
}
private static class JacksonJsonArray extends JsonLikeArray {
private final ArrayNode nativeArray;
private final Map<Integer, JsonLikeValue> jsonLikeMap = new LinkedHashMap<Integer, JsonLikeValue>();
public JacksonJsonArray(ArrayNode 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 = index;
JsonLikeValue result = null;
if (jsonLikeMap.containsKey(key)) {
result = jsonLikeMap.get(key);
} else {
JsonNode child = nativeArray.get(index);
if (child != null) {
result = new JacksonJsonValue(child);
}
jsonLikeMap.put(key, result);
}
return result;
}
}
private static class JacksonJsonValue extends JsonLikeValue {
private final JsonNode nativeValue;
private JsonLikeObject jsonLikeObject = null;
private JsonLikeArray jsonLikeArray = null;
public JacksonJsonValue(JsonNode jsonNode) {
this.nativeValue = jsonNode;
}
@Override
public Object getValue() {
if (nativeValue != null && nativeValue.isValueNode()) {
if (nativeValue.isNumber()) {
return nativeValue.numberValue();
}
if (nativeValue.isBoolean()) {
return nativeValue.booleanValue();
}
return nativeValue.asText();
}
return null;
}
@Override
public ValueType getJsonType() {
if (null == nativeValue || nativeValue.isNull()) {
return ValueType.NULL;
}
if (nativeValue.isObject()) {
return ValueType.OBJECT;
}
if (nativeValue.isArray()) {
return ValueType.ARRAY;
}
if (nativeValue.isValueNode()) {
return ValueType.SCALAR;
}
return null;
}
@Override
public ScalarType getDataType() {
if (nativeValue != null && nativeValue.isValueNode()) {
if (nativeValue.isNumber()) {
return ScalarType.NUMBER;
}
if (nativeValue.isTextual()) {
return ScalarType.STRING;
}
if (nativeValue.isBoolean()) {
return ScalarType.BOOLEAN;
}
}
return null;
}
@Override
public JsonLikeArray getAsArray() {
if (nativeValue != null && nativeValue.isArray()) {
if (null == jsonLikeArray) {
jsonLikeArray = new JacksonJsonArray((ArrayNode) nativeValue);
}
}
return jsonLikeArray;
}
@Override
public JsonLikeObject getAsObject() {
if (nativeValue != null && nativeValue.isObject()) {
if (null == jsonLikeObject) {
jsonLikeObject = new JacksonJsonObject((ObjectNode) nativeValue);
}
}
return jsonLikeObject;
}
@Override
public Number getAsNumber() {
return nativeValue != null ? nativeValue.numberValue() : null;
}
@Override
public String getAsString() {
if (nativeValue != null) {
if (nativeValue instanceof DecimalNode) {
BigDecimal value = nativeValue.decimalValue();
return value.toPlainString();
}
return nativeValue.asText();
}
return null;
}
@Override
public boolean getAsBoolean() {
if (nativeValue != null && nativeValue.isValueNode() && nativeValue.isBoolean()) {
return nativeValue.asBoolean();
}
return super.getAsBoolean();
}
}
private static ObjectMapper createObjectMapper() {
ObjectMapper retVal = new ObjectMapper();
retVal = retVal.setNodeFactory(new JsonNodeFactory(true));
retVal = retVal.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
retVal = retVal.enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
retVal = retVal.disable(JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION);
retVal = retVal.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
retVal = retVal.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
retVal = retVal.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
return retVal;
}
}

View File

@ -0,0 +1,217 @@
package ca.uhn.fhir.parser.json.jackson;
/*-
* #%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.parser.json.JsonLikeWriter;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.PrettyPrinter;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.core.util.Separators;
import java.io.IOException;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
public class JacksonWriter extends JsonLikeWriter {
private JsonGenerator myJsonGenerator;
public JacksonWriter(JsonFactory theJsonFactory, Writer theWriter) throws IOException {
myJsonGenerator = theJsonFactory.createGenerator(theWriter);
setWriter(theWriter);
}
public JacksonWriter() {
}
@Override
public JsonLikeWriter init() {
if (isPrettyPrint()) {
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter() {
/**
* Objects should serialize as
* <pre>
* {
* "key": "value"
* }
* </pre>
* in order to be consistent with Gson behaviour, instead of the jackson default
* <pre>
* {
* "key" : "value"
* }
* </pre>
*/
@Override
public DefaultPrettyPrinter withSeparators(Separators separators) {
_separators = separators;
_objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSeparator() + " ";
return this;
}
};
prettyPrinter = prettyPrinter.withObjectIndenter(new DefaultIndenter(" ", "\n"));
myJsonGenerator.setPrettyPrinter(prettyPrinter);
}
return this;
}
@Override
public JsonLikeWriter flush() {
return this;
}
@Override
public void close() throws IOException {
myJsonGenerator.close();
}
@Override
public JsonLikeWriter beginObject() throws IOException {
myJsonGenerator.writeStartObject();
return this;
}
@Override
public JsonLikeWriter beginObject(String name) throws IOException {
myJsonGenerator.writeObjectFieldStart(name);
return this;
}
@Override
public JsonLikeWriter beginArray(String name) throws IOException {
myJsonGenerator.writeArrayFieldStart(name);
return this;
}
@Override
public JsonLikeWriter write(String value) throws IOException {
myJsonGenerator.writeObject(value);
return this;
}
@Override
public JsonLikeWriter write(BigInteger value) throws IOException {
myJsonGenerator.writeObject(value);
return this;
}
@Override
public JsonLikeWriter write(BigDecimal value) throws IOException {
myJsonGenerator.writeObject(value);
return this;
}
@Override
public JsonLikeWriter write(long value) throws IOException {
myJsonGenerator.writeObject(value);
return this;
}
@Override
public JsonLikeWriter write(double value) throws IOException {
myJsonGenerator.writeObject(value);
return this;
}
@Override
public JsonLikeWriter write(Boolean value) throws IOException {
myJsonGenerator.writeObject(value);
return this;
}
@Override
public JsonLikeWriter write(boolean value) throws IOException {
myJsonGenerator.writeObject(value);
return this;
}
@Override
public JsonLikeWriter writeNull() throws IOException {
myJsonGenerator.writeNull();
return this;
}
@Override
public JsonLikeWriter write(String name, String value) throws IOException {
myJsonGenerator.writeObjectField(name, value);
return this;
}
@Override
public JsonLikeWriter write(String name, BigInteger value) throws IOException {
myJsonGenerator.writeObjectField(name, value);
return this;
}
@Override
public JsonLikeWriter write(String name, BigDecimal value) throws IOException {
myJsonGenerator.writeObjectField(name, value);
return this;
}
@Override
public JsonLikeWriter write(String name, long value) throws IOException {
myJsonGenerator.writeObjectField(name, value);
return this;
}
@Override
public JsonLikeWriter write(String name, double value) throws IOException {
myJsonGenerator.writeObjectField(name, value);
return this;
}
@Override
public JsonLikeWriter write(String name, Boolean value) throws IOException {
myJsonGenerator.writeObjectField(name, value);
return this;
}
@Override
public JsonLikeWriter write(String name, boolean value) throws IOException {
myJsonGenerator.writeObjectField(name, value);
return this;
}
@Override
public JsonLikeWriter endObject() throws IOException {
myJsonGenerator.writeEndObject();
return this;
}
@Override
public JsonLikeWriter endArray() throws IOException {
myJsonGenerator.writeEndArray();
return this;
}
@Override
public JsonLikeWriter endBlock() throws IOException {
myJsonGenerator.writeEndObject();
return this;
}
}

View File

@ -22,7 +22,14 @@ package ca.uhn.fhir.rest.api;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
public class Constants { public class Constants {
@ -252,7 +259,8 @@ public class Constants {
* </p> * </p>
*/ */
public static final String EXT_META_SOURCE = "http://hapifhir.io/fhir/StructureDefinition/resource-meta-source"; public static final String EXT_META_SOURCE = "http://hapifhir.io/fhir/StructureDefinition/resource-meta-source";
public static final String CODESYSTEM_VALIDATE_NOT_NEEDED = UUID.randomUUID().toString(); public static final String PARAM_FHIRPATH = "_fhirpath";
public static final String PARAM_TYPE = "_type";
static { static {
CHARSET_UTF8 = StandardCharsets.UTF_8; CHARSET_UTF8 = StandardCharsets.UTF_8;
@ -354,16 +362,7 @@ public class Constants {
CORS_ALLWED_METHODS = Collections.unmodifiableSet(corsAllowedMethods); CORS_ALLWED_METHODS = Collections.unmodifiableSet(corsAllowedMethods);
} }
public static boolean codeSystemNotNeeded(String theCodeSystem) {
return Constants.CODESYSTEM_VALIDATE_NOT_NEEDED.equals(theCodeSystem);
}
public static String codeSystemWithDefaultDescription(String theSystem) { public static String codeSystemWithDefaultDescription(String theSystem) {
if (codeSystemNotNeeded(theSystem)) { return defaultIfBlank(theSystem, "(none)");
return "(none)";
} else {
return theSystem;
}
} }
} }

View File

@ -34,7 +34,14 @@ public interface IBaseOn<T> {
* Perform the operation across all versions of all resources of the given type on the server * Perform the operation across all versions of all resources of the given type on the server
*/ */
T onType(Class<? extends IBaseResource> theResourceType); T onType(Class<? extends IBaseResource> theResourceType);
/**
* Perform the operation across all versions of all resources of the given type on the server
*
* @param theResourceType The resource type name, e.g. "ValueSet"
*/
T onType(String theResourceType);
/** /**
* Perform the operation across all versions of a specific resource (by ID and type) on the server. * Perform the operation across all versions of a specific resource (by ID and type) on the server.
* Note that <code>theId</code> must be populated with both a resource type and a resource ID at * Note that <code>theId</code> must be populated with both a resource type and a resource ID at

View File

@ -200,6 +200,13 @@ public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/
return myValue; return myValue;
} }
/**
* Note that the parameter to this method <b>must</b> be a resource reference, e.g
* <code>123</code> or <code>Patient/123</code> or <code>http://example.com/fhir/Patient/123</code>
* or something like this. This is not appropriate for cases where a chain is being used and
* the value is for a different type of parameter (e.g. a token). In that case, use one of the
* setter constructors.
*/
public ReferenceParam setValue(String theValue) { public ReferenceParam setValue(String theValue) {
IdDt id = new IdDt(theValue); IdDt id = new IdDt(theValue);
String qualifier= null; String qualifier= null;

View File

@ -238,7 +238,7 @@ public class ParametersUtil {
addPart(theContext, theParameter, theName, coding); addPart(theContext, theParameter, theName, coding);
} }
private static void addPart(FhirContext theContext, IBase theParameter, String theName, IBase theValue) { public static void addPart(FhirContext theContext, IBase theParameter, String theName, IBase theValue) {
BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass()); BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass());
BaseRuntimeChildDefinition partChild = def.getChildByName("part"); BaseRuntimeChildDefinition partChild = def.getChildByName("part");
@ -252,4 +252,19 @@ public class ParametersUtil {
partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue); partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue);
} }
public static void addPartResource(FhirContext theContext, IBase theParameter, String theName, IBaseResource theValue) {
BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass());
BaseRuntimeChildDefinition partChild = def.getChildByName("part");
BaseRuntimeElementCompositeDefinition<?> partChildElem = (BaseRuntimeElementCompositeDefinition<?>) partChild.getChildByName("part");
IBase part = partChildElem.newInstance();
partChild.getMutator().addValue(theParameter, part);
IPrimitiveType<String> name = (IPrimitiveType<String>) theContext.getElementDefinition("string").newInstance();
name.setValue(theName);
partChildElem.getChildByName("name").getMutator().addValue(part, name);
partChildElem.getChildByName("resource").getMutator().addValue(part, theValue);
}
} }

View File

@ -19,31 +19,37 @@ package ca.uhn.fhir.util;
* limitations under the License. * limitations under the License.
* #L% * #L%
*/ */
import java.lang.reflect.*;
import ca.uhn.fhir.context.ConfigurationException;
import org.apache.commons.lang3.Validate;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.support.IContextValidationSupport;
public class ReflectionUtil { public class ReflectionUtil {
private static final ConcurrentHashMap<String, Object> ourFhirServerVersions = new ConcurrentHashMap<String, Object>(); private static final ConcurrentHashMap<String, Object> ourFhirServerVersions = new ConcurrentHashMap<>();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReflectionUtil.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReflectionUtil.class);
public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0];
public static LinkedHashSet<Method> getDeclaredMethods(Class<?> theClazz) { public static LinkedHashSet<Method> getDeclaredMethods(Class<?> theClazz) {
LinkedHashSet<Method> retVal = new LinkedHashSet<Method>(); LinkedHashSet<Method> retVal = new LinkedHashSet<>();
for (Method next : theClazz.getDeclaredMethods()) { for (Method next : theClazz.getDeclaredMethods()) {
try { try {
Method method = theClazz.getMethod(next.getName(), next.getParameterTypes()); Method method = theClazz.getMethod(next.getName(), next.getParameterTypes());
retVal.add(method); retVal.add(method);
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException | SecurityException e) {
retVal.add(next);
} catch (SecurityException e) {
retVal.add(next); retVal.add(next);
} }
} }
@ -93,7 +99,7 @@ public class ReflectionUtil {
return getGenericCollectionTypeOf(collectionType.getActualTypeArguments()[0]); return getGenericCollectionTypeOf(collectionType.getActualTypeArguments()[0]);
} }
@SuppressWarnings({ "rawtypes" }) @SuppressWarnings({"rawtypes"})
private static Class<?> getGenericCollectionTypeOf(Type theType) { private static Class<?> getGenericCollectionTypeOf(Type theType) {
Class<?> type; Class<?> type;
if (ParameterizedType.class.isAssignableFrom(theType.getClass())) { if (ParameterizedType.class.isAssignableFrom(theType.getClass())) {
@ -140,43 +146,38 @@ public class ReflectionUtil {
public static Object newInstanceOfFhirServerType(String theType) { public static Object newInstanceOfFhirServerType(String theType) {
String errorMessage = "Unable to instantiate server framework. Please make sure that hapi-fhir-server library is on your classpath!"; String errorMessage = "Unable to instantiate server framework. Please make sure that hapi-fhir-server library is on your classpath!";
String wantedType = "ca.uhn.fhir.rest.api.server.IFhirVersionServer"; String wantedType = "ca.uhn.fhir.rest.api.server.IFhirVersionServer";
return newInstanceOfType(theType, errorMessage, wantedType); return newInstanceOfType(theType, theType, errorMessage, wantedType, new Class[0], new Object[0]);
} }
@SuppressWarnings("unchecked") private static Object newInstanceOfType(String theKey, String theType, String errorMessage, String wantedType, Class<?>[] theParameterArgTypes, Object[] theConstructorArgs) {
public static <EVS_IN, EVS_OUT, SDT, CST, CDCT, IST> ca.uhn.fhir.context.support.IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST> newInstanceOfFhirProfileValidationSupport( Object fhirServerVersion = ourFhirServerVersions.get(theKey);
String theType) {
String errorMessage = "Unable to instantiate validation support! Please make sure that hapi-fhir-validation and the appropriate structures JAR are on your classpath!";
String wantedType = "ca.uhn.fhir.context.support.IContextValidationSupport";
Object fhirServerVersion = newInstanceOfType(theType, errorMessage, wantedType);
return (IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>) fhirServerVersion;
}
private static Object newInstanceOfType(String theType, String errorMessage, String wantedType) {
Object fhirServerVersion = ourFhirServerVersions.get(theType);
if (fhirServerVersion == null) { if (fhirServerVersion == null) {
try { try {
Class<?> type = Class.forName(theType); Class<?> type = Class.forName(theType);
Class<?> serverType = Class.forName(wantedType); Class<?> serverType = Class.forName(wantedType);
Validate.isTrue(serverType.isAssignableFrom(type)); Validate.isTrue(serverType.isAssignableFrom(type));
fhirServerVersion = type.newInstance(); fhirServerVersion = type.getConstructor(theParameterArgTypes).newInstance(theConstructorArgs);
} catch (Exception e) { } catch (Exception e) {
throw new ConfigurationException(errorMessage, e); throw new ConfigurationException(errorMessage, e);
} }
ourFhirServerVersions.put(theType, fhirServerVersion); ourFhirServerVersions.put(theKey, fhirServerVersion);
} }
return fhirServerVersion; return fhirServerVersion;
} }
@SuppressWarnings("unchecked")
public static <T> T newInstanceOrReturnNull(String theClassName, Class<T> theType) { public static <T> T newInstanceOrReturnNull(String theClassName, Class<T> theType) {
return newInstanceOrReturnNull(theClassName, theType, EMPTY_CLASS_ARRAY, EMPTY_OBJECT_ARRAY);
}
@SuppressWarnings("unchecked")
public static <T> T newInstanceOrReturnNull(String theClassName, Class<T> theType, Class<?>[] theArgTypes, Object[] theArgs) {
try { try {
Class<?> clazz = Class.forName(theClassName); Class<?> clazz = Class.forName(theClassName);
if (!theType.isAssignableFrom(clazz)) { if (!theType.isAssignableFrom(clazz)) {
throw new ConfigurationException(theClassName + " is not assignable to " + theType); throw new ConfigurationException(theClassName + " is not assignable to " + theType);
} }
return (T) clazz.newInstance(); return (T) clazz.getConstructor(theArgTypes).newInstance(theArgs);
} catch (ConfigurationException e) { } catch (ConfigurationException e) {
throw e; throw e;
} catch (Exception e) { } catch (Exception e) {
@ -185,4 +186,12 @@ public class ReflectionUtil {
} }
} }
public static boolean typeExists(String theName) {
try {
Class.forName(theName);
return true;
} catch (ClassNotFoundException theE) {
return false;
}
}
} }

View File

@ -112,7 +112,8 @@ public class TestUtil {
* environment * environment
*/ */
public static void randomizeLocale() { public static void randomizeLocale() {
Locale[] availableLocales = {Locale.CANADA, Locale.GERMANY, Locale.TAIWAN}; // Locale[] availableLocales = {Locale.CANADA, Locale.GERMANY, Locale.TAIWAN};
Locale[] availableLocales = {Locale.US};
Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]); Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]);
ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName()); ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
if (Math.random() < 0.5) { if (Math.random() < 0.5) {

View File

@ -59,7 +59,8 @@ public enum VersionEnum {
V4_0_3, V4_0_3,
V4_1_0, V4_1_0,
V4_2_0, V4_2_0,
V4_3_0; V4_3_0, // 4.3.0 was renamed to 5.0.0 during the cycle
V5_0_0;
public static VersionEnum latestVersion() { public static VersionEnum latestVersion() {
VersionEnum[] values = VersionEnum.values(); VersionEnum[] values = VersionEnum.values();

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.jpa.term; package ca.uhn.fhir.util;
/* /*
* #%L * #%L
* HAPI FHIR JPA Server * HAPI FHIR - Core Library
* %% * %%
* Copyright (C) 2014 - 2020 University Health Network * Copyright (C) 2014 - 2020 University Health Network
* %% * %%
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.term;
* #L% * #L%
*/ */
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.CompareToBuilder; import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;
@ -28,20 +29,30 @@ public class VersionIndependentConcept implements Comparable<VersionIndependentC
private final String mySystem; private final String mySystem;
private final String myCode; private final String myCode;
private final String myDisplay;
private int myHashCode; private int myHashCode;
/** /**
* Constructor * Constructor
*/ */
public VersionIndependentConcept(String theSystem, String theCode) { public VersionIndependentConcept(String theSystem, String theCode) {
this(theSystem, theCode, null);
}
public VersionIndependentConcept(String theSystem, String theCode, String theDisplay) {
mySystem = theSystem; mySystem = theSystem;
myCode = theCode; myCode = theCode;
myDisplay = theDisplay;
myHashCode = new HashCodeBuilder(17, 37) myHashCode = new HashCodeBuilder(17, 37)
.append(mySystem) .append(mySystem)
.append(myCode) .append(myCode)
.toHashCode(); .toHashCode();
} }
public String getDisplay() {
return myDisplay;
}
public String getSystem() { public String getSystem() {
return mySystem; return mySystem;
} }

View File

@ -83,6 +83,7 @@ ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionMissingUrl=Unable to perfor
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionInvalidUrl=Unable to perform {0}, URL provided is invalid: {1} ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionInvalidUrl=Unable to perform {0}, URL provided is invalid: {1}
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.cantValidateWithNoResource=No resource supplied for $validate operation (resource is required unless mode is \"delete\") ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.cantValidateWithNoResource=No resource supplied for $validate operation (resource is required unless mode is \"delete\")
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.deleteBlockedBecauseDisabled=Resource deletion is not permitted on this server
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.duplicateCreateForcedId=Can not create entity with ID[{0}], a resource with this ID already exists ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.duplicateCreateForcedId=Can not create entity with ID[{0}], a resource with this ID already exists
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.failedToCreateWithInvalidId=Can not process entity with ID[{0}], this is not a valid FHIR ID ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.failedToCreateWithInvalidId=Can not process entity with ID[{0}], this is not a valid FHIR ID
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.incorrectResourceType=Incorrect resource type detected for endpoint, found {0} but expected {1} ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.incorrectResourceType=Incorrect resource type detected for endpoint, found {0} but expected {1}
@ -134,3 +135,6 @@ ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateValueSetUrl=Can no
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted! ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted!
ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils.failedToApplyPatch=Failed to apply JSON patch to {0}: {1} ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils.failedToApplyPatch=Failed to apply JSON patch to {0}: {1}
ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderReference.invalidTargetTypeForChain=Resource type "{0}" is not a valid target type for reference search parameter: {1}
ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderReference.invalidResourceType=Invalid/unsupported resource type: "{0}"

View File

@ -5,6 +5,7 @@ import static org.junit.Assert.assertNotNull;
import java.io.StringReader; import java.io.StringReader;
import ca.uhn.fhir.parser.json.jackson.JacksonStructure;
import org.junit.Test; import org.junit.Test;
public class JsonLikeStructureTest { public class JsonLikeStructureTest {
@ -39,7 +40,7 @@ public class JsonLikeStructureTest {
@Test @Test
public void testStructureLoading() { public void testStructureLoading() {
StringReader reader = new StringReader(TEST_STRUCTURELOADING_DATA); StringReader reader = new StringReader(TEST_STRUCTURELOADING_DATA);
JsonLikeStructure jsonStructure = new GsonStructure(); JsonLikeStructure jsonStructure = new JacksonStructure();
jsonStructure.load(reader); jsonStructure.load(reader);
JsonLikeObject rootObject = jsonStructure.getRootObject(); JsonLikeObject rootObject = jsonStructure.getRootObject();
@ -70,7 +71,7 @@ public class JsonLikeStructureTest {
@Test @Test
public void testJsonAndDataTypes() { public void testJsonAndDataTypes() {
StringReader reader = new StringReader(TEST_JSONTYPES_DATA); StringReader reader = new StringReader(TEST_JSONTYPES_DATA);
JsonLikeStructure jsonStructure = new GsonStructure(); JsonLikeStructure jsonStructure = new JacksonStructure();
jsonStructure.load(reader); jsonStructure.load(reader);
JsonLikeObject rootObject = jsonStructure.getRootObject(); JsonLikeObject rootObject = jsonStructure.getRootObject();

View File

@ -45,4 +45,10 @@ public class ReflectionUtilTest {
assertEquals("Failed to instantiate java.util.List", e.getMessage()); assertEquals("Failed to instantiate java.util.List", e.getMessage());
} }
} }
@Test
public void testTypeExists() {
assertFalse(ReflectionUtil.typeExists("ca.Foo"));
assertTrue(ReflectionUtil.typeExists(String.class.getName()));
}
} }

View File

@ -3,14 +3,14 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-bom</artifactId> <artifactId>hapi-fhir-bom</artifactId>
<version>4.3.0-SNAPSHOT</version> <version>5.0.0-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>HAPI FHIR BOM</name> <name>HAPI FHIR BOM</name>
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>4.3.0-SNAPSHOT</version> <version>5.0.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

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