From ea7377043312d6df063bd247a634f8a4f36922eb Mon Sep 17 00:00:00 2001
From: jamesagnew The server supports all operations on the Binary resource, including history
+ * Important usage notes:
+ *
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ *
+ *
+ * Performance Note: This class is cheap to create, and may be called once for + * every message being processed without incurring any performance penalty + *
+ */ public IParser newJsonParser() { return new JsonParser(this); } /** - * Instantiates a new client instance + * Instantiates a new client instance. + * + *+ * Performance Note: This class is cheap to create, and may be called once for + * every message being processed without incurring any performance penalty + *
* * @param theClientType * The client type, which is an interface type to be instantiated @@ -119,14 +173,22 @@ public class FhirContext { return getRestfulClientFactory().newClient(theClientType, theServerBase); } + /** + * Create and return a new JSON parser. + * + *+ * Performance Note: This class is cheap to create, and may be called once for + * every message being processed without incurring any performance penalty + *
+ */ public IParser newXmlParser() { return new XmlParser(this); } - + public void setNarrativeGenerator(INarrativeGenerator theNarrativeGenerator) { myNarrativeGenerator = theNarrativeGenerator; } - + private RuntimeResourceDefinition scanResourceType(Class extends IResource> theResourceType) { ArrayListnull
, which is the default, a new HTTP client with
+ * this factory. If set to null
, a new HTTP client with
* default settings will be created.
*
* @param theHttpClient An HTTP client instance to use, or null
*/
- public void setHttpClient(HttpClient theHttpClient);
+ void setHttpClient(HttpClient theHttpClient);
+
+ /**
+ * Returns the Apache HTTP client instance. This method will not return null.
+ *
+ * @see #setHttpClient(HttpClient)
+ */
+ HttpClient getHttpClient();
}
\ No newline at end of file
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java
index 63b9512608f..f1d9af2286b 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java
@@ -3,6 +3,8 @@ package ca.uhn.fhir.rest.client;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.http.client.HttpClient;
@@ -20,6 +22,7 @@ public class RestfulClientFactory implements IRestfulClientFactory {
private FhirContext myContext;
private HttpClient myHttpClient;
+ private Mapnull
, which is
- * the default, a new HTTP client with default settings will be created.
+ * Sets the Apache HTTP client instance to be used by any new restful clients created by this factory. If set to null
, which is the default, a new HTTP client with default settings
+ * will be created.
*
* @param theHttpClient
* An HTTP client instance to use, or null
*/
@Override
- public void setHttpClient(HttpClient theHttpClient) {
+ public synchronized void setHttpClient(HttpClient theHttpClient) {
myHttpClient = theHttpClient;
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java
index 18c8bb61135..271e8913e96 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java
@@ -201,4 +201,8 @@ public abstract class BaseMethodBinding {
public abstract Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Mapnull
(which is the default) to disable narrative generation.
- *
- * @throws IllegalStateException
- * Note that this method can only be called prior to {@link #init() initialization} and will throw an {@link IllegalStateException} if called after that.
- */
- public void setNarrativeGenerator(INarrativeGenerator theNarrativeGenerator) {
- if (myFhirContext != null) {
- throw new IllegalStateException("Server has already been initialized, can not change this property");
- }
- myNarrativeGenerator = theNarrativeGenerator;
- }
-
- /**
- * If set to true
(default is false), the server will use browser friendly content-types (instead of standard FHIR ones) when it detects that the request is coming from a browser
- * instead of a FHIR
- */
- public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
- myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
- }
-
-
- private void findResourceMethods(IResourceProvider theProvider) throws Exception {
-
- Class extends IResource> resourceType = theProvider.getResourceType();
- RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(resourceType);
-
- ResourceBinding r = new ResourceBinding();
- r.setResourceProvider(theProvider);
- r.setResourceName(definition.getName());
- resources.put(definition.getName(), r);
-
- ourLog.info("Scanning type for RESTful methods: {}", theProvider.getClass());
-
- Class> clazz = theProvider.getClass();
- for (Method m : clazz.getDeclaredMethods()) {
- if (Modifier.isPublic(m.getModifiers())) {
- ourLog.debug("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
-
- BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(theProvider.getResourceType(), m, myFhirContext);
- if (foundMethodBinding != null) {
- r.addMethod(foundMethodBinding);
- ourLog.info(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
- } else {
- ourLog.debug(" * Method: {}#{} is not a handler", theProvider.getClass(), m.getName());
- }
- }
- }
+ public void addHapiHeader(HttpServletResponse theHttpResponse) {
+ theHttpResponse.addHeader("X-CatchingFhir", "HAPI FHIR " + VersionUtil.getVersion());
}
@Override
@@ -203,7 +83,97 @@ public abstract class RestfulServer extends HttpServlet {
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handleRequest(SearchMethodBinding.RequestType.PUT, request, response);
}
-
+
+ private void findResourceMethods(IResourceProvider theProvider) throws Exception {
+
+ Class extends IResource> resourceType = theProvider.getResourceType();
+ RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(resourceType);
+
+ ResourceBinding r = new ResourceBinding();
+ r.setResourceProvider(theProvider);
+ r.setResourceName(definition.getName());
+ myResourceNameToProvider.put(definition.getName(), r);
+
+ ourLog.info("Scanning type for RESTful methods: {}", theProvider.getClass());
+
+ Class> clazz = theProvider.getClass();
+ for (Method m : clazz.getDeclaredMethods()) {
+ if (Modifier.isPublic(m.getModifiers())) {
+ ourLog.debug("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
+
+ BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(theProvider.getResourceType(), m, myFhirContext);
+ if (foundMethodBinding != null) {
+ r.addMethod(foundMethodBinding);
+ ourLog.info(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
+ } else {
+ ourLog.debug(" * Method: {}#{} is not a handler", theProvider.getClass(), m.getName());
+ }
+ }
+ }
+ }
+
+ private void findSystemMethods(Object theSystemProvider) {
+ Class> clazz = theSystemProvider.getClass();
+ for (Method m : clazz.getDeclaredMethods()) {
+ if (Modifier.isPublic(m.getModifiers())) {
+ ourLog.debug("Scanning public method: {}#{}", theSystemProvider.getClass(), m.getName());
+
+ BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindSystemMethod(m, myFhirContext);
+ if (foundMethodBinding != null) {
+ if (foundMethodBinding instanceof ConformanceMethodBinding) {
+ myServerConformanceMethod = foundMethodBinding;
+ }
+ ourLog.info(" * Method: {}#{} is a handler", theSystemProvider.getClass(), m.getName());
+ } else {
+ ourLog.debug(" * Method: {}#{} is not a handler", theSystemProvider.getClass(), m.getName());
+ }
+ }
+ }
+
+ }
+
+ public FhirContext getFhirContext() {
+ return myFhirContext;
+ }
+
+ public INarrativeGenerator getNarrativeGenerator() {
+ return myNarrativeGenerator;
+ }
+
+ public Collection
+ * By default, the {@link ServerConformanceProvider} is used, but
+ * this can be changed, or set to null
if you do not wish
+ * to export a conformance statement.
+ *
null
(which is the default) to disable narrative generation.
+ * Note that this method can only be called before the server is initialized.
+ *
+ * @throws IllegalStateException
+ * Note that this method can only be called prior to {@link #init() initialization} and will throw an {@link IllegalStateException} if called after that.
+ */
+ public void setNarrativeGenerator(INarrativeGenerator theNarrativeGenerator) {
+ if (myFhirContext != null) {
+ throw new IllegalStateException("Server has already been initialized, can not change this property");
+ }
+ myNarrativeGenerator = theNarrativeGenerator;
+ }
+
+ /**
+ * Returns the server conformance provider, which is the provider that
+ * is used to generate the server's conformance (metadata) statement.
+ *
+ * By default, the {@link ServerConformanceProvider} is used, but
+ * this can be changed, or set to null
if you do not wish
+ * to export a conformance statement.
+ *
true
(default is false), the server will use browser friendly content-types (instead of standard FHIR ones) when it detects that the request is coming from a browser
+ * instead of a FHIR
+ */
+ public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
+ myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
+ }
+
public enum NarrativeModeEnum {
NORMAL,
ONLY,
@@ -361,8 +428,4 @@ public abstract class RestfulServer extends HttpServlet {
}
}
- public void addHapiHeader(HttpServletResponse theHttpResponse) {
- theHttpResponse.addHeader("X-CatchingFhir", "HAPI FHIR " + VersionUtil.getVersion());
- }
-
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java
index dcba25ed240..34aa03135be 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java
@@ -24,13 +24,12 @@ import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.IQueryParameter;
-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.util.ExtensionConstants;
import ca.uhn.fhir.util.VersionUtil;
-public class ServerConformanceProvider implements IResourceProvider {
+public class ServerConformanceProvider {
private volatile Conformance myConformance;
private final RestfulServer myRestfulServer;
@@ -142,9 +141,4 @@ public class ServerConformanceProvider implements IResourceProvider {
return retVal;
}
- @Override
- public Class extends IResource> getResourceType() {
- return Conformance.class;
- }
-
}
diff --git a/hapi-fhir-base/src/site/example/java/example/QuickUsage.java b/hapi-fhir-base/src/site/example/java/example/QuickUsage.java
index b8f8f64b8a2..de9a4cbe3af 100644
--- a/hapi-fhir-base/src/site/example/java/example/QuickUsage.java
+++ b/hapi-fhir-base/src/site/example/java/example/QuickUsage.java
@@ -1,5 +1,8 @@
package example;
+import static ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum.OFFICIAL;
+import static ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum.SECONDARY;
+
import java.io.IOException;
import java.util.List;
@@ -7,7 +10,6 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.AdministrativeGenderCodesEnum;
-import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.RequiredParam;
@@ -15,10 +17,10 @@ import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
-import static ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum.*;
public class QuickUsage {
+@SuppressWarnings("unused")
public static void main(String[] args) throws DataFormatException, IOException {
Patient patient = new Patient();
diff --git a/hapi-fhir-base/src/site/example/java/example/RestfulPatientResourceProviderMore.java b/hapi-fhir-base/src/site/example/java/example/RestfulPatientResourceProviderMore.java
index 282a1a68106..2336de75ca7 100644
--- a/hapi-fhir-base/src/site/example/java/example/RestfulPatientResourceProviderMore.java
+++ b/hapi-fhir-base/src/site/example/java/example/RestfulPatientResourceProviderMore.java
@@ -1,15 +1,18 @@
package example;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
+import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.PathSpecification;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
+import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Organization;
@@ -17,26 +20,32 @@ import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
+import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.IncludeParam;
+import ca.uhn.fhir.rest.annotation.Metadata;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.Read;
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.Update;
import ca.uhn.fhir.rest.annotation.VersionIdParam;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.ITestClient;
+import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.param.CodingListParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QualifiedDateParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
+import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
@SuppressWarnings("unused")
public abstract class RestfulPatientResourceProviderMore implements IResourceProvider {
+private boolean detectedVersionConflict;
//START SNIPPET: searchAll
@Search
public List+ The following table lists the operations supported by + HAPI FHIR RESTful Servers and Clients. +
+ +Operation | +Definition | +
+ Instance - Read + | ++ Read the current state of the resource + | +
+ Instance - VRead + | ++ Read the state of a specific version of the resource + | +
+ Instance - Update + | ++ Read the state of a specific version of the resource + | +
+ Instance - Delete + | ++ Delete a resource + | +
+ Instance - History + | ++ Retrieve the update history for a particular resource + | +
+ Type - Create + | ++ Create a new resource with a server assigned id + | +
+ Type - Search
+ |
+ + Search the resource type based on some filter criteria + | +
+ Type - Search + | ++ Search the resource type based on some filter criteria + | +
+ Type - History + | ++ Retrieve the update history for a particular resource type + | +
+ Type - Validate + | ++ Check that the content would be acceptable as an update + | +
+ System - Conformance + | ++ Get a conformance statement for the system + | +
+ System - Transaction + | ++ Update, create or delete a set of resources as a single transaction + | +
+ System - History + | ++ Retrieve the update history for all resources + | +
+ System - Search + | ++ Search across all resource types based on some filter criteria + | +
The @@ -53,7 +188,12 @@
The @@ -77,7 +217,158 @@
+ The + update + operation updates a specific resource instance (using its ID), and optionally + accepts a version ID as well (which can be used to detect version conflicts). +
++ Update methods must be annotated with the + @Update + annotation, and have a parameter annotated with the + @Resource + annotation. This parameter contains the resource instance to be created. +
++ In addition, the method must have a parameter annotated with the + @IdParam + annotation, and optionally may have a parameter annotated with the + @VersionIdParam +
++ Update methods must return an object of type + MethodOutcome. This + object contains the identity of the created resource. +
++ The following snippet shows how to define an update method on a server: +
+ +
+ Example URL to invoke this method (this would be invoked using an HTTP PUT,
+ with the resource in the PUT body):
+ http://fhir.example.com/Patient
+
+ The following snippet shows how the corresponding client interface + would look: +
+ ++ In the case of a server which is able to do version conflict checking, an + extra parameter would be added: +
+ ++ Not yet implemented +
+ ++ Not yet implemented +
+ ++ The + create + operation saves a new resource to the server, allowing the server to + give that resource an ID and version ID. +
++ Create methods must be annotated with the + @Create + annotation, and have a single parameter annotated with the + @Resource + annotation. This parameter contains the resource instance to be created. +
++ Create methods must return an object of type + MethodOutcome. This + object contains the identity of the created resource. +
++ The following snippet shows how to define a server create method: +
+ +
+ Example URL to invoke this method (this would be invoked using an HTTP POST,
+ with the resource in the POST body):
+ http://fhir.example.com/Patient
+
+ The following snippet shows how the corresponding client interface + would look: +
+ +The @@ -152,7 +443,7 @@
-Search methods may take multiple parameters, and these parameters @@ -375,50 +666,128 @@
- The - create - operation saves a new resource to the server, allowing the server to - give that resource an ID and version ID. + Not yet implemented
+ +- Create methods must be annotated with the - @Create - annotation, and have a single parameter annotated with the - @Resource - annotation. This parameter contains the resource instance to be created. + Not yet implemented +
+ ++ FHIR defines that a FHIR Server must be able to export a conformance statement, + which is an instance of the + Conformance + resource describing the server itself.
- The following snippet shows how to define a server create method: + The HAPI FHIR RESTful server will automatically export such + a conformance statement. See the + RESTful Server + documentation for more information.
++ If you wish to override this default behaviour by creating + your own metadata provider, you simply need to define a class + with a method annotated using the + @Metadata + annotation. +
+ To create a Client which can retrieve a Server's conformance + statement is simple. First, define your Client Interface, using + the @Metadata annotation: +
+
- Example URL to invoke this method (this would be invoked using an HTTP POST,
- with the resource in the POST body):
- http://fhir.example.com/Patient
+ Then use the standard
+ RESTful Client mechanism for instantiating
+ a client:
- The following snippet shows how the corresponding client interface - would look: -
-+ Not yet implemented +
+ ++ Not yet implemented +
+ ++ Not yet implemented +
+ +