This commit is contained in:
lmds1 2014-09-17 11:35:44 -04:00
commit 4b730cc418
147 changed files with 4046 additions and 1155 deletions

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>0.6</version>
<version>0.7-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
@ -17,7 +17,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>0.6</version>
<version>0.7-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>

View File

@ -3,6 +3,7 @@ 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;
@ -20,19 +21,25 @@ public class ExampleRestfulServlet extends RestfulServer {
private static final long serialVersionUID = 1L;
/**
* Constructor
* 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.
*/
public ExampleRestfulServlet() {
/*
* 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);
}
@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

@ -7,6 +7,7 @@ import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.PeriodDt;
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.AdministrativeGenderCodesEnum;
@ -16,88 +17,103 @@ import ca.uhn.fhir.model.primitive.StringDt;
public class FhirDataModel {
public static void datatypes() {
// START SNIPPET: datatypes
Observation obs = new Observation();
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());
// 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.getIssued().getValue(); // A date object
String dateString = obs.getIssued().getValueAsString(); // "2014-03-08T12:59:58.068-05:00"
// END SNIPPET: datatypes
// The InstantDt also lets you work with the instant as a Java Date
// object or as a FHIR String.
Date date = obs.getIssued().getValue(); // A date object
String dateString = obs.getIssued().getValueAsString(); // "2014-03-08T12:59:58.068-05:00"
// END SNIPPET: datatypes
System.out.println(date);
System.out.println(dateString);
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 return null, but instead create their respective
// child elements.
IdentifierDt identifierDt = observation.getIdentifier();
PeriodDt periodDt = observation.getIdentifier().getPeriod();
DateTimeDt activeDt = observation.getIdentifier().getPeriod().getStart();
@SuppressWarnings("unused")
public void nonNull() {
// START SNIPPET: nonNull
Observation observation = new Observation();
// DateTimeDt is a FHIR primitive however, so the following will return null
// unless a value has been placed there.
Date active = observation.getIdentifier().getPeriod().getStart().getValue();
// END SNIPPET: nonNull
// None of these calls will return null, but instead create their
// respective
// child elements.
IdentifierDt identifierDt = observation.getIdentifier();
PeriodDt periodDt = observation.getIdentifier().getPeriod();
DateTimeDt activeDt = observation.getIdentifier().getPeriod().getStart();
// DateTimeDt is a FHIR primitive however, so the following will return
// null
// unless a value has been placed there.
Date active = observation.getIdentifier().getPeriod().getStart().getValue();
// END SNIPPET: nonNull
}
public static void codes() {
// START SNIPPET: codes
Patient patient = new Patient();
}
// Coded types can naturally be set using plain Strings
CodingDt genderCoding = patient.getGender().addCoding();
genderCoding.setSystem("http://hl7.org/fhir/v3/AdministrativeGender");
genderCoding.setCode("M");
public static void codes() {
// START SNIPPET: codes
Patient patient = new Patient();
// This is equivalent to the three statements above
patient.setGender(AdministrativeGenderCodesEnum.M);
// END SNIPPET: codes
// Coded types can naturally be set using plain Strings
CodingDt genderCoding = patient.getGender().addCoding();
genderCoding.setSystem("http://hl7.org/fhir/v3/AdministrativeGender");
genderCoding.setCode("M");
// This is equivalent to the three statements above
patient.setGender(AdministrativeGenderCodesEnum.M);
// END SNIPPET: codes
}
public static void main(String[] args) {
datatypes();
// START SNIPPET: observation
Observation observation = new Observation();
// Create a quantity datatype
QuantityDt q = new QuantityDt();
q.setValue(185);
q.setSystem("http://unitsofmeasure.org");
q.setCode("lbs");
// Put the datatype in the observation
observation.setValue(q);
// Set the reference range
observation.getReferenceRangeFirstRep().setLow(100);
observation.getReferenceRangeFirstRep().setHigh(200);
// END SNIPPET: observation
}
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
}
}
public static void main(String[] args) {
datatypes();
}
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

@ -0,0 +1,37 @@
package example;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
@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
}

View File

@ -1,8 +1,15 @@
package example;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.ContactSystemEnum;
import ca.uhn.fhir.parser.DataFormatException;
@ -11,8 +18,7 @@ import ca.uhn.fhir.validation.ValidationFailureException;
public class ValidatorExamples {
public static void main(String[] args) throws DataFormatException, IOException {
public void validateResource() {
//START SNIPPET: basicValidation
// As always, you need a context
FhirContext ctx = new FhirContext();
@ -35,10 +41,45 @@ public class ValidatorExamples {
System.out.println("Validation failed");
// The ValidationFailureException which gets thrown by the validator
// will contain an OperationOutcome resource describing the failure
String results = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome());
System.out.println(results);
}
//END SNIPPET: basicValidation
}
public static void main(String[] args) throws DataFormatException, IOException {
validateFiles();
}
private static void validateFiles() throws IOException, FileNotFoundException {
//START SNIPPET: validateFiles
FhirContext ctx = new FhirContext();
// 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)
IResource resource = ctx.newJsonParser().parseResource(nextFileContents);
// Apply the validation. This will throw an exception on the first validation failure
validator.validate(resource);
}
// If we make it here with no exception, all the files validated!
//START SNIPPET: validateFiles
}
}

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>0.6</version>
<version>0.7-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -225,6 +225,13 @@
<version>9.1.1.v20140108</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<version>9.1.1.v20140108</version>
<scope>test</scope>
</dependency>
<!-- UNIT TEST DEPENDENCIES -->
<dependency>
@ -521,7 +528,6 @@
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>${maven_site_plugin_version}</version>
<executions>
<execution>
<id>stage-for-scm-publish</id>

View File

@ -6,7 +6,30 @@
<title>HAPI FHIR Changelog</title>
</properties>
<body>
<release version="0.6" date="TBD">
<release version="0.7" date="TBD">
<action type="add">
Documentation update, thanks to Suranga Nath Kasthurirathne of the OpenMRS project.
</action>
<action type="fix">
Server implementation was not correctly figuring out its own FHIR Base URL when deployed
on Amazon Web Service server. Thanks to Jeffrey Ting and Bill De Beaubien of
Systems Made Simple for their help in figuring out this issue!
</action>
<action type="fix">
XML Parser failed to encode fields with both a resource reference child and
a primitive type child. Thanks to Jeffrey Ting and Bill De Beaubien of
Systems Made Simple for their help in figuring out this issue!
</action>
<action type="fix">
HAPI now runs successfully on Servlet 2.5 containers (such as Tomcat 6). Thanks to
Bernard Gitaadji for reporting and diagnosing the issue!
</action>
<action type="fix">
XHTML (in narratives) containing escapable characters (e.g. &lt; or &quot;) will now always have those characters
escaped properly in encoded messages.
</action>
</release>
<release version="0.6" date="2014-Sep-08" description="This release brings a number of new features and bug fixes!">
<!--
<action type="add">
Allow generic client ... OAUTH

View File

@ -0,0 +1,324 @@
<?xml version="1.0"?>
<document xmlns="http://maven.apache.org/changes/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/changes/1.0.0 ./changes.xsd">
<properties>
<author>James Agnew</author>
<title>HAPI FHIR Changelog</title>
</properties>
<body>
<release version="0.7" date="TBD">
<action type="add">
Documentation update, thanks to Suranga Nath Kasthurirathne of the OpenMRS project.
</action>
<action type="fix">
<<<<<<< HEAD
Server implementation was not correctly figuring out its own FHIR Base URL when deployed
on Amazon Web Service server. Thanks to Jeffrey Ting and Bill De Beaubien of
Systems Made Simple for their help in figuring out this issue!
</action>
<action type="fix">
XML Parser failed to encode fields with both a resource reference child and
a primitive type child. Thanks to Jeffrey Ting and Bill De Beaubien of
Systems Made Simple for their help in figuring out this issue!
=======
HAPI now runs successfully on Servlet 2.5 containers (such as Tomcat 6). Thanks to
Bernard Gitaadji for reporting and diagnosing the issue!
</action>
<action type="fix">
XHTML (in narratives) containing escapable characters (e.g. &lt; or &quot;) will now always have those characters
escaped properly in encoded messages.
>>>>>>> 83f9c1c6a992266de12f9076b733dffdaaf63203
</action>
</release>
<release version="0.6" date="2014-Sep-08" description="This release brings a number of new features and bug fixes!">
<!--
<action type="add">
Allow generic client ... OAUTH
</action>
-->
<action type="add">
Add server interceptor framework, and new interceptor for logging incoming
requests.
</action>
<action type="add">
Add server validation framework for validating resources against the FHIR schemas and schematrons
</action>
<action type="fix">
Tester UI created double _format and _pretty param entries in searches. Thanks to Gered King of University
Health Network for reporting!
</action>
<action type="fix" issue="4">
Create method was incorrectly returning an HTTP 204 on sucessful completion, but
should be returning an HTTP 200 per the FHIR specification. Thanks to wanghaisheng
for reporting!
</action>
<action type="fix">
FHIR Tester UI now correctly sends UTF-8 charset in responses so that message payloads containing
non US-ASCII characters will correctly display in the browser
</action>
<action type="fix">
JSON parser was incorrectly encoding extensions on composite elements outside the element itself
(as is done correctly for non-composite elements) instead of inside of them. Thanks to David Hay of
Orion for reporting this!
</action>
<action type="add">
Contained/included resource instances received by a client are now automatically
added to any ResourceReferenceDt instancea in other resources which reference them.
</action>
<action type="add">
Add documentation on how to use eBay CORS Filter to support Cross Origin Resource
Sharing (CORS) to server. CORS support that was built in to the server itself has
been removed, as it did not work correctly (and was reinventing a wheel that others
have done a great job inventing). Thanks to Peter Bernhardt of Relay Health for all the assistance
in testing this!
</action>
<action type="fix">
IResource interface did not expose the getLanguage/setLanguage methods from BaseResource,
so the resource language was difficult to access.
</action>
<action type="fix">
JSON Parser now gives a more friendly error message if it tries to parse JSON with invalid use
of single quotes
</action>
<action type="add">
Transaction server method is now allowed to return an OperationOutcome in addition to the
incoming resources. The public test server now does this in order to return status information
about the transaction processing.
</action>
<action type="add">
Update method in the server can now flag (via a field on the MethodOutcome object being returned)
that the result was actually a creation, and Create method can indicate that it was actually an
update. This has no effect other than to switch between the HTTP 200 and HTTP 201 status codes on the
response, but this may be useful in some circumstances.
</action>
<action type="fix">
Annotation client search methods with a specific resource type (e.g. List&lt;Patient&gt; search())
won't return any resources that aren't of the correct type that are received in a response
bundle (generally these are referenced resources, so they are populated in the reference fields instead).
Thanks to Tahura Chaudhry of University Health Network for the unit test!
</action>
<action type="add">
Added narrative generator template for OperationOutcome resource
</action>
<action type="fix">
Date/time types did not correctly parse values in the format "yyyymmdd" (although the FHIR-defined format
is "yyyy-mm-dd" anyhow, and this is correctly handled). Thanks to Jeffrey Ting of Systems Made Simple
for reporting!
</action>
<action type="fix">
Server search method for an unnamed query gets called if the client requests a named query
with the same parameter list. Thanks to Neal Acharya of University Health Network for reporting!
</action>
<action type="fix">
Category header (for tags) is correctly read in client for "read" operation
</action>
<action type="add">
Transaction method in server can now have parameter type Bundle instead of
List&lt;IResource&gt;
</action>
<action type="add">
HAPI parsers now use field access to get/set values instead of method accessors and mutators.
This should give a small performance boost.
</action>
<action type="fix">
JSON parser encodes resource references incorrectly, using the name "resource" instead
of the name "reference" for the actual reference. Thanks to
Ricky Nguyen for reporting and tracking down the issue!
</action>
<action type="fix">
Rename NotImpementedException to NotImplementedException (to correct typo)
</action>
<action type="fix">
Server setUseBrowserFriendlyContentType setting also respected for errors (e.g. OperationOutcome with 4xx/5xx status)
</action>
<action type="fix">
Fix performance issue in date/time datatypes where pattern matchers were not static
</action>
<action type="fix">
Server now gives a more helpful error message if a @Read method has a search parameter (which is invalid, but
previously lead to a very unhelpful error message). Thanks to Tahura Chaudhry of UHN for reporting!
</action>
<action type="fix">
Resource of type "List" failed to parse from a bundle correctly. Thanks to David Hay of Orion Health
for reporting!
</action>
<action type="fix">
QuantityParam correctly encodes approximate (~) prefix to values
</action>
<action type="fix" issue="14">
If a server defines a method with parameter "_id", incoming search requests for that method may
get delegated to the wrong method. Thanks to Neal Acharya for reporting!
</action>
<action type="add">
SecurityEvent.Object structural element has been renamed to
SecurityEvent.ObjectElement to avoid conflicting names with the
java Object class. Thanks to Laurie Macdougall-Sookraj of UHN for
reporting!
</action>
<action type="fix">
Text/narrative blocks that were created with a non-empty
namespace prefix (e.g. &lt;xhtml:div xmlns:xhtml="..."&gt;...&lt;/xhtml:div&gt;)
failed to encode correctly (prefix was missing in encoded resource)
</action>
<action type="fix">
Resource references previously encoded their children (display and reference)
in the wrong order so references with both would fail schema validation.
</action>
<action type="add">
SecurityEvent resource's enums now use friendly enum names instead of the unfriendly
numeric code values. Thanks to Laurie MacDougall-Sookraj of UHN for the
suggestion!
</action>
</release>
<release version="0.5" date="2014-Jul-30">
<action type="add">
having multiple ways of accomplishing the same thing. This means that a number of existing classes
have been deprocated in favour of new naming schemes.
<![CDATA[<br/><br/>]]>
All annotation-based clients and all server search method parameters are now named
(type)Param, for example: StringParam, TokenParam, etc.
<![CDATA[<br/><br/>]]>
All generic/fluent client method parameters are now named
(type)ClientParam, for example: StringClientParam, TokenClientParam, etc.
<![CDATA[<br/><br/>]]>
All renamed classes have been retained and deprocated, so this change should not cause any issues
for existing applications but those applications should be refactored to use the
new parameters when possible.
</action>
<action type="add">
Allow server methods to return wildcard generic types (e.g. List&lt;? extends IResource&gt;)
</action>
<action type="add">
Search parameters are not properly escaped and unescaped. E.g. for a token parameter such as
"&amp;identifier=system|codepart1\|codepart2"
</action>
<action type="add">
Add support for OPTIONS verb (which returns the server conformance statement)
</action>
<action type="add">
Add support for CORS headers in server
</action>
<action type="add">
Bump SLF4j dependency to latest version (1.7.7)
</action>
<action type="add">
Add interceptor framework for clients (annotation based and generic), and add interceptors
for configurable logging, capturing requests and responses, and HTTP basic auth.
</action>
<action type="fix">
Transaction client invocations with XML encoding were using the wrong content type ("application/xml+fhir" instead
of the correct "application/atom+xml"). Thanks to David Hay of Orion Health for surfacing this one!
</action>
<action type="add">
Bundle entries now support a link type of "search". Thanks to David Hay for the suggestion!
</action>
<action type="add" issue="1">
If a client receives a non 2xx response (e.g. HTTP 500) and the response body is a text/plain message or
an OperationOutcome resource, include the message in the exception message so that it will be
more conveniently displayed in logs and other places. Thanks to Neal Acharya for the suggestion!
</action>
<action type="add" issue="2">
Read invocations in the client now process the "Content-Location" header and use it to
populate the ID of the returned resource. Thanks to Neal Acharya for the suggestion!
</action>
<action type="fix" issue="3">
Fix issue where vread invocations on server incorrectly get routed to instance history method if one is
defined. Thanks to Neal Acharya from UHN for surfacing this one!
</action>
<action type="add">
Binary reads on a server not include the Content-Disposition header, to prevent HTML in binary
blobs from being used for nefarious purposes. See
<![CDATA[<a href="http://gforge.hl7.org/gf/project/fhir/tracker/?action=TrackerItemEdit&tracker_id=677&tracker_item_id=3298">FHIR Tracker Bug 3298</a>]]>
for more information.
</action>
<action type="add">
Support has been added for using an HTTP proxy for outgoing requests.
</action>
<action type="fix">
Fix: Primitive extensions declared against custom resource types
are encoded even if they have no value. Thanks to David Hay of Orion for
reporting this!
</action>
<action type="fix">
Fix: RESTful server deployed to a location where the URL to access it contained a
space (e.g. a WAR file with a space in the name) failed to work correctly.
Thanks to David Hay of Orion for reporting this!
</action>
</release>
<release version="0.4" date="2014-Jul-13">
<action type="add">
<![CDATA[<b>BREAKING CHANGE:</b>]]>: IdDt has been modified so that it
contains a partial or complete resource identity. Previously it contained
only the simple alphanumeric id of the resource (the part at the end of the "read" URL for
that resource) but it can now contain a complete URL or even a partial URL (e.g. "Patient/123")
and can optionally contain a version (e.g. "Patient/123/_history/456"). New methods have
been added to this datatype which provide just the numeric portion. See the JavaDoc
for more information.
</action>
<action type="add">
<![CDATA[<b>API CHANGE:</b>]]>: Most elements in the HAPI FHIR model contain
a getId() and setId() method. This method is confusing because it is only actually used
for IDREF elements (which are rare) but its name makes it easy to confuse with more
important identifiers. For this reason, these methods have been deprocated and replaced with
get/setElementSpecificId() methods. The old methods will be removed at some point. Resource
types are unchanged and retain their get/setId methods.
</action>
<action type="add">
Allow use of QuantityDt as a service parameter to support the "quantity" type. Previously
QuantityDt did not implement IQueryParameterType so it was not valid, and there was no way to
support quantity search parameters on the server (e.g. Observation.value-quantity)
</action>
<action type="add">
Introduce StringParameter type which can be used as a RESTful operation search parameter
type. StringParameter allows ":exact" matches to be specified in clients, and handled in servers.
</action>
<action type="add">
Parsers (XML/JSON) now support deleted entries in bundles
</action>
<action type="add">
Transaction method now supported in servers
</action>
<action type="add">
Support for Binary resources added (in servers, clients, parsers, etc.)
</action>
<action type="fix">
Support for Query resources fixed (in parser)
</action>
<action type="fix">
Nested contained resources (e.g. encoding a resource with a contained resource that itself contains a resource)
now parse and encode correctly, meaning that all contained resources are placed in the "contained" element
of the root resource, and the parser looks in the root resource for all container levels when stitching
contained resources back together.
</action>
<action type="fix">
Server methods with @Include parameter would sometimes fail when no _include was actually
specified in query strings.
</action>
<action type="fix">
Client requests for IdentifierDt types (such as Patient.identifier) did not create the correct
query string if the system is null.
</action>
<action type="add">
Add support for paging responses from RESTful servers.
</action>
<action type="fix">
Don't fail on narrative blocks in JSON resources with only an XML declaration but no content (these are
produced by the Health Intersections server)
</action>
<action type="fix">
Server now automatically compresses responses if the client indicates support
</action>
<action type="fix">
Server failed to support optional parameters when type is String and :exact qualifier is used
</action>
<action type="fix">
Read method in client correctly populated resource ID in returned object
</action>
<action type="add">
Support added for deleted-entry by/name, by/email, and comment from Tombstones spec
</action>
</release>
<release version="0.3" date="2014-May-12" description="This release corrects lots of bugs and introduces the fluent client mode">
</release>
</body>
</document>

View File

@ -107,6 +107,8 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini
if (IResource.class.isAssignableFrom(next)) {
myDatatypeToElementDefinition.put(ResourceReferenceDt.class, nextDef);
alternateElementName = getElementName() + "Resource";
myDatatypeToElementName.put(ResourceReferenceDt.class, alternateElementName);
}
myDatatypeToElementDefinition.put(next, nextDef);

View File

@ -20,8 +20,12 @@ package ca.uhn.fhir.model.primitive;
* #L%
*/
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
@ -33,6 +37,10 @@ import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import org.apache.commons.lang3.StringEscapeUtils;
import org.codehaus.stax2.XMLOutputFactory2;
import org.codehaus.stax2.io.EscapingWriterFactory;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.api.BasePrimitive;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
@ -50,7 +58,7 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
public XhtmlDt() {
// nothing
}
/**
* Constructor which accepts a string code
*
@ -64,9 +72,8 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
/**
* Accepts a textual DIV and parses it into XHTML events which are stored internally.
* <p>
* <b>Formatting note:</b> The text will be trimmed {@link String#trim()}. If the text does not start with
* an HTML tag (generally this would be a div tag), a div tag will be automatically
* placed surrounding the text.
* <b>Formatting note:</b> The text will be trimmed {@link String#trim()}. If the text does not start with an HTML tag (generally this would be a div tag), a div tag will be automatically placed
* surrounding the text.
* </p>
*/
@Override
@ -75,7 +82,7 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
myValue = null;
return;
}
String val = theValue.trim();
if (!val.startsWith("<")) {
val = "<div>" + val + "</div>";
@ -84,7 +91,7 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
myValue = null;
return;
}
try {
ArrayList<XMLEvent> value = new ArrayList<XMLEvent>();
XMLEventReader er = XMLInputFactory.newInstance().createXMLEventReader(new StringReader(val));
@ -101,9 +108,9 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
}
}
setValue(value);
} catch (XMLStreamException e) {
throw new DataFormatException("String does not appear to be valid XML/XHTML (error is \""+e.getMessage() + "\"): " + theValue, e);
throw new DataFormatException("String does not appear to be valid XML/XHTML (error is \"" + e.getMessage() + "\"): " + theValue, e);
} catch (FactoryConfigurationError e) {
throw new ConfigurationException(e);
}
@ -116,9 +123,17 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
}
try {
StringWriter w = new StringWriter();
XMLEventWriter ew = XMLOutputFactory.newInstance().createXMLEventWriter(w);
XMLOutputFactory newInstance = XMLOutputFactory.newInstance();
newInstance.setProperty(XMLOutputFactory2.P_TEXT_ESCAPER, new MyEscaper());
XMLEventWriter ew = newInstance.createXMLEventWriter(w);
for (XMLEvent next : myValue) {
ew.add(next);
if (next.isCharacters()) {
ew.add(next);
} else {
ew.add(next);
}
}
ew.close();
return w.toString();
@ -129,6 +144,55 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
}
}
private static class MyEscaper implements EscapingWriterFactory {
@Override
public Writer createEscapingWriterFor(final Writer theW, String theEnc) throws UnsupportedEncodingException {
return new Writer() {
@Override
public void write(char[] theCbuf, int theOff, int theLen) throws IOException {
boolean hasEscapable = false;
for (int i = 0; i < theLen && !hasEscapable; i++) {
char nextChar = theCbuf[i + theOff];
switch (nextChar) {
case '<':
case '>':
case '"':
case '&':
hasEscapable = true;
}
}
if (!hasEscapable) {
theW.write(theCbuf, theOff, theLen);
return;
}
String escaped = StringEscapeUtils.escapeXml10(new String(theCbuf, theOff, theLen));
theW.write(escaped.toCharArray());
}
@Override
public void flush() throws IOException {
theW.flush();
}
@Override
public void close() throws IOException {
theW.close();
}
};
}
@Override
public Writer createEscapingWriterFor(OutputStream theOut, String theEnc) throws UnsupportedEncodingException {
// TODO Auto-generated method stub
return null;
}
}
@Override
public List<XMLEvent> getValue() {
return myValue;

View File

@ -28,6 +28,7 @@ import java.lang.annotation.Target;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;
@ -76,5 +77,12 @@ public @interface Search {
*/
// NB: Read, Search (maybe others) share this annotation method, so update the javadocs everywhere
Class<? extends IResource> type() default IResource.class;
/**
* This is an experimental option - Use with caution
*
* @see IDynamicSearchResourceProvider
*/
boolean dynamic() default false;
}

View File

@ -63,8 +63,10 @@ import ca.uhn.fhir.rest.server.BundleProviders;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.SearchParameterMap;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -82,7 +84,6 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
private FhirContext myContext;
private Method myMethod;
private List<IParameter> myParameters;
private Object myProvider;
public BaseMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
@ -92,7 +93,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
myMethod = theMethod;
myContext = theContext;
myProvider = theProvider;
myParameters = MethodUtil.getResourceParameters(theMethod);
myParameters = MethodUtil.getResourceParameters(theMethod, theProvider);
}
public List<Class<?>> getAllowableParamAnnotations() {
@ -349,7 +350,12 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
if (read != null) {
return new ReadMethodBinding(returnType, theMethod, theContext, theProvider);
} else if (search != null) {
return new SearchMethodBinding(returnType, theMethod, theContext, theProvider);
if (search.dynamic()) {
IDynamicSearchResourceProvider provider = (IDynamicSearchResourceProvider) theProvider;
return new DynamicSearchMethodBinding(returnType, theMethod, theContext, provider);
} else {
return new SearchMethodBinding(returnType, theMethod, theContext, theProvider);
}
} else if (conformance != null) {
return new ConformanceMethodBinding(theMethod, theContext, theProvider);
} else if (create != null) {

View File

@ -172,12 +172,14 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
break;
case UPDATE:
if (response.getCreated() == null || Boolean.FALSE.equals(response.getCreated())) {
if (response == null || response.getCreated() == null || Boolean.FALSE.equals(response.getCreated())) {
servletResponse.setStatus(Constants.STATUS_HTTP_200_OK);
} else {
servletResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
}
addLocationHeader(theRequest, servletResponse, response);
if (response != null && response.getId() != null) {
addLocationHeader(theRequest, servletResponse, response);
}
break;
case VALIDATE:

View File

@ -0,0 +1,164 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 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.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class DynamicSearchMethodBinding extends BaseResourceReturningMethodBinding {
private IDynamicSearchResourceProvider myProvider;
private List<RuntimeSearchParam> mySearchParameters;
private HashSet<String> myParamNames;
private Integer myIdParamIndex;
public DynamicSearchMethodBinding(Class<? extends IResource> theReturnResourceType, Method theMethod, FhirContext theConetxt, IDynamicSearchResourceProvider theProvider) {
super(theReturnResourceType, theMethod, theConetxt, theProvider);
myProvider = theProvider;
mySearchParameters = myProvider.getSearchParameters();
myParamNames = new HashSet<String>();
for (RuntimeSearchParam next : mySearchParameters) {
myParamNames.add(next.getName());
}
myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod);
}
@Override
public List<IParameter> getParameters() {
List<IParameter> retVal = new ArrayList<IParameter>(super.getParameters());
for (RuntimeSearchParam next : mySearchParameters) {
}
return retVal;
}
@Override
public ReturnTypeEnum getReturnType() {
return ReturnTypeEnum.BUNDLE;
}
@Override
public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
if (myIdParamIndex != null) {
theMethodParams[myIdParamIndex] = theRequest.getId();
}
Object response = invokeServerMethod(theMethodParams);
return toResourceList(response);
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
return RestfulOperationTypeEnum.SEARCH_TYPE;
}
@Override
public RestfulOperationSystemEnum getSystemOperationType() {
return null;
}
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DynamicSearchMethodBinding.class);
@Override
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
if (!theRequest.getResourceName().equals(getResourceName())) {
ourLog.trace("Method {} doesn't match because resource name {} != {}", getMethod().getName(), theRequest.getResourceName(), getResourceName());
return false;
}
if (theRequest.getId() != null && myIdParamIndex == null) {
ourLog.trace("Method {} doesn't match because ID is not null: {}", theRequest.getId());
return false;
}
if (theRequest.getRequestType() == RequestType.GET && theRequest.getOperation() != null && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
ourLog.trace("Method {} doesn't match because request type is GET but operation is not null: {}", theRequest.getId(), theRequest.getOperation());
return false;
}
if (theRequest.getRequestType() == RequestType.POST && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
ourLog.trace("Method {} doesn't match because request type is POST but operation is not _search: {}", theRequest.getId(), theRequest.getOperation());
return false;
}
if (theRequest.getRequestType() != RequestType.GET && theRequest.getRequestType() != RequestType.POST) {
ourLog.trace("Method {} doesn't match because request type is {}", getMethod());
return false;
}
if (theRequest.getCompartmentName() != null) {
ourLog.trace("Method {} doesn't match because it is for compartment {}", new Object[] { getMethod(), theRequest.getCompartmentName() });
return false;
}
for (String next : theRequest.getParameters().keySet()) {
if (next.charAt(0) == '_') {
continue;
}
String nextQualified = next;
int colonIndex = next.indexOf(':');
int dotIndex = next.indexOf('.');
if (colonIndex != -1 || dotIndex != -1) {
int index;
if (colonIndex != -1 && dotIndex != -1) {
index = Math.min(colonIndex, dotIndex);
} else {
index = (colonIndex != -1) ? colonIndex : dotIndex;
}
next = next.substring(0, index);
}
if (!myParamNames.contains(next)) {
ourLog.trace("Method {} doesn't match because has parameter {}", new Object[] { getMethod(), nextQualified });
return false;
}
}
return true;
}
@Override
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
// there should be no way to call this....
throw new UnsupportedOperationException("Dynamic search methods are only used for server implementations");
}
public Collection<? extends RuntimeSearchParam> getSearchParams() {
return mySearchParameters;
}
}

View File

@ -0,0 +1,169 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 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.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.param.CompositeOrListParam;
import ca.uhn.fhir.rest.param.DateOrListParam;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.NumberOrListParam;
import ca.uhn.fhir.rest.param.NumberParam;
import ca.uhn.fhir.rest.param.QuantityOrListParam;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.ReferenceOrListParam;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenOrListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
import ca.uhn.fhir.rest.server.SearchParameterMap;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class DynamicSearchParameter implements IParameter {
private Map<String, RuntimeSearchParam> myNameToParam = new HashMap<String, RuntimeSearchParam>();
public DynamicSearchParameter(IDynamicSearchResourceProvider theProvider) {
for (RuntimeSearchParam next : theProvider.getSearchParameters()) {
myNameToParam.put(next.getName(), next);
}
}
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException {
throw new UnsupportedOperationException("Dynamic search is not supported in client mode (use fluent client for dynamic-like searches)");
}
@SuppressWarnings("unchecked")
@Override
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
SearchParameterMap retVal = new SearchParameterMap();
for (String next : theRequest.getParameters().keySet()) {
String qualifier = null;
String qualifiedParamName = next;
RuntimeSearchParam param = myNameToParam.get(next);
if (param == null) {
int colonIndex = next.indexOf(':');
int dotIndex = next.indexOf('.');
if (colonIndex != -1 || dotIndex != -1) {
int index;
if (colonIndex != -1 && dotIndex != -1) {
index = Math.min(colonIndex, dotIndex);
} else {
index = (colonIndex != -1) ? colonIndex : dotIndex;
}
qualifier = next.substring(index);
next = next.substring(0, index);
param = myNameToParam.get(next);
}
}
if (param != null) {
for (String nextValue : theRequest.getParameters().get(qualifiedParamName)) {
QualifiedParamList paramList = QualifiedParamList.splitQueryStringByCommasIgnoreEscape(qualifier, nextValue);
switch (param.getParamType()) {
case COMPOSITE:
Class<? extends IQueryParameterType> left = toParamType(param.getCompositeOf().get(0));
Class<? extends IQueryParameterType> right = toParamType(param.getCompositeOf().get(0));
@SuppressWarnings({ "rawtypes" })
CompositeOrListParam compositeOrListParam = new CompositeOrListParam(left, right);
compositeOrListParam.setValuesAsQueryTokens(paramList);
retVal.add(next, compositeOrListParam);
break;
case DATE:
DateOrListParam dateOrListParam = new DateOrListParam();
dateOrListParam.setValuesAsQueryTokens(paramList);
retVal.add(next, dateOrListParam);
break;
case NUMBER:
NumberOrListParam numberOrListParam = new NumberOrListParam();
numberOrListParam.setValuesAsQueryTokens(paramList);
retVal.add(next, numberOrListParam);
break;
case QUANTITY:
QuantityOrListParam quantityOrListParam = new QuantityOrListParam();
quantityOrListParam.setValuesAsQueryTokens(paramList);
retVal.add(next, quantityOrListParam);
break;
case REFERENCE:
ReferenceOrListParam referenceOrListParam = new ReferenceOrListParam();
referenceOrListParam.setValuesAsQueryTokens(paramList);
retVal.add(next, referenceOrListParam);
break;
case STRING:
StringOrListParam stringOrListParam = new StringOrListParam();
stringOrListParam.setValuesAsQueryTokens(paramList);
retVal.add(next, stringOrListParam);
break;
case TOKEN:
TokenOrListParam tokenOrListParam = new TokenOrListParam();
tokenOrListParam.setValuesAsQueryTokens(paramList);
retVal.add(next, tokenOrListParam);
break;
}
}
}
}
return retVal;
}
private Class<? extends IQueryParameterType> toParamType(RuntimeSearchParam theRuntimeSearchParam) {
switch (theRuntimeSearchParam.getParamType()) {
case COMPOSITE:
throw new IllegalStateException("Composite subtype");
case DATE:
return DateParam.class;
case NUMBER:
return NumberParam.class;
case QUANTITY:
return QuantityParam.class;
case REFERENCE:
return ReferenceParam.class;
case STRING:
return StringParam.class;
case TOKEN:
return TokenParam.class;
default:
throw new IllegalStateException("null type");
}
}
@Override
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
// nothing
}
}

View File

@ -1,7 +1,6 @@
package ca.uhn.fhir.rest.method;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.io.PushbackReader;
@ -45,6 +44,7 @@ import ca.uhn.fhir.rest.annotation.IncludeParam;
import ca.uhn.fhir.rest.annotation.OptionalParam;
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.annotation.ServerBase;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.annotation.Sort;
@ -57,6 +57,8 @@ import ca.uhn.fhir.rest.param.CollectionBinder;
import ca.uhn.fhir.rest.param.ResourceParameter;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
import ca.uhn.fhir.rest.server.SearchParameterMap;
import ca.uhn.fhir.util.ReflectionUtil;
/*
@ -91,7 +93,7 @@ public class MethodUtil {
urlBuilder.append(resourceName);
urlBuilder.append('/');
urlBuilder.append(theId.getIdPart());
HttpPutClientInvocation retVal;
String urlExtension = urlBuilder.toString();
if (StringUtils.isBlank(theResourceBody)) {
@ -109,10 +111,10 @@ public class MethodUtil {
urlBuilder.append(versionId);
retVal.addHeader(Constants.HEADER_CONTENT_LOCATION, urlBuilder.toString());
}
}
}
addTagsToPostOrPut(theResource, retVal);
return retVal;
}
@ -135,7 +137,7 @@ public class MethodUtil {
String headerValue = clHeaders.get(0);
resource.getId().setValue(headerValue);
}
List<String> categoryHeaders = theHeaders.get(Constants.HEADER_CATEGORY_LC);
if (categoryHeaders != null && categoryHeaders.size() > 0 && StringUtils.isNotBlank(categoryHeaders.get(0))) {
TagList tagList = new TagList();
@ -223,7 +225,6 @@ public class MethodUtil {
}
static void addTagsToPostOrPut(IResource resource, BaseHttpClientInvocation retVal) {
TagList list = (TagList) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
if (list != null) {
@ -265,8 +266,7 @@ public class MethodUtil {
return new HttpGetClientInvocation("metadata");
}
public static MethodOutcome process2xxResponse(FhirContext theContext, String theResourceName, int theResponseStatusCode, String theResponseMimeType, Reader theResponseReader,
Map<String, List<String>> theHeaders) {
public static MethodOutcome process2xxResponse(FhirContext theContext, String theResourceName, int theResponseStatusCode, String theResponseMimeType, Reader theResponseReader, Map<String, List<String>> theHeaders) {
List<String> locationHeaders = new ArrayList<String>();
List<String> lh = theHeaders.get(Constants.HEADER_LOCATION_LC);
if (lh != null) {
@ -378,7 +378,7 @@ public class MethodUtil {
}
@SuppressWarnings("unchecked")
public static List<IParameter> getResourceParameters(Method theMethod) {
public static List<IParameter> getResourceParameters(Method theMethod, Object theProvider) {
List<IParameter> parameters = new ArrayList<IParameter>();
Class<?>[] parameterTypes = theMethod.getParameterTypes();
@ -389,7 +389,14 @@ public class MethodUtil {
Class<?> parameterType = parameterTypes[paramIndex];
Class<? extends java.util.Collection<?>> outerCollectionType = null;
Class<? extends java.util.Collection<?>> innerCollectionType = null;
if (TagList.class.isAssignableFrom(parameterType)) {
if (SearchParameterMap.class.equals(parameterType)) {
if (theProvider instanceof IDynamicSearchResourceProvider) {
Search searchAnnotation = theMethod.getAnnotation(Search.class);
if (searchAnnotation != null && searchAnnotation.dynamic()) {
param = new DynamicSearchParameter((IDynamicSearchResourceProvider) theProvider);
}
}
} else if (TagList.class.isAssignableFrom(parameterType)) {
// TagList is handled directly within the method bindings
param = new NullParameter();
} else {
@ -403,8 +410,7 @@ public class MethodUtil {
parameterType = ReflectionUtil.getGenericCollectionTypeOfMethodParameter(theMethod, paramIndex);
}
if (Collection.class.isAssignableFrom(parameterType)) {
throw new ConfigurationException("Argument #" + paramIndex + " of Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName()
+ "' is of an invalid generic type (can not be a collection of a collection of a collection)");
throw new ConfigurationException("Argument #" + paramIndex + " of Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is of an invalid generic type (can not be a collection of a collection of a collection)");
}
}
if (parameterType.equals(HttpServletRequest.class) || parameterType.equals(ServletRequest.class)) {
@ -443,19 +449,16 @@ public class MethodUtil {
instantiableCollectionType = null;
specType = String.class;
} else if ((parameterType != Include.class && parameterType != PathSpecification.class) || innerCollectionType == null || outerCollectionType != null) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' is annotated with @" + IncludeParam.class.getSimpleName() + " but has a type other than Collection<"
+ Include.class.getSimpleName() + ">");
throw new ConfigurationException("Method '" + theMethod.getName() + "' is annotated with @" + IncludeParam.class.getSimpleName() + " but has a type other than Collection<" + Include.class.getSimpleName() + ">");
} else {
instantiableCollectionType = (Class<? extends Collection<Include>>) CollectionBinder.getInstantiableCollectionType(innerCollectionType, "Method '" + theMethod.getName()
+ "'");
instantiableCollectionType = (Class<? extends Collection<Include>>) CollectionBinder.getInstantiableCollectionType(innerCollectionType, "Method '" + theMethod.getName() + "'");
specType = parameterType;
}
param = new IncludeParameter((IncludeParam) nextAnnotation, instantiableCollectionType, specType);
} else if (nextAnnotation instanceof ResourceParam) {
if (!IResource.class.isAssignableFrom(parameterType)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' is annotated with @" + ResourceParam.class.getSimpleName()
+ " but has a type that is not an implemtation of " + IResource.class.getCanonicalName());
throw new ConfigurationException("Method '" + theMethod.getName() + "' is annotated with @" + ResourceParam.class.getSimpleName() + " but has a type that is not an implemtation of " + IResource.class.getCanonicalName());
}
param = new ResourceParameter((Class<? extends IResource>) parameterType);
} else if (nextAnnotation instanceof IdParam || nextAnnotation instanceof VersionIdParam) {
@ -479,8 +482,8 @@ public class MethodUtil {
}
if (param == null) {
throw new ConfigurationException("Parameter #" + ((paramIndex + 1)) + "/" + (parameterTypes.length) + " of method '" + theMethod.getName() + "' on type '"
+ theMethod.getDeclaringClass().getCanonicalName() + "' has no recognized FHIR interface parameter annotations. Don't know how to handle this parameter");
throw new ConfigurationException("Parameter #" + ((paramIndex + 1)) + "/" + (parameterTypes.length) + " of method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName()
+ "' has no recognized FHIR interface parameter annotations. Don't know how to handle this parameter");
}
param.initializeTypes(theMethod, outerCollectionType, innerCollectionType, parameterType);

View File

@ -23,7 +23,6 @@ package ca.uhn.fhir.rest.method;
import java.util.ArrayList;
import java.util.StringTokenizer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
@ -41,7 +40,7 @@ public class QualifiedParamList extends ArrayList<String> {
super(theCapacity);
}
public QualifiedParamList(FhirContext theContext, IQueryParameterOr<?> theNextOr) {
public QualifiedParamList(IQueryParameterOr<?> theNextOr) {
for (IQueryParameterType next : theNextOr.getValuesAsQueryTokens()) {
if (myQualifier==null) {
myQualifier=next.getQueryParameterQualifier();

View File

@ -60,7 +60,6 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
private Class<? extends IResource> myDeclaredResourceType;
private String myDescription;
private Integer myIdParamIndex;
private String myQueryName;
@SuppressWarnings("unchecked")
@ -118,40 +117,6 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
}
public static QualifierDetails extractQualifiersFromParameterName(String theParamName) {
QualifierDetails retVal = new QualifierDetails();
if (theParamName == null || theParamName.length() == 0) {
return retVal;
}
int dotIdx = -1;
int colonIdx = -1;
for (int idx = 0; idx < theParamName.length(); idx++) {
char nextChar = theParamName.charAt(idx);
if (nextChar == '.' && dotIdx == -1) {
dotIdx = idx;
} else if (nextChar == ':' && colonIdx == -1) {
colonIdx = idx;
}
}
if (dotIdx != -1 && colonIdx != -1) {
if (dotIdx < colonIdx) {
retVal.setDotQualifier(theParamName.substring(dotIdx, colonIdx));
retVal.setColonQualifier(theParamName.substring(colonIdx));
} else {
retVal.setColonQualifier(theParamName.substring(colonIdx, dotIdx));
retVal.setDotQualifier(theParamName.substring(dotIdx));
}
} else if (dotIdx != -1) {
retVal.setDotQualifier(theParamName.substring(dotIdx));
} else if (colonIdx != -1) {
retVal.setColonQualifier(theParamName.substring(colonIdx));
}
return retVal;
}
public Class<? extends IResource> getDeclaredResourceType() {
return myDeclaredResourceType;
}
@ -322,6 +287,15 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
}
public void setResourceType(Class<? extends IResource> resourceType) {
this.myDeclaredResourceType = resourceType;
}
@Override
public String toString() {
return getMethod().toString();
}
private List<String> processWhitelistAndBlacklist(List<String> theQualifiedNames, Set<String> theQualifierWhitelist, Set<String> theQualifierBlacklist) {
if (theQualifierWhitelist == null && theQualifierBlacklist == null) {
return theQualifiedNames;
@ -337,10 +311,6 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
return retVal;
}
public void setResourceType(Class<? extends IResource> resourceType) {
this.myDeclaredResourceType = resourceType;
}
public static BaseHttpClientInvocation createSearchInvocation(FhirContext theContext, String theResourceName, Map<String, List<String>> theParameters, IdDt theId, String theCompartmentName,
SearchStyleEnum theSearchStyle) {
SearchStyleEnum searchStyle = theSearchStyle;
@ -402,6 +372,40 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
return invocation;
}
public static QualifierDetails extractQualifiersFromParameterName(String theParamName) {
QualifierDetails retVal = new QualifierDetails();
if (theParamName == null || theParamName.length() == 0) {
return retVal;
}
int dotIdx = -1;
int colonIdx = -1;
for (int idx = 0; idx < theParamName.length(); idx++) {
char nextChar = theParamName.charAt(idx);
if (nextChar == '.' && dotIdx == -1) {
dotIdx = idx;
} else if (nextChar == ':' && colonIdx == -1) {
colonIdx = idx;
}
}
if (dotIdx != -1 && colonIdx != -1) {
if (dotIdx < colonIdx) {
retVal.setDotQualifier(theParamName.substring(dotIdx, colonIdx));
retVal.setColonQualifier(theParamName.substring(colonIdx));
} else {
retVal.setColonQualifier(theParamName.substring(colonIdx, dotIdx));
retVal.setDotQualifier(theParamName.substring(dotIdx));
}
} else if (dotIdx != -1) {
retVal.setDotQualifier(theParamName.substring(dotIdx));
} else if (colonIdx != -1) {
retVal.setColonQualifier(theParamName.substring(colonIdx));
}
return retVal;
}
public static class QualifierDetails {
private String myColonQualifier;
@ -467,11 +471,6 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
public String toString() {
return getMethod().toString();
}
public static enum RequestType {
DELETE, GET, OPTIONS, POST, PUT
}

View File

@ -156,7 +156,7 @@ public class SearchParameter extends BaseQueryParameter {
List<IQueryParameterOr<?>> val = myParamBinder.encode(theContext, theObject);
for (IQueryParameterOr<?> nextOr : val) {
retVal.add(new QualifiedParamList(theContext, nextOr));
retVal.add(new QualifiedParamList(nextOr));
}
return retVal;

View File

@ -25,13 +25,18 @@ import java.util.List;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
abstract class BaseAndListParam<T extends IQueryParameterOr<?>> implements IQueryParameterAnd<T> {
public abstract class BaseAndListParam<T extends IQueryParameterOr<?>> implements IQueryParameterAnd<T> {
private List<T> myValues=new ArrayList<T>();
public void addValue(T theValue) {
myValues.add(theValue);
}
@Override
public void setValuesAsQueryTokens(List<QualifiedParamList> theParameters) throws InvalidRequestException {
myValues.clear();
@ -42,6 +47,8 @@ abstract class BaseAndListParam<T extends IQueryParameterOr<?>> implements IQuer
}
}
public abstract SearchParamTypeEnum getSearchParamType();
abstract T newInstance();
@Override

View File

@ -25,6 +25,7 @@ import java.util.List;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.method.QualifiedParamList;
@ -33,10 +34,10 @@ abstract class BaseOrListParam<T extends IQueryParameterType> implements IQueryP
private List<T> myList=new ArrayList<T>();
public void addToken(T theParam) {
Validate.notNull(theParam,"Param can not be null");
myList.add(theParam);
}
// public void addToken(T theParam) {
// Validate.notNull(theParam,"Param can not be null");
// myList.add(theParam);
// }
@Override
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {

View File

@ -117,7 +117,7 @@ public class CodingListParam implements IQueryParameterOr<IQueryParameterType>,
}
@Override
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
getCodings().clear();
for (String string : theParameters) {
CodingDt dt = new CodingDt();

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
/*
* #%L
@ -38,6 +39,11 @@ public class CompositeAndListParam<A extends IQueryParameterType, B extends IQue
CompositeOrListParam<A,B> newInstance() {
return new CompositeOrListParam<A,B>(myLeftType, myRightType);
}
@Override
public SearchParamTypeEnum getSearchParamType() {
return SearchParamTypeEnum.COMPOSITE;
}
}

View File

@ -34,6 +34,14 @@ public class CompositeOrListParam<A extends IQueryParameterType, B extends IQuer
myRightType = theRightType;
}
public Class<A> getLeftType() {
return myLeftType;
}
public Class<B> getRightType() {
return myRightType;
}
@Override
CompositeParam<A,B> newInstance() {
return new CompositeParam<A,B>(myLeftType, myRightType);

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
/*
* #%L
* HAPI FHIR - Core Library
@ -28,4 +30,9 @@ public class DateAndListParam extends BaseAndListParam<DateOrListParam> {
return new DateOrListParam();
}
@Override
public SearchParamTypeEnum getSearchParamType() {
return SearchParamTypeEnum.DATE;
}
}

View File

@ -154,7 +154,7 @@ public class DateParam extends DateTimeDt implements IQueryParameterType, IQuery
}
@Override
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
myBase.setMissing(null);
myComparator = null;
setValueAsString(null);
@ -164,6 +164,7 @@ public class DateParam extends DateTimeDt implements IQueryParameterType, IQuery
} else if (theParameters.size() > 1) {
throw new InvalidRequestException("This server does not support multi-valued dates for this paramater: " + theParameters);
}
}
@Override

View File

@ -59,7 +59,7 @@ public class IdentifierListParam implements IQueryParameterOr<IQueryParameterTyp
}
@Override
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
for (String string : theParameters) {
IdentifierDt dt = new IdentifierDt();
dt.setValueAsQueryToken(null, string);

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
/*
* #%L
* HAPI FHIR - Core Library
@ -27,5 +29,10 @@ public class NumberAndListParam extends BaseAndListParam<NumberOrListParam> {
NumberOrListParam newInstance() {
return new NumberOrListParam();
}
@Override
public SearchParamTypeEnum getSearchParamType() {
return SearchParamTypeEnum.NUMBER;
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
/*
* #%L
* HAPI FHIR - Core Library
@ -28,4 +30,9 @@ public class QuantityAndListParam extends BaseAndListParam<QuantityOrListParam>
return new QuantityOrListParam();
}
@Override
public SearchParamTypeEnum getSearchParamType() {
return SearchParamTypeEnum.QUANTITY;
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
/*
* #%L
* HAPI FHIR - Core Library
@ -28,4 +30,10 @@ public class ReferenceAndListParam extends BaseAndListParam<ReferenceOrListParam
return new ReferenceOrListParam();
}
@Override
public SearchParamTypeEnum getSearchParamType() {
return SearchParamTypeEnum.REFERENCE;
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
/*
* #%L
* HAPI FHIR - Core Library
@ -28,4 +30,9 @@ public class StringAndListParam extends BaseAndListParam<StringOrListParam> {
return new StringOrListParam();
}
@Override
public SearchParamTypeEnum getSearchParamType() {
return SearchParamTypeEnum.STRING;
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
/*
* #%L
* HAPI FHIR - Core Library
@ -28,4 +30,10 @@ public class TokenAndListParam extends BaseAndListParam<TokenOrListParam> {
return new TokenOrListParam();
}
@Override
public SearchParamTypeEnum getSearchParamType() {
return SearchParamTypeEnum.TOKEN;
}
}

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.rest.server;
* #L%
*/
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
/**
@ -30,16 +31,11 @@ public class HardcodedServerAddressStrategy implements IServerAddressStrategy {
private String myValue;
public HardcodedServerAddressStrategy() {
//nothing
}
public HardcodedServerAddressStrategy(String theValue) {
myValue=theValue;
// nothing
}
@Override
public String determineServerBase(HttpServletRequest theRequest) {
return myValue;
public HardcodedServerAddressStrategy(String theValue) {
myValue = theValue;
}
public String getValue() {
@ -50,4 +46,9 @@ public class HardcodedServerAddressStrategy implements IServerAddressStrategy {
myValue = theValue;
}
@Override
public String determineServerBase(ServletContext theServletContext, HttpServletRequest theRequest) {
return myValue;
}
}

View File

@ -0,0 +1,35 @@
package ca.uhn.fhir.rest.server;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 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.util.List;
import ca.uhn.fhir.context.RuntimeSearchParam;
/**
* This is still an experimental API - It isn't meant for public consumption yet. Get in touch if you'd like to use it
* and maybe we can help work out a good design together.
*/
public interface IDynamicSearchResourceProvider extends IResourceProvider {
List<RuntimeSearchParam> getSearchParameters();
}

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.rest.server;
* #L%
*/
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
/**
@ -31,6 +32,6 @@ public interface IServerAddressStrategy {
/**
* Determine the server base for a given request
*/
public String determineServerBase(HttpServletRequest theRequest);
String determineServerBase(ServletContext theServletContext, HttpServletRequest theRequest);
}

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.rest.server;
* #L%
*/
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
@ -30,13 +31,13 @@ import org.apache.commons.lang3.StringUtils;
public class IncomingRequestAddressStrategy implements IServerAddressStrategy {
@Override
public String determineServerBase(HttpServletRequest theRequest) {
public String determineServerBase(ServletContext theServletContext, HttpServletRequest theRequest) {
String requestFullPath = StringUtils.defaultString(theRequest.getRequestURI());
String servletPath = StringUtils.defaultString(theRequest.getServletPath());
StringBuffer requestUrl = theRequest.getRequestURL();
String servletContextPath = "";
if (theRequest.getServletContext() != null) {
servletContextPath = StringUtils.defaultString(theRequest.getServletContext().getContextPath());
if (theServletContext != null) {
servletContextPath = StringUtils.defaultString(theServletContext.getContextPath());
// } else {
// servletContextPath = servletPath;
}
@ -46,15 +47,23 @@ public class IncomingRequestAddressStrategy implements IServerAddressStrategy {
requestPath = requestPath.substring(1);
}
int startOfPath = requestUrl.indexOf("//");
if (startOfPath != -1 && (startOfPath + 2) < requestUrl.length()) {
startOfPath = requestUrl.indexOf("/", startOfPath + 2);
}
if (startOfPath == -1) {
startOfPath = 0;
}
int contextIndex;
if (servletPath.length() == 0) {
if (requestPath.length() == 0) {
contextIndex = requestUrl.length();
} else {
contextIndex = requestUrl.indexOf(requestPath);
contextIndex = requestUrl.indexOf(requestPath, startOfPath);
}
} else {
contextIndex = requestUrl.indexOf(servletPath);
contextIndex = requestUrl.indexOf(servletPath, startOfPath);
}
String fhirServerBase;

View File

@ -454,12 +454,14 @@ public class RestfulServer extends HttpServlet {
String servletPath = StringUtils.defaultString(theRequest.getServletPath());
StringBuffer requestUrl = theRequest.getRequestURL();
String servletContextPath = "";
if (theRequest.getServletContext() != null) {
servletContextPath = StringUtils.defaultString(theRequest.getServletContext().getContextPath());
// } else {
// servletContextPath = servletPath;
}
// if (getServletContext().getMajorVersion() >= 3) {
// // getServletContext is only supported in version 3+ of servlet-api
if (getServletContext() != null) {
servletContextPath = StringUtils.defaultString(getServletContext().getContextPath());
}
// }
if (ourLog.isTraceEnabled()) {
ourLog.trace("Request FullPath: {}", requestFullPath);
ourLog.trace("Servlet Path: {}", servletPath);
@ -476,7 +478,7 @@ public class RestfulServer extends HttpServlet {
requestPath = requestPath.substring(1);
}
fhirServerBase = myServerAddressStrategy.determineServerBase(theRequest);
fhirServerBase = myServerAddressStrategy.determineServerBase(getServletContext(), theRequest);
if (fhirServerBase.endsWith("/")) {
fhirServerBase = fhirServerBase.substring(0, fhirServerBase.length() - 1);
@ -951,6 +953,8 @@ public class RestfulServer extends HttpServlet {
if (theServer.getPagingProvider() == null) {
numToReturn = theResult.size();
resourceList = theResult.getResources(0, numToReturn);
validateResourceListNotNull(resourceList);
} else {
IPagingProvider pagingProvider = theServer.getPagingProvider();
if (theLimit == null) {
@ -961,6 +965,7 @@ public class RestfulServer extends HttpServlet {
numToReturn = Math.min(numToReturn, theResult.size() - theOffset);
resourceList = theResult.getResources(theOffset, numToReturn + theOffset);
validateResourceListNotNull(resourceList);
if (theSearchId != null) {
searchId = theSearchId;
@ -1012,6 +1017,12 @@ public class RestfulServer extends HttpServlet {
return bundle;
}
private static void validateResourceListNotNull(List<IResource> theResourceList) {
if (theResourceList == null) {
throw new InternalErrorException("IBundleProvider returned a null list of resources - This is not allowed");
}
}
public static Bundle createBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults) {
Bundle bundle = new Bundle();
bundle.getAuthorName().setValue(theAuthor);

View File

@ -0,0 +1,129 @@
package ca.uhn.fhir.rest.server;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 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.util.LinkedHashMap;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.param.BaseAndListParam;
import ca.uhn.fhir.rest.param.CompositeAndListParam;
import ca.uhn.fhir.rest.param.CompositeOrListParam;
import ca.uhn.fhir.rest.param.DateAndListParam;
import ca.uhn.fhir.rest.param.DateOrListParam;
import ca.uhn.fhir.rest.param.NumberAndListParam;
import ca.uhn.fhir.rest.param.NumberOrListParam;
import ca.uhn.fhir.rest.param.QuantityAndListParam;
import ca.uhn.fhir.rest.param.QuantityOrListParam;
import ca.uhn.fhir.rest.param.ReferenceAndListParam;
import ca.uhn.fhir.rest.param.ReferenceOrListParam;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.param.TokenOrListParam;
public class SearchParameterMap extends LinkedHashMap<String, BaseAndListParam<?>> {
private static final long serialVersionUID = 1L;
public <A extends IQueryParameterType, B extends IQueryParameterType> void add(String theName, CompositeOrListParam<A, B> theCompositeOrListParam) {
@SuppressWarnings("unchecked")
CompositeAndListParam<A, B> andList = (CompositeAndListParam<A, B>) get(theName);
if (andList == null) {
andList = new CompositeAndListParam<A, B>(theCompositeOrListParam.getLeftType(), theCompositeOrListParam.getRightType());
put(theName, andList);
}
andList.addValue(theCompositeOrListParam);
}
public void add(String theName, DateOrListParam theOrListParam) {
DateAndListParam andList = (DateAndListParam) get(theName);
if (andList == null) {
andList = new DateAndListParam();
put(theName, andList);
}
andList.addValue(theOrListParam);
}
public void add(String theName, NumberOrListParam theOrListParam) {
NumberAndListParam andList = (NumberAndListParam) get(theName);
if (andList == null) {
andList = new NumberAndListParam();
put(theName, andList);
}
andList.addValue(theOrListParam);
}
public void add(String theName, TokenOrListParam theOrListParam) {
TokenAndListParam andList = (TokenAndListParam) get(theName);
if (andList == null) {
andList = new TokenAndListParam();
put(theName, andList);
}
andList.addValue(theOrListParam);
}
public void add(String theName, StringOrListParam theOrListParam) {
StringAndListParam andList = (StringAndListParam) get(theName);
if (andList == null) {
andList = new StringAndListParam();
put(theName, andList);
}
andList.addValue(theOrListParam);
}
public void add(String theName, QuantityOrListParam theOrListParam) {
QuantityAndListParam andList = (QuantityAndListParam) get(theName);
if (andList == null) {
andList = new QuantityAndListParam();
put(theName, andList);
}
andList.addValue(theOrListParam);
}
public void add(String theName, ReferenceOrListParam theOrListParam) {
ReferenceAndListParam andList = (ReferenceAndListParam) get(theName);
if (andList == null) {
andList = new ReferenceAndListParam();
put(theName, andList);
}
andList.addValue(theOrListParam);
}
// public void add(String theName, IQueryParameterOr<?> theOr) {
// if (theOr == null) {
// return;
// }
//
// switch (theOr.getClass()) {
//
// }
//
// if (!containsKey(theName)) {
// put(theName, new ArrayList<List<? extends IQueryParameterType>>());
// }
//
// StringAndListParam
//
// IQueryParameterAnd<IQueryParameterOr<?>> and = get(theName);
// and.add(theOr);
// }
}

View File

@ -49,6 +49,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.Metadata;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.method.DynamicSearchMethodBinding;
import ca.uhn.fhir.rest.method.IParameter;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.method.SearchParameter;
@ -61,11 +62,10 @@ import ca.uhn.fhir.util.ExtensionConstants;
* Server FHIR Provider which serves the conformance statement for a RESTful server implementation
*
* <p>
* Note: This class is safe to extend, but it is important to note that the same instance of
* {@link Conformance} is always returned unless {@link #setCache(boolean)} is called with a value
* of <code>false</code>. This means that if you are adding anything to the returned
* conformance instance on each call you should call <code>setCache(false)</code> in
* your provider constructor.
* Note: This class is safe to extend, but it is important to note that the same instance of {@link Conformance} is
* always returned unless {@link #setCache(boolean)} is called with a value of <code>false</code>. This means that if
* you are adding anything to the returned conformance instance on each call you should call
* <code>setCache(false)</code> in your provider constructor.
* </p>
*/
public class ServerConformanceProvider {
@ -142,91 +142,9 @@ public class ServerConformanceProvider {
}
if (nextMethodBinding instanceof SearchMethodBinding) {
SearchMethodBinding searchMethodBinding = (SearchMethodBinding) nextMethodBinding;
includes.addAll(searchMethodBinding.getIncludes());
List<IParameter> params = searchMethodBinding.getParameters();
List<SearchParameter> searchParameters = new ArrayList<SearchParameter>();
for (IParameter nextParameter : params) {
if ((nextParameter instanceof SearchParameter)) {
searchParameters.add((SearchParameter) nextParameter);
}
}
Collections.sort(searchParameters, new Comparator<SearchParameter>() {
@Override
public int compare(SearchParameter theO1, SearchParameter theO2) {
if (theO1.isRequired() == theO2.isRequired()) {
return theO1.getName().compareTo(theO2.getName());
}
if (theO1.isRequired()) {
return -1;
}
return 1;
}
});
if (searchParameters.isEmpty()) {
continue;
}
boolean allOptional = searchParameters.get(0).isRequired() == false;
RestQuery query = null;
if (!allOptional) {
query = rest.addQuery();
query.getDocumentation().setValue(searchMethodBinding.getDescription());
query.addUndeclaredExtension(false, ExtensionConstants.QUERY_RETURN_TYPE, new CodeDt(resourceName));
for (String nextInclude : searchMethodBinding.getIncludes()) {
query.addUndeclaredExtension(false, ExtensionConstants.QUERY_ALLOWED_INCLUDE, new StringDt(nextInclude));
}
}
for (SearchParameter nextParameter : searchParameters) {
String nextParamName = nextParameter.getName();
// String chain = null;
String nextParamUnchainedName = nextParamName;
if (nextParamName.contains(".")) {
// chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
}
String nextParamDescription = nextParameter.getDescription();
/*
* If the parameter has no description, default to the one from the resource
*/
if (StringUtils.isBlank(nextParamDescription)) {
RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
if (paramDef != null) {
nextParamDescription = paramDef.getDescription();
}
}
RestResourceSearchParam param;
if (query == null) {
param = resource.addSearchParam();
} else {
param = query.addParameter();
param.addUndeclaredExtension(false, ExtensionConstants.PARAM_IS_REQUIRED, new BooleanDt(nextParameter.isRequired()));
}
param.setName(nextParamName);
// if (StringUtils.isNotBlank(chain)) {
// param.addChain(chain);
// }
param.setDocumentation(nextParamDescription);
param.setType(nextParameter.getParamType());
for (Class<? extends IResource> nextTarget : nextParameter.getDeclaredTypes()) {
RuntimeResourceDefinition targetDef = myRestfulServer.getFhirContext().getResourceDefinition(nextTarget);
if (targetDef != null) {
ResourceTypeEnum code = ResourceTypeEnum.VALUESET_BINDER.fromCodeString(targetDef.getName());
if (code != null) {
param.addTarget(code);
}
}
}
}
handleSearchMethodBinding(rest, resource, resourceName, def, includes, (SearchMethodBinding) nextMethodBinding);
} else if (nextMethodBinding instanceof DynamicSearchMethodBinding) {
handleDynamicSearchMethodBinding(resource, def, includes, (DynamicSearchMethodBinding) nextMethodBinding);
}
Collections.sort(resource.getOperation(), new Comparator<RestResourceOperation>() {
@ -259,6 +177,149 @@ public class ServerConformanceProvider {
return retVal;
}
private void handleSearchMethodBinding(Rest rest, RestResource resource, String resourceName, RuntimeResourceDefinition def, TreeSet<String> includes, SearchMethodBinding searchMethodBinding) {
includes.addAll(searchMethodBinding.getIncludes());
List<IParameter> params = searchMethodBinding.getParameters();
List<SearchParameter> searchParameters = new ArrayList<SearchParameter>();
for (IParameter nextParameter : params) {
if ((nextParameter instanceof SearchParameter)) {
searchParameters.add((SearchParameter) nextParameter);
}
}
sortSearchParameters(searchParameters);
if (!searchParameters.isEmpty()) {
boolean allOptional = searchParameters.get(0).isRequired() == false;
RestQuery query = null;
if (!allOptional) {
query = rest.addQuery();
query.getDocumentation().setValue(searchMethodBinding.getDescription());
query.addUndeclaredExtension(false, ExtensionConstants.QUERY_RETURN_TYPE, new CodeDt(resourceName));
for (String nextInclude : searchMethodBinding.getIncludes()) {
query.addUndeclaredExtension(false, ExtensionConstants.QUERY_ALLOWED_INCLUDE, new StringDt(nextInclude));
}
}
for (SearchParameter nextParameter : searchParameters) {
String nextParamName = nextParameter.getName();
// String chain = null;
String nextParamUnchainedName = nextParamName;
if (nextParamName.contains(".")) {
// chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
}
String nextParamDescription = nextParameter.getDescription();
/*
* If the parameter has no description, default to the one from the resource
*/
if (StringUtils.isBlank(nextParamDescription)) {
RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
if (paramDef != null) {
nextParamDescription = paramDef.getDescription();
}
}
RestResourceSearchParam param;
if (query == null) {
param = resource.addSearchParam();
} else {
param = query.addParameter();
param.addUndeclaredExtension(false, ExtensionConstants.PARAM_IS_REQUIRED, new BooleanDt(nextParameter.isRequired()));
}
param.setName(nextParamName);
// if (StringUtils.isNotBlank(chain)) {
// param.addChain(chain);
// }
param.setDocumentation(nextParamDescription);
param.setType(nextParameter.getParamType());
for (Class<? extends IResource> nextTarget : nextParameter.getDeclaredTypes()) {
RuntimeResourceDefinition targetDef = myRestfulServer.getFhirContext().getResourceDefinition(nextTarget);
if (targetDef != null) {
ResourceTypeEnum code = ResourceTypeEnum.VALUESET_BINDER.fromCodeString(targetDef.getName());
if (code != null) {
param.addTarget(code);
}
}
}
}
}
}
private void handleDynamicSearchMethodBinding(RestResource resource, RuntimeResourceDefinition def, TreeSet<String> includes, DynamicSearchMethodBinding searchMethodBinding) {
includes.addAll(searchMethodBinding.getIncludes());
List<RuntimeSearchParam> searchParameters = new ArrayList<RuntimeSearchParam>();
searchParameters.addAll(searchMethodBinding.getSearchParams());
sortRuntimeSearchParameters(searchParameters);
if (!searchParameters.isEmpty()) {
for (RuntimeSearchParam nextParameter : searchParameters) {
String nextParamName = nextParameter.getName();
// String chain = null;
String nextParamUnchainedName = nextParamName;
if (nextParamName.contains(".")) {
// chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
}
String nextParamDescription = nextParameter.getDescription();
/*
* If the parameter has no description, default to the one from the resource
*/
if (StringUtils.isBlank(nextParamDescription)) {
RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
if (paramDef != null) {
nextParamDescription = paramDef.getDescription();
}
}
RestResourceSearchParam param;
param = resource.addSearchParam();
param.setName(nextParamName);
// if (StringUtils.isNotBlank(chain)) {
// param.addChain(chain);
// }
param.setDocumentation(nextParamDescription);
param.setType(nextParameter.getParamType());
}
}
}
private void sortRuntimeSearchParameters(List<RuntimeSearchParam> searchParameters) {
Collections.sort(searchParameters, new Comparator<RuntimeSearchParam>() {
@Override
public int compare(RuntimeSearchParam theO1, RuntimeSearchParam theO2) {
return theO1.getName().compareTo(theO2.getName());
}
});
}
private void sortSearchParameters(List<SearchParameter> searchParameters) {
Collections.sort(searchParameters, new Comparator<SearchParameter>() {
@Override
public int compare(SearchParameter theO1, SearchParameter theO2) {
if (theO1.isRequired() == theO2.isRequired()) {
return theO1.getName().compareTo(theO2.getName());
}
if (theO1.isRequired()) {
return -1;
}
return 1;
}
});
}
/**
* Sets the cache property (default is true). If set to true, the same response will be returned for each
* invocation.

View File

@ -0,0 +1,60 @@
package ca.uhn.fhir.util;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 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.net.ServerSocket;
import java.util.ArrayList;
import java.util.List;
/**
* Provides server ports
*/
public class PortUtil {
private static List<Integer> ourPorts = new ArrayList<Integer>();
/**
* This is really only used for unit tests but is included in the library so it can be reused across modules. Use with caution.
*/
public static int findFreePort() {
ServerSocket server;
try {
server = new ServerSocket(0);
int port = server.getLocalPort();
ourPorts.add(port);
server.close();
Thread.sleep(500);
return port;
} catch (IOException e) {
throw new Error(e);
} catch (InterruptedException e) {
throw new Error(e);
}
}
public static List<Integer> list() {
return ourPorts;
}
}

View File

@ -26,14 +26,15 @@ import java.util.List;
import org.apache.commons.lang3.Validate;
import com.phloc.schematron.ISchematronResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
/**
* Resource validator, which checks resources for compliance against various validation schemes (schemas, schematrons,
* etc.)
* Resource validator, which checks resources for compliance against various validation schemes (schemas, schematrons, etc.)
*
* <p>
* To obtain a resource validator, call {@link FhirContext#newValidator()}
@ -43,17 +44,33 @@ public class FhirValidator {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirValidator.class);
private static final String I18N_KEY_NO_PHLOC_WARNING = FhirValidator.class.getName()+".noPhlocWarningOnStartup";
private static final String I18N_KEY_NO_PHLOC_ERROR = FhirValidator.class.getName()+".noPhlocError";
private FhirContext myContext;
private List<IValidator> myValidators = new ArrayList<IValidator>();
private static volatile Boolean ourPhlocPresentOnClasspath;
/**
* Constructor (this should not be called directly, but rather {@link FhirContext#newValidator()} should be called
* to obtain an instance of {@link FhirValidator})
* Constructor (this should not be called directly, but rather {@link FhirContext#newValidator()} should be called to obtain an instance of {@link FhirValidator})
*/
public FhirValidator(FhirContext theFhirContext) {
myContext = theFhirContext;
setValidateAgainstStandardSchema(true);
setValidateAgainstStandardSchematron(true);
if (ourPhlocPresentOnClasspath == null) {
try {
Class.forName("com.phloc.schematron.ISchematronResource");
ourPhlocPresentOnClasspath = true;
} catch (ClassNotFoundException e) {
ourLog.info(theFhirContext.getLocalizer().getMessage(I18N_KEY_NO_PHLOC_WARNING));
ourPhlocPresentOnClasspath = false;
}
}
if (ourPhlocPresentOnClasspath) {
setValidateAgainstStandardSchematron(true);
}
}
private void addOrRemoveValidator(boolean theValidateAgainstStandardSchema, Class<? extends IValidator> type, IValidator instance) {
@ -83,43 +100,43 @@ public class FhirValidator {
}
/**
* Should the validator validate the resource against the base schema (the schema provided with the FHIR
* distribution itself)
* Should the validator validate the resource against the base schema (the schema provided with the FHIR distribution itself)
*/
public boolean isValidateAgainstStandardSchema() {
return haveValidatorOfType(SchemaBaseValidator.class);
}
/**
* Should the validator validate the resource against the base schema (the schema provided with the FHIR
* distribution itself)
* Should the validator validate the resource against the base schema (the schema provided with the FHIR distribution itself)
*/
public boolean isValidateAgainstStandardSchematron() {
return haveValidatorOfType(SchematronBaseValidator.class);
}
/**
* Should the validator validate the resource against the base schema (the schema provided with the FHIR
* distribution itself)
* Should the validator validate the resource against the base schema (the schema provided with the FHIR distribution itself)
*/
public void setValidateAgainstStandardSchema(boolean theValidateAgainstStandardSchema) {
addOrRemoveValidator(theValidateAgainstStandardSchema, SchemaBaseValidator.class, new SchemaBaseValidator());
}
/**
* Should the validator validate the resource against the base schematron (the schematron provided with the FHIR
* distribution itself)
* Should the validator validate the resource against the base schematron (the schematron provided with the FHIR distribution itself)
*/
public void setValidateAgainstStandardSchematron(boolean theValidateAgainstStandardSchematron) {
if (theValidateAgainstStandardSchematron && !ourPhlocPresentOnClasspath) {
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_KEY_NO_PHLOC_ERROR));
}
addOrRemoveValidator(theValidateAgainstStandardSchematron, SchematronBaseValidator.class, new SchematronBaseValidator());
}
/**
* Validates a bundle instance, throwing a {@link ValidationFailureException} if the validation fails. This
* validation includes validation of all resources in the bundle.
* Validates a bundle instance, throwing a {@link ValidationFailureException} if the validation fails. This validation includes validation of all resources in the bundle.
*
* @param theResource The resource to validate
* @throws ValidationFailureException If the validation fails
* @param theResource
* The resource to validate
* @throws ValidationFailureException
* If the validation fails
*/
public void validate(Bundle theBundle) {
Validate.notNull(theBundle, "theBundle must not be null");
@ -140,8 +157,10 @@ public class FhirValidator {
/**
* Validates a resource instance, throwing a {@link ValidationFailureException} if the validation fails
*
* @param theResource The resource to validate
* @throws ValidationFailureException If the validation fails
* @param theResource
* The resource to validate
* @throws ValidationFailureException
* If the validation fails
*/
public void validate(IResource theResource) throws ValidationFailureException {
Validate.notNull(theResource, "theResource must not be null");

View File

@ -1,4 +1,7 @@
ca.uhn.fhir.rest.method.SearchMethodBinding.invalidSpecialParamName=Method [{0}] in provider [{1}] contains search parameter annotated to use name [{2}] - This name is reserved according to the FHIR specification and can not be used as a search parameter name.
ca.uhn.fhir.rest.method.SearchMethodBinding.idWithoutCompartment=Method [{0}] in provider [{1}] has an @IdParam parameter. This is only allowable for compartment search (e.g. @Search(compartment="foo") )
ca.uhn.fhir.rest.method.SearchMethodBinding.idNullForCompartmentSearch=ID parameter can not be null or empty for compartment search
ca.uhn.fhir.rest.method.SearchMethodBinding.idNullForCompartmentSearch=ID parameter can not be null or empty for compartment search
ca.uhn.fhir.validation.FhirValidator.noPhlocWarningOnStartup=Phloc-schematron library not found on classpath, will not attempt to perform schematron validation
ca.uhn.fhir.validation.FhirValidator.noPhlocError=Phloc-schematron library not found on classpath, can not enable perform schematron validation

View File

@ -86,12 +86,12 @@
}
tt {
margin-left: 10px;
white-space: pre;
color: #448;
padding: 4px;
margin-bottom: 5px;
margin-top: 10px;
padding: 2px;
border: 1px solid #AAA;
}
h1,h2,h3,h4,h5 {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -4,18 +4,18 @@
<bannerLeft>
<name>HAPI</name>
<src>images/hapi_fhir_banner.png</src>
<href>http://hl7api.sourceforge.net/hapi-fhir/</href>
<href>http://jamesagnew.github.io/hapi-fhir/</href>
</bannerLeft>
<bannerRight>
<name>FHIR</name>
<src>images/hapi_fhir_banner_right.png</src>
<href>http://hl7api.sourceforge.net/hapi-fhir/</href>
<href>http://jamesagnew.github.io/hapi-fhir/</href>
</bannerRight>
<poweredBy>
<logo name="Hosted on SourceForge.net" href="https://sourceforge.net/projects/hl7api/" img="http://sflogo.sourceforge.net/sflogo.php?group_id=38899&amp;type=2" />
<logo name="Built with Maven 2" href="http://maven.apache.org" img="images/logos/maven-feather.png" />
<logo name="Hosted on GitHub" href="https://github.com/jamesagnew/hapi-fhir" img="./images/github-logo-mini.png"/>
<logo name="Built with Maven 3" href="http://maven.apache.org" img="./images/maven-logo-mini.png" />
</poweredBy>
<version position="left" />

View File

@ -103,7 +103,24 @@
</subsection>
</section>
<section name="Examples">
<subsection name="Populating an Observation Resource">
<p>
The following example shows how to create an observation resource containing
a numeric datatype.
</p>
<macro name="snippet">
<param name="id" value="observation" />
<param name="file" value="examples/src/main/java/example/FhirDataModel.java" />
</macro>
</subsection>
</section>
</body>
</document>

View File

@ -108,6 +108,16 @@
</p>
</section>
<section name="Server Request Logging">
<p>
To enable detailed logging of server requests and responses,
an interceptor may be added to the server which logs each transaction. See
<a href="./doc_rest_server_interceptor.html#Logging">Logging Server Requests</a> for more information.
</p>
</section>
</body>

View File

@ -110,6 +110,21 @@
of detail about each incoming request.
</p>
<p>
The following example shows how to register a logging interceptor within
a FHIR RESTful server.
</p>
<macro name="snippet">
<param name="id" value="loggingInterceptor" />
<param name="file" value="examples/src/main/java/example/ServletExamples.java" />
</macro>
<p>
This interceptor will then produce output similar to the following:
</p>
<source><![CDATA[2014-09-04 02:37:30.030 Source[127.0.0.1] Operation[vread Patient/1667/_history/1] UA[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.94 Safari/537.36] Params[?_format=json]
2014-09-04 03:30:00.443 Source[127.0.0.1] Operation[search-type Organization] UA[Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)] Params[]]]></source>
</subsection>
</section>

View File

@ -17,13 +17,13 @@
<p>
HAPI provides a built-in and configurable mechanism for validating resources.
This mechanism is called the <i>Resource Validator</i>.
This mechanism is called the <i>FHIR Validator</i>.
</p>
<subsection name="Background">
<p>
FHIR resource definitions are distributed with a set of XML schema files (XDS)
FHIR resource definitions are distributed with a set of XML schema files (XSD)
as well as a set of XML Schematron (SCH) files. These two sets of files are
complimentary to each other, meaning that in order to claim compliance to the
FHIR specification, your resources must validate against both sets.
@ -60,6 +60,19 @@
</subsection>
<subsection name="Validating a Set of Files">
<p>
The following example shows how to load a set of resources from files
on disk and validate each one.
</p>
<macro name="snippet">
<param name="id" value="validateFiles" />
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
</macro>
</subsection>
</section>
</body>

View File

@ -39,6 +39,14 @@
<section name="Announcements">
<p>
<b>Sep 8, 2014 - HAPI FHIR 0.6 Released</b> - A new build has been uploaded,
containing a number of new features and bug fixes. See the changelog
for a complete list of what has changed.
<br/>
- <a href="mailto:jamesagnew@users.sourceforge.net">James Agnew</a>
</p>
<p>
<b>July 30, 2014 - HAPI FHIR 0.5 Released</b> - HAPI 0.5 has now been released.
This is surprisingly soon after the last release (and probably not a

View File

@ -0,0 +1,43 @@
package ca.uhn.fhir.model.primitive;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class XhtmlDtTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XhtmlDtTest.class);
@Test
public void testRoundtripTiny() {
String div = "<div>xmlns=&quot;http://hl7.org/fhir&quot;</div>";
XhtmlDt x = new XhtmlDt();
x.setValueAsString(div);
String actual = x.getValueAsString();
ourLog.info("Expected {}", div.replace("\r", "").replace("\n", "\\n"));
ourLog.info("Actual {}", actual.replace("\r\n", "\\r\\n").replace("\n", "\\n"));
assertEquals(div.replace("\r", ""), actual);
}
@Test
public void testRoundtrip() {
String div = "<div><pre>\r\n&lt;<a title=\"Prospective warnings of potential issues when providing care to the patient.\" class=\"dict\" href=\"alert-definitions.html#Alert\"><b>Alert</b></a> xmlns=&quot;http://hl7.org/fhir&quot;&gt; <span style=\"float: right\"><a title=\"Documentation for this format\" href=\"formats.html\"><img alt=\"doco\" src=\"help.png\"/></a></span>\r\n &lt;!-- from <a href=\"resources.html\">Resource</a>: <a href=\"extensibility.html\">extension</a>, <a href=\"extensibility.html#modifierExtension\">modifierExtension</a>, language, <a href=\"narrative.html#Narrative\">text</a>, and <a href=\"references.html#contained\">contained</a> --&gt;\r\n &lt;<a title=\"Identifier assigned to the alert for external use (outside the FHIR environment).\" class=\"dict\" href=\"alert-definitions.html#Alert.identifier\"><b>identifier</b></a>&gt;<span style=\"color: Gray\">&lt;!--</span> <span style=\"color: brown\"><b>0..*</b></span> <span style=\"color: darkgreen\"><a href=\"datatypes.html#Identifier\">Identifier</a></span> <span style=\"color: navy\">Business identifier</span><span style=\"color: Gray\"> --&gt;</span>&lt;/identifier&gt;\r\n &lt;<a title=\"Allows an alert to be divided into different categories like clinical, administrative etc.\" class=\"dict\" href=\"alert-definitions.html#Alert.category\"><b>category</b></a>&gt;<span style=\"color: Gray\">&lt;!--</span> <span style=\"color: brown\"><b>0..1</b></span> <span style=\"color: darkgreen\"><a href=\"datatypes.html#CodeableConcept\">CodeableConcept</a></span> <span style=\"color: navy\">Clinical, administrative, etc.</span><span style=\"color: Gray\"> --&gt;</span>&lt;/category&gt;\r\n &lt;<a title=\"Supports basic workflow.\" class=\"dict\" href=\"alert-definitions.html#Alert.status\"><b>status</b></a> value=&quot;[<span style=\"color: darkgreen\"><a href=\"datatypes.html#code\">code</a></span>]&quot;/&gt;<span style=\"color: Gray\">&lt;!--</span> <span style=\"color: brown\"><b>1..1</b></span> <span style=\"color: navy\"><a style=\"color: navy\" href=\"alert-status.html\">active | inactive | entered in error</a></span><span style=\"color: Gray\"> --&gt;</span>\r\n &lt;<a title=\"The person who this alert concerns.\" class=\"dict\" href=\"alert-definitions.html#Alert.subject\"><b>subject</b></a>&gt;<span style=\"color: Gray\">&lt;!--</span> <span style=\"color: brown\"><b>1..1</b></span> <span style=\"color: darkgreen\"><a href=\"references.html#Resource\">Resource</a>(<a href=\"patient.html#Patient\">Patient</a>)</span> <span style=\"color: navy\">Who is alert about?</span><span style=\"color: Gray\"> --&gt;</span>&lt;/subject&gt;\r\n &lt;<a title=\"The person or device that created the alert.\" class=\"dict\" href=\"alert-definitions.html#Alert.author\"><b>author</b></a>&gt;<span style=\"color: Gray\">&lt;!--</span> <span style=\"color: brown\"><b>0..1</b></span> <span style=\"color: darkgreen\"><a href=\"references.html#Resource\">Resource</a>(<a href=\"practitioner.html#Practitioner\">Practitioner</a>|<a href=\"patient.html#Patient\">Patient</a>|<a href=\"device.html#Device\">Device</a>)</span> <span style=\"color: navy\">Alert creator</span><span style=\"color: Gray\"> --&gt;</span>&lt;/author&gt;\r\n &lt;<a title=\"The textual component of the alert to display to the user.\" class=\"dict\" href=\"alert-definitions.html#Alert.note\"><b>note</b></a> value=&quot;[<span style=\"color: darkgreen\"><a href=\"datatypes.html#string\">string</a></span>]&quot;/&gt;<span style=\"color: Gray\">&lt;!--</span> <span style=\"color: brown\"><b>1..1</b></span> <span style=\"color: navy\">Text of alert</span><span style=\"color: Gray\"> --&gt;</span>\r\n&lt;/Alert&gt;\r\n</pre></div>";
XhtmlDt x = new XhtmlDt();
x.setValueAsString(div);
String actual = x.getValueAsString();
ourLog.info("Expected {}", div.replace("\r", "").replace("\n", "\\n"));
ourLog.info("Actual {}", actual.replace("\r\n", "\\r\\n").replace("\n", "\\n"));
assertEquals(div.replace("\r", ""), actual);
}
}

View File

@ -86,7 +86,8 @@ public class DefaultThymeleafNarrativeGeneratorTest {
enc.setType(EncounterTypeEnum.ANNUAL_DIABETES_MELLITUS_SCREENING);
String title = gen.generateTitle(enc);
assertEquals("1234567 / ADMS / ambulatory / Tue Jan 02 11:11:00 EST 2001 - ?", title);
title = title.replaceAll("00 [A-Z]+ 2001", "00 TZ 2001"); // account for whatever time zone
assertEquals("1234567 / ADMS / ambulatory / Tue Jan 02 11:11:00 TZ 2001 - ?", title);
ourLog.info(title);
}

View File

@ -44,6 +44,7 @@ import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.resource.Profile;
import ca.uhn.fhir.model.dstu.resource.Query;
import ca.uhn.fhir.model.dstu.resource.Specimen;
import ca.uhn.fhir.model.dstu.resource.ValueSet;
@ -442,6 +443,31 @@ public class JsonParserTest {
ourLog.info(encoded);
}
@Test
public void testParseJsonProfile() throws IOException {
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/alert.profile.json"));
ourLog.info(msg);
IParser p = ourCtx.newJsonParser();
Profile res = p.parseResource(Profile.class, msg);
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(res);
ourLog.info(encoded);
JSON expected = JSONSerializer.toJSON(msg.trim());
JSON actual = JSONSerializer.toJSON(encoded.trim());
String exp = expected.toString().replace("\\r\\n", "\\n");
String act = actual.toString().replace("\\r\\n","\\n");
ourLog.info("Expected: {}", exp);
ourLog.info("Actual : {}", act);
assertEquals(exp, act);
}
@Test
@ -926,9 +952,13 @@ public class JsonParserTest {
JSON expected = JSONSerializer.toJSON(jsonString);
JSON actual = JSONSerializer.toJSON(encoded.trim());
ourLog.info("Expected: {}", expected);
ourLog.info("Actual : {}", actual);
assertEquals(expected.toString(), actual.toString());
// The encoded escapes quote marks using XML escaping instead of JSON escaping, which is probably nicer anyhow...
String exp = expected.toString().replace("\\\"Jim\\\"", "&quot;Jim&quot;");
String act = actual.toString();
ourLog.info("Expected: {}", exp);
ourLog.info("Actual : {}", act);
assertEquals(exp, act);
}
@ -969,9 +999,13 @@ public class JsonParserTest {
JSON expected = JSONSerializer.toJSON(jsonString);
JSON actual = JSONSerializer.toJSON(encoded.trim());
ourLog.info("Expected: {}", expected);
ourLog.info("Actual : {}", actual);
assertEquals(expected.toString(), actual.toString());
// The encoded escapes quote marks using XML escaping instead of JSON escaping, which is probably nicer anyhow...
String exp = expected.toString().replace("\\\"Jim\\\"", "&quot;Jim&quot;");
String act = actual.toString();
ourLog.info("Expected: {}", exp);
ourLog.info("Actual : {}", act);
assertEquals(exp, act);
}

View File

@ -18,7 +18,7 @@ import ca.uhn.fhir.util.ElementUtil;
@ResourceDef(name="Patient")
public class MyObservationWithExtensions extends Patient {
@Extension(url = "urn:patientext:att", definedLocally = false, isModifier = false)
@Child(name = "extAtt", order = 0)
private AttachmentDt myExtAtt;

View File

@ -45,6 +45,7 @@ import ca.uhn.fhir.model.dstu.resource.ListResource;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.resource.Profile;
import ca.uhn.fhir.model.dstu.resource.Query;
import ca.uhn.fhir.model.dstu.resource.Specimen;
import ca.uhn.fhir.model.dstu.resource.ValueSet;
@ -67,6 +68,16 @@ public class XmlParserTest {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserTest.class);
@Test
public void testEncodeProfile() {
Profile p = new Profile();
p.getStructureFirstRep().getElementFirstRep().getDefinition().getBinding().setReference(new ResourceReferenceDt("ValudSet/123"));
String encoded = ourCtx.newXmlParser().encodeResourceToString(p);
ourLog.info(encoded);
}
@Test
public void testEncodeBinaryResource() {
@ -909,7 +920,11 @@ public class XmlParserTest {
ValueSet resource = (ValueSet) entry.getResource();
assertEquals("LOINC Codes for Cholesterol", resource.getName().getValue());
assertEquals(summaryText.trim(), entry.getSummary().getValueAsString().trim());
String exp = summaryText.trim();
exp = exp.replace("\"LOINC", "&quot;LOINC");
exp = exp.replace("terol\"", "terol&quot;");
assertEquals(exp, entry.getSummary().getValueAsString().trim());
TagList tl = (TagList) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
assertEquals(1, tl.size());

View File

@ -27,7 +27,7 @@ import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
public class ClientIntegrationTest {
@ -37,7 +37,7 @@ public class ClientIntegrationTest {
@Before
public void before() {
myPort = RandomServerPortProvider.findFreePort();
myPort = PortUtil.findFreePort();
myServer = new Server(myPort);
myPatientProvider = new MyPatientResourceProvider();

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.ResourceBinding;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.Appender;
@ -56,12 +56,12 @@ public class InterceptorTest {
Patient patient = client.read(Patient.class, "1");
assertFalse(patient.getIdentifierFirstRep().isEmpty());
int times = 1;
if (LoggerFactory.getLogger(ResourceBinding.class).isDebugEnabled()) {
times = 3;
}
// int times = 1;
// if (LoggerFactory.getLogger(ResourceBinding.class).isDebugEnabled()) {
// times = 3;
// }
verify(mockAppender, times(times)).doAppend(argThat(new ArgumentMatcher<ILoggingEvent>() {
verify(mockAppender).doAppend(argThat(new ArgumentMatcher<ILoggingEvent>() {
@Override
public boolean matches(final Object argument) {
return ((LoggingEvent) argument).getFormattedMessage().contains("Content-Type: application/xml+fhir; charset=UTF-8");
@ -80,7 +80,7 @@ public class InterceptorTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyProvider patientProvider = new DummyProvider();

View File

@ -35,7 +35,7 @@ import ca.uhn.fhir.rest.annotation.Read;
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.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -122,7 +122,7 @@ public class BinaryTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
ResourceProvider patientProvider = new ResourceProvider();

View File

@ -32,7 +32,7 @@ import ca.uhn.fhir.rest.param.CompositeOrListParam;
import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -100,7 +100,7 @@ public class CompositeParameterTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyObservationResourceProvider patientProvider = new DummyObservationResourceProvider();

View File

@ -30,7 +30,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -101,7 +101,7 @@ public class CompressionTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyProvider patientProvider = new DummyProvider();

View File

@ -30,7 +30,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.rest.server.ResfulServerSelfReferenceTest.DummyPatientResourceProvider;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -42,7 +42,7 @@ public class CorsTest {
@Test
public void testContextWithSpace() throws Exception {
int port = RandomServerPortProvider.findFreePort();
int port = PortUtil.findFreePort();
Server server = new Server(port);
RestfulServer restServer = new RestfulServer();

View File

@ -35,7 +35,7 @@ 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.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -146,7 +146,7 @@ public class CreateTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
PatientProvider patientProvider = new PatientProvider();

View File

@ -30,7 +30,7 @@ import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -134,7 +134,7 @@ public class CustomTypeTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();

View File

@ -0,0 +1,197 @@
package ca.uhn.fhir.rest.server;
import static org.apache.commons.lang.StringUtils.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class DynamicSearchTest {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = new FhirContext();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DynamicSearchTest.class);
private static int ourPort;
private static Server ourServer;
private static SearchParameterMap ourLastSearchParams;
@Before
public void before() {
ourLastSearchParams = null;
}
@Test
public void testSearchOneStringParam() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=param1value");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.getEntries().size());
assertEquals(1, ourLastSearchParams.size());
StringAndListParam andList =(StringAndListParam) ourLastSearchParams.get("param1");
assertEquals(1,andList.getValuesAsQueryTokens().size());
StringOrListParam orList = andList.getValuesAsQueryTokens().get(0);
assertEquals(1,orList.getValuesAsQueryTokens().size());
StringParam param1 = orList.getValuesAsQueryTokens().get(0);
assertEquals("param1value", param1.getValue());
}
@Test
public void testSearchOneStringParamWithOr() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=param1value,param1value2");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.getEntries().size());
assertEquals(1, ourLastSearchParams.size());
StringAndListParam andList =(StringAndListParam) ourLastSearchParams.get("param1");
assertEquals(1,andList.getValuesAsQueryTokens().size());
StringOrListParam orList = andList.getValuesAsQueryTokens().get(0);
assertEquals(2,orList.getValuesAsQueryTokens().size());
StringParam param1 = orList.getValuesAsQueryTokens().get(0);
assertEquals("param1value", param1.getValue());
StringParam param1b = orList.getValuesAsQueryTokens().get(1);
assertEquals("param1value2", param1b.getValue());
}
@Test
public void testSearchOneStringParamWithAnd() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=param1value&param1=param1value2");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.getEntries().size());
assertEquals(1, ourLastSearchParams.size());
StringAndListParam andList =(StringAndListParam) ourLastSearchParams.get("param1");
assertEquals(2,andList.getValuesAsQueryTokens().size());
StringOrListParam orList = andList.getValuesAsQueryTokens().get(0);
assertEquals(1,orList.getValuesAsQueryTokens().size());
StringParam param1 = orList.getValuesAsQueryTokens().get(0);
assertEquals("param1value", param1.getValue());
orList = andList.getValuesAsQueryTokens().get(1);
assertEquals(1,orList.getValuesAsQueryTokens().size());
StringParam param1b = orList.getValuesAsQueryTokens().get(0);
assertEquals("param1value2", param1b.getValue());
}
@Test
public void testConformance() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
Conformance conf = ourCtx.newXmlParser().parseResource(Conformance.class,responseContent);
ourLog.info(responseContent);
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
/**
* Created by dsotnikov on 2/25/2014.
*/
public static class DummyPatientResourceProvider implements IDynamicSearchResourceProvider {
@Override
public Class<? extends IResource> getResourceType() {
return Patient.class;
}
@Override
public List<RuntimeSearchParam> getSearchParameters() {
ArrayList<RuntimeSearchParam> retVal = new ArrayList<RuntimeSearchParam>();
retVal.add(new RuntimeSearchParam("param1", "This is the first parameter", "Patient.param1", SearchParamTypeEnum.STRING));
retVal.add(new RuntimeSearchParam("param2", "This is the second parameter", "Patient.param2", SearchParamTypeEnum.DATE));
return retVal;
}
@Search(dynamic=true)
public List<Patient> search(SearchParameterMap theSearchParams) {
ourLastSearchParams = theSearchParams;
ArrayList<Patient> retVal = new ArrayList<Patient>();
Patient patient = new Patient();
patient.setId("Patient/1");
patient.addIdentifier("system", "fooCompartment");
retVal.add(patient);
return retVal;
}
}
}

View File

@ -33,7 +33,7 @@ import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -131,7 +131,7 @@ public class ExceptionTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();

View File

@ -29,7 +29,7 @@ import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -119,7 +119,7 @@ public class HistoryTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPlainProvider plainProvider = new DummyPlainProvider();

View File

@ -36,8 +36,8 @@ import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.IncludeParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.ElementUtil;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -225,7 +225,7 @@ public class IncludeTest {
public static void beforeClass() throws Exception {
ourCtx = new FhirContext();
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();

View File

@ -0,0 +1,30 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import javax.servlet.http.HttpServletRequest;
import org.junit.Test;
public class IncomingRequestAddressStrategyTest {
/**
* This is an incoming request from an instance of Tomcat on AWS, provided by
* Simon Ling of Systems Made Simple
*/
@Test
public void testAwsUrl() {
HttpServletRequest req = mock(HttpServletRequest.class);
when(req.getRequestURI()).thenReturn("/FhirStorm/fhir/Patient/_search");
when(req.getServletPath()).thenReturn("/fhir");
when(req.getRequestURL()).thenReturn(new StringBuffer().append("http://fhirstorm.dyndns.org:8080/FhirStorm/fhir/Patient/_search"));
when(req.getContextPath()).thenReturn("/FhirStorm");
IncomingRequestAddressStrategy incomingRequestAddressStrategy = new IncomingRequestAddressStrategy();
String actual = incomingRequestAddressStrategy.determineServerBase(null,req);
assertEquals("http://fhirstorm.dyndns.org:8080/FhirStorm/fhir", actual);
}
}

View File

@ -40,7 +40,7 @@ import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -95,7 +95,7 @@ public class InterceptorTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();

View File

@ -28,7 +28,7 @@ import ca.uhn.fhir.rest.annotation.OptionalParam;
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.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -68,7 +68,7 @@ public class MethodPriorityTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
ServletHandler proxyHandler = new ServletHandler();

View File

@ -27,7 +27,7 @@ import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -167,7 +167,7 @@ public class PagingTest {
public static void beforeClass() throws Exception {
ourContext = new FhirContext();
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();

View File

@ -43,7 +43,7 @@ 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.annotation.Since;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
public class PlainProviderTest {
@ -56,7 +56,7 @@ public class PlainProviderTest {
@Before
public void before() throws Exception {
myPort = RandomServerPortProvider.findFreePort();
myPort = PortUtil.findFreePort();
myServer = new Server(myPort);
myCtx = new FhirContext(Patient.class);

View File

@ -26,7 +26,7 @@ import ca.uhn.fhir.model.dstu.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.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -116,7 +116,7 @@ public class ReadTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyProvider patientProvider = new DummyProvider();

View File

@ -37,7 +37,7 @@ import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -229,7 +229,7 @@ public class ReferenceParameterTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();

View File

@ -69,10 +69,11 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.param.CodingListParam;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.provider.ServerProfileProvider;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -1001,7 +1002,7 @@ public class ResfulServerMethodTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
ourCtx = new FhirContext(Patient.class);
@ -1303,6 +1304,10 @@ public class ResfulServerMethodTest {
@Read(version=true)
public Patient vread(@IdParam IdDt theId) {
Patient retVal = getIdToPatient().get(theId.getIdPart());
if (retVal == null) {
throw new ResourceNotFoundException("Couldn't find ID " + theId.getIdPart() + " - Valid IDs are: " + getIdToPatient().keySet());
}
List<HumanNameDt> name = retVal.getName();
HumanNameDt nameDt = name.get(0);
String value = theId.getVersionIdPart();

View File

@ -36,7 +36,7 @@ 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.server.provider.ServerProfileProvider;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -59,7 +59,7 @@ public class ResfulServerSelfReferenceTest {
@Test
public void testContextWithSpace() throws Exception {
int port = RandomServerPortProvider.findFreePort();
int port = PortUtil.findFreePort();
Server server = new Server(port);
RestfulServer restServer = new RestfulServer();
@ -102,7 +102,7 @@ public class ResfulServerSelfReferenceTest {
@Test
public void testSearchByParamIdentifier() throws Exception {
int port = RandomServerPortProvider.findFreePort();
int port = PortUtil.findFreePort();
Server hServer = new Server(port);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();

View File

@ -15,7 +15,7 @@ import ca.uhn.fhir.model.dstu.resource.Binary;
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.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
public class ResourceProviderWithNoMethodsTest {
@ -28,7 +28,7 @@ public class ResourceProviderWithNoMethodsTest {
@Test
public void testNoAnnotatedMethods() throws Exception {
int port = RandomServerPortProvider.findFreePort();
int port = PortUtil.findFreePort();
ourServer = new Server(port);
ResourceProvider patientProvider = new ResourceProvider();
@ -50,7 +50,7 @@ public class ResourceProviderWithNoMethodsTest {
@Test
public void testNonPublicMethod() throws Exception {
int port = RandomServerPortProvider.findFreePort();
int port = PortUtil.findFreePort();
ourServer = new Server(port);
NonPublicMethodProvider patientProvider = new NonPublicMethodProvider();

View File

@ -39,9 +39,10 @@ import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenOrListParam;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -105,6 +106,21 @@ public class SearchTest {
assertEquals("IDAAA (identifier123)", bundle.getEntries().get(0).getTitle().getValue());
}
@Test
public void testSearchWithOrList() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?findPatientWithOrList=aaa,bbb");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.getEntries().size());
Patient p = bundle.getResources(Patient.class).get(0);
assertEquals("aaa", p.getIdentifier().get(0).getValue().getValue());
assertEquals("bbb", p.getIdentifier().get(1).getValue().getValue());
}
@Test
public void testSearchByPost() throws Exception {
HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search");
@ -199,7 +215,7 @@ public class SearchTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
@ -295,6 +311,20 @@ public class SearchTest {
return retVal;
}
@Search()
public List<Patient> findPatientWithOrList(@RequiredParam(name = "findPatientWithOrList") StringOrListParam theParam) {
ArrayList<Patient> retVal = new ArrayList<Patient>();
Patient patient = new Patient();
patient.setId("1");
for (StringParam next : theParam.getValuesAsQueryTokens()) {
patient.addIdentifier("system", next.getValue());
}
retVal.add(patient);
return retVal;
}
@Search(queryName = "findWithLinks")
public List<Patient> findWithLinks() {
ArrayList<Patient> retVal = new ArrayList<Patient>();

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -51,7 +51,7 @@ public class ServerBaseTest {
@Test
public void testWithContextPath() throws Exception {
int port = RandomServerPortProvider.findFreePort();
int port = PortUtil.findFreePort();
myServer = new Server(port);
DummyProvider patientProvider = new DummyProvider();
@ -85,7 +85,7 @@ public class ServerBaseTest {
@Test
public void testWithContextAndServletPath() throws Exception {
int port = RandomServerPortProvider.findFreePort();
int port = PortUtil.findFreePort();
myServer = new Server(port);
DummyProvider patientProvider = new DummyProvider();
@ -118,7 +118,7 @@ public class ServerBaseTest {
@Test
public void testWithNoPath() throws Exception {
int port = RandomServerPortProvider.findFreePort();
int port = PortUtil.findFreePort();
myServer = new Server(port);
DummyProvider patientProvider = new DummyProvider();

View File

@ -27,7 +27,7 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.gclient.StringClientParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
public class ServerExtraParametersTest {
@ -37,7 +37,7 @@ public class ServerExtraParametersTest {
@Before
public void before() {
myPort = RandomServerPortProvider.findFreePort();
myPort = PortUtil.findFreePort();
myServer = new Server(myPort);
ServletHandler proxyHandler = new ServletHandler();

View File

@ -36,7 +36,7 @@ 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.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -251,7 +251,7 @@ public class ServerFeaturesTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();

View File

@ -28,7 +28,7 @@ import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -130,7 +130,7 @@ public class SortTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();

View File

@ -26,7 +26,7 @@ import ca.uhn.fhir.rest.annotation.OptionalParam;
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.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -148,7 +148,7 @@ public class StringParameterTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();

View File

@ -34,7 +34,7 @@ import ca.uhn.fhir.rest.annotation.GetTags;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.TagListParam;
import ca.uhn.fhir.rest.annotation.VersionIdParam;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -238,7 +238,7 @@ public class TagsServerTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
ourCtx = new FhirContext(Patient.class);

View File

@ -32,7 +32,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.annotation.Transaction;
import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -224,7 +224,7 @@ public class TransactionTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyProvider patientProvider = new DummyProvider();

View File

@ -32,7 +32,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.annotation.Transaction;
import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -173,7 +173,7 @@ public class TransactionWithBundleParamTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyProvider patientProvider = new DummyProvider();

View File

@ -29,13 +29,14 @@ import ca.uhn.fhir.model.dstu.resource.DiagnosticOrder;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
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.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -279,6 +280,21 @@ public class UpdateTest {
fail();
}
@Test
public void testUpdateWithNoReturn() throws Exception {
Organization patient = new Organization();
patient.addIdentifier().setValue("002");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Organization/001");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourClient.execute(httpPost);
assertEquals(200, response.getStatusLine().getStatusCode());
response.close();
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
@ -286,7 +302,7 @@ public class UpdateTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
PatientProvider patientProvider = new PatientProvider();
@ -295,7 +311,7 @@ public class UpdateTest {
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
servlet.setResourceProviders(patientProvider, ourReportProvider, new ObservationProvider());
servlet.setResourceProviders(patientProvider, ourReportProvider, new ObservationProvider(), new OrganizationResourceProvider());
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
@ -308,6 +324,23 @@ public class UpdateTest {
}
public static class OrganizationResourceProvider implements IResourceProvider
{
@Override
public Class<? extends IResource> getResourceType() {
return Organization.class;
}
@SuppressWarnings("unused")
@Update
public MethodOutcome update(@IdParam IdDt theId, @ResourceParam Organization theOrganization) {
return new MethodOutcome();
}
}
public static class DiagnosticReportProvider implements IResourceProvider {
private TagList myLastTags;

View File

@ -38,7 +38,7 @@ import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
@ -124,7 +124,7 @@ public class LoggingInterceptorTest {
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();

View File

@ -9,6 +9,7 @@ import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.AdministrativeGenderCodesEnum;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.util.FhirTerser;
@ -18,6 +19,7 @@ public class FhirTerserTest {
public void testGetAllPopulatedChildElementsOfType() {
Patient p = new Patient();
p.setGender(AdministrativeGenderCodesEnum.M);
p.addIdentifier().setSystem("urn:foo");
p.addAddress().addLine("Line1");
p.addAddress().addLine("Line2");

View File

@ -0,0 +1,24 @@
package ca.uhn.fhir.validation;
import static org.junit.Assert.*;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.FhirValidator;
public class ValidatorInstantiatorTest {
@Test
public void testValidator() {
FhirContext ctx = new FhirContext();
FhirValidator val = ctx.newValidator();
// We have a full classpath, so take advantage
assertTrue(val.isValidateAgainstStandardSchema());
assertTrue(val.isValidateAgainstStandardSchematron());
}
}

View File

@ -0,0 +1,294 @@
{
"resourceType": "Profile",
"text": {
"status": "generated",
"div": "<div><pre>\r\n&lt;<a title=\"Prospective warnings of potential issues when providing care to the patient.\" class=\"dict\" href=\"alert-definitions.html#Alert\"><b>Alert</b></a> xmlns=&quot;http://hl7.org/fhir&quot;&gt; <span style=\"float: right\"><a title=\"Documentation for this format\" href=\"formats.html\"><img alt=\"doco\" src=\"help.png\"/></a></span>\r\n &lt;!-- from <a href=\"resources.html\">Resource</a>: <a href=\"extensibility.html\">extension</a>, <a href=\"extensibility.html#modifierExtension\">modifierExtension</a>, language, <a href=\"narrative.html#Narrative\">text</a>, and <a href=\"references.html#contained\">contained</a> --&gt;\r\n &lt;<a title=\"Identifier assigned to the alert for external use (outside the FHIR environment).\" class=\"dict\" href=\"alert-definitions.html#Alert.identifier\"><b>identifier</b></a>&gt;<span style=\"color: Gray\">&lt;!--</span> <span style=\"color: brown\"><b>0..*</b></span> <span style=\"color: darkgreen\"><a href=\"datatypes.html#Identifier\">Identifier</a></span> <span style=\"color: navy\">Business identifier</span><span style=\"color: Gray\"> --&gt;</span>&lt;/identifier&gt;\r\n &lt;<a title=\"Allows an alert to be divided into different categories like clinical, administrative etc.\" class=\"dict\" href=\"alert-definitions.html#Alert.category\"><b>category</b></a>&gt;<span style=\"color: Gray\">&lt;!--</span> <span style=\"color: brown\"><b>0..1</b></span> <span style=\"color: darkgreen\"><a href=\"datatypes.html#CodeableConcept\">CodeableConcept</a></span> <span style=\"color: navy\">Clinical, administrative, etc.</span><span style=\"color: Gray\"> --&gt;</span>&lt;/category&gt;\r\n &lt;<a title=\"Supports basic workflow.\" class=\"dict\" href=\"alert-definitions.html#Alert.status\"><b>status</b></a> value=&quot;[<span style=\"color: darkgreen\"><a href=\"datatypes.html#code\">code</a></span>]&quot;/&gt;<span style=\"color: Gray\">&lt;!--</span> <span style=\"color: brown\"><b>1..1</b></span> <span style=\"color: navy\"><a style=\"color: navy\" href=\"alert-status.html\">active | inactive | entered in error</a></span><span style=\"color: Gray\"> --&gt;</span>\r\n &lt;<a title=\"The person who this alert concerns.\" class=\"dict\" href=\"alert-definitions.html#Alert.subject\"><b>subject</b></a>&gt;<span style=\"color: Gray\">&lt;!--</span> <span style=\"color: brown\"><b>1..1</b></span> <span style=\"color: darkgreen\"><a href=\"references.html#Resource\">Resource</a>(<a href=\"patient.html#Patient\">Patient</a>)</span> <span style=\"color: navy\">Who is alert about?</span><span style=\"color: Gray\"> --&gt;</span>&lt;/subject&gt;\r\n &lt;<a title=\"The person or device that created the alert.\" class=\"dict\" href=\"alert-definitions.html#Alert.author\"><b>author</b></a>&gt;<span style=\"color: Gray\">&lt;!--</span> <span style=\"color: brown\"><b>0..1</b></span> <span style=\"color: darkgreen\"><a href=\"references.html#Resource\">Resource</a>(<a href=\"practitioner.html#Practitioner\">Practitioner</a>|<a href=\"patient.html#Patient\">Patient</a>|<a href=\"device.html#Device\">Device</a>)</span> <span style=\"color: navy\">Alert creator</span><span style=\"color: Gray\"> --&gt;</span>&lt;/author&gt;\r\n &lt;<a title=\"The textual component of the alert to display to the user.\" class=\"dict\" href=\"alert-definitions.html#Alert.note\"><b>note</b></a> value=&quot;[<span style=\"color: darkgreen\"><a href=\"datatypes.html#string\">string</a></span>]&quot;/&gt;<span style=\"color: Gray\">&lt;!--</span> <span style=\"color: brown\"><b>1..1</b></span> <span style=\"color: navy\">Text of alert</span><span style=\"color: Gray\"> --&gt;</span>\r\n&lt;/Alert&gt;\r\n</pre></div>"
},
"name": "alert",
"publisher": "FHIR Project",
"description": "Basic Profile. Prospective warnings of potential issues when providing care to the patient.",
"status": "draft",
"date": "2014-05-09",
"requirements": "Scope and Usage The Alert resource provides a single interface for managing clinical and administrative facts that need to be brought to the attention of users of clinical systems. Examples can include many things. Patient's posing particular risks (falls, physical violence), patient's needing special accomodations (hard of hearing, use easy-open caps), administrative concerns (verify postal address, pre-payment required) or any other situation that needs to be brought to attention within the context of a particular workflow. (The workflow relevant to the issue can be identified by the category element.) \r\n\r\nUsually, resources specific to particular types of issues (health conditions, allergies, active medications will be used to communicate those issues. However, in some cases, particularly important information (a latex or severe food allergy) migt be highlighted as an Alert as well as the more typical resource.",
"mapping": [
{
"identity": "rim",
"uri": "http://hl7.org/v3",
"name": "RIM"
}
],
"structure": [
{
"type": "Alert",
"publish": true,
"element": [
{
"path": "Alert",
"definition": {
"short": "Key information to flag to healthcare providers",
"formal": "Prospective warnings of potential issues when providing care to the patient.",
"min": 1,
"max": "1",
"type": [
{
"code": "Resource"
}
],
"isModifier": false,
"mapping": [
{
"identity": "rim",
"map": "Observation[classCode=ISSUE, moodCode=EVN]"
}
]
}
},
{
"path": "Alert.extension",
"definition": {
"short": "Additional Content defined by implementations",
"formal": "May be used to represent additional information that is not part of the basic definition of the resource. In order to make the use of extensions safe and manageable, there is a strict governance applied to the definition and use of extensions. Though any implementer is allowed to define an extension, there is a set of requirements that SHALL be met as part of the definition of the extension.",
"comments": "there can be no stigma associated with the use of extensions by any application, project, or standard - regardless of the institution or jurisdiction that uses or defines the extensions. The use of extensions is what allows the FHIR specification to retain a core simplicity for everyone.",
"synonym": [
"extensions",
"user content"
],
"min": 0,
"max": "*",
"type": [
{
"code": "Extension"
}
],
"isModifier": false
}
},
{
"path": "Alert.modifierExtension",
"definition": {
"short": "Extensions that cannot be ignored",
"formal": "May be used to represent additional information that is not part of the basic definition of the resource, and that modifies the understanding of the element that contains it. Usually modifier elements provide negation or qualification. In order to make the use of extensions safe and manageable, there is a strict governance applied to the definition and use of extensions. Though any implementer is allowed to define an extension, there is a set of requirements that SHALL be met as part of the definition of the extension. Applications processing a resource are required to check for modifier extensions.",
"comments": "there can be no stigma associated with the use of extensions by any application, project, or standard - regardless of the institution or jurisdiction that uses or defines the extensions. The use of extensions is what allows the FHIR specification to retain a core simplicity for everyone.",
"synonym": [
"extensions",
"user content"
],
"min": 0,
"max": "*",
"type": [
{
"code": "Extension"
}
],
"isModifier": false
}
},
{
"path": "Alert.text",
"definition": {
"short": "Text summary of the resource, for human interpretation",
"formal": "A human-readable narrative that contains a summary of the resource, and may be used to represent the content of the resource to a human. The narrative need not encode all the structured data, but is required to contain sufficient detail to make it \"clinically safe\" for a human to just read the narrative. Resource definitions may define what content should be represented in the narrative to ensure clinical safety.",
"comments": "Contained resources do not have narrative. Resources that are not contained SHOULD have a narrative.",
"synonym": [
"narrative",
"html",
"xhtml",
"display"
],
"min": 0,
"max": "1",
"type": [
{
"code": "Narrative"
}
],
"isModifier": false
}
},
{
"path": "Alert.contained",
"definition": {
"short": "Contained, inline Resources",
"formal": "These resources do not have an independent existence apart from the resource that contains them - they cannot be identified independently, and nor can they have their own independent transaction scope.",
"comments": "This should never be done when the content can be identified properly, as once identification is lost, it is extremely difficult (and context dependent) to restore it again.",
"synonym": [
"inline resources",
"anonymous resources",
"contained resources"
],
"min": 0,
"max": "*",
"type": [
{
"code": "Resource"
}
],
"isModifier": false
}
},
{
"path": "Alert.identifier",
"definition": {
"short": "Business identifier",
"formal": "Identifier assigned to the alert for external use (outside the FHIR environment).",
"min": 0,
"max": "*",
"type": [
{
"code": "Identifier"
}
],
"isModifier": false,
"mapping": [
{
"identity": "rim",
"map": ".id"
}
]
}
},
{
"path": "Alert.category",
"definition": {
"short": "Clinical, administrative, etc.",
"formal": "Allows an alert to be divided into different categories like clinical, administrative etc.",
"min": 0,
"max": "1",
"type": [
{
"code": "CodeableConcept"
}
],
"isModifier": false,
"mapping": [
{
"identity": "rim",
"map": ".code"
}
]
}
},
{
"path": "Alert.status",
"definition": {
"short": "active | inactive | entered in error",
"formal": "Supports basic workflow.",
"min": 1,
"max": "1",
"type": [
{
"code": "code"
}
],
"isModifier": false,
"binding": {
"name": "AlertStatus",
"isExtensible": false,
"conformance": "required",
"referenceResource": {
"reference": "http://hl7.org/fhir/vs/alert-status"
}
},
"mapping": [
{
"identity": "rim",
"map": ".status"
}
]
}
},
{
"path": "Alert.subject",
"definition": {
"short": "Who is alert about?",
"formal": "The person who this alert concerns.",
"min": 1,
"max": "1",
"type": [
{
"code": "ResourceReference",
"profile": "http://hl7.org/fhir/profiles/Patient"
}
],
"isModifier": false,
"mapping": [
{
"identity": "rim",
"map": ".participation[typeCode=SBJ].role[classCode=PAT]"
}
]
}
},
{
"path": "Alert.author",
"definition": {
"short": "Alert creator",
"formal": "The person or device that created the alert.",
"min": 0,
"max": "1",
"type": [
{
"code": "ResourceReference",
"profile": "http://hl7.org/fhir/profiles/Practitioner"
},
{
"code": "ResourceReference",
"profile": "http://hl7.org/fhir/profiles/Patient"
},
{
"code": "ResourceReference",
"profile": "http://hl7.org/fhir/profiles/Device"
}
],
"isModifier": false,
"mapping": [
{
"identity": "rim",
"map": ".participation[typeCode=AUT].role"
}
]
}
},
{
"path": "Alert.note",
"definition": {
"short": "Text of alert",
"formal": "The textual component of the alert to display to the user.",
"min": 1,
"max": "1",
"type": [
{
"code": "string"
}
],
"isModifier": false,
"mapping": [
{
"identity": "rim",
"map": ".value"
}
]
}
}
],
"searchParam": [
{
"name": "_id",
"type": "token",
"documentation": "The logical resource id associated with the resource (must be supported by all servers)"
},
{
"name": "_language",
"type": "token",
"documentation": "The language of the resource"
},
{
"name": "subject",
"type": "reference",
"documentation": "The identity of a subject to list alerts for",
"xpath": "f:Alert/f:subject"
}
]
}
]
}

View File

@ -10,6 +10,9 @@
<logger name="org.eclipse" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.apache" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<!--
<logger name="ca.uhn.fhir.rest.client" additivity="false" level="trace">

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry combineaccessrules="false" kind="src" path="/hapi-fhir-base"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>hapi-fhir-mitreid-integration</name>
<name>hapi-fhir-base-examples</name>
<comment></comment>
<projects>
</projects>

View File

@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8

View File

@ -0,0 +1,289 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.6
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
org.eclipse.jdt.core.formatter.comment.format_block_comments=true
org.eclipse.jdt.core.formatter.comment.format_header=false
org.eclipse.jdt.core.formatter.comment.format_html=true
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
org.eclipse.jdt.core.formatter.comment.format_line_comments=true
org.eclipse.jdt.core.formatter.comment.format_source_code=true
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
org.eclipse.jdt.core.formatter.comment.line_length=80
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
org.eclipse.jdt.core.formatter.compact_else_if=true
org.eclipse.jdt.core.formatter.continuation_indentation=2
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_empty_lines=false
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
org.eclipse.jdt.core.formatter.indentation.size=3
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
org.eclipse.jdt.core.formatter.lineSplit=160
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
org.eclipse.jdt.core.formatter.tabulation.char=space
org.eclipse.jdt.core.formatter.tabulation.size=3
org.eclipse.jdt.core.formatter.use_on_off_tags=false
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=true
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true

View File

@ -0,0 +1,3 @@
eclipse.preferences.version=1
formatter_profile=_examples-format
formatter_settings_version=12

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