Merge remote-tracking branch 'remotes/origin/master' into ks-20191119-scheduler

This commit is contained in:
Ken Stevens 2019-12-11 14:01:01 -05:00
commit f5c4c8e3e3
45 changed files with 1074 additions and 375 deletions

View File

@ -46,7 +46,7 @@ import java.util.*;
public enum Pointcut { public enum Pointcut {
/** /**
* <b>Registry Hook: </b> * <b>Interceptor Framework Hook:</b>
* This pointcut will be called once when a given interceptor is registered * This pointcut will be called once when a given interceptor is registered
*/ */
INTERCEPTOR_REGISTERED(void.class), INTERCEPTOR_REGISTERED(void.class),
@ -89,7 +89,7 @@ public enum Pointcut {
), ),
/** /**
* <b>Server Hook: </b> * <b>Server Hook:</b>
* This hook is called before any other processing takes place for each incoming request. It may be used to provide * This hook is called before any other processing takes place for each incoming request. It may be used to provide
* alternate handling for some requests, or to screen requests before they are handled, etc. * alternate handling for some requests, or to screen requests before they are handled, etc.
* <p> * <p>
@ -117,10 +117,10 @@ public enum Pointcut {
), ),
/** /**
* <b>Server Hook: </b> * <b>Server Hook:</b>
* This hook is invoked upon any exception being thrown within the server's request processing code. This includes * This hook is invoked upon any exception being thrown within the server's request processing code. This includes
* any exceptions thrown within resource provider methods (e.g. {@link Search} and {@link Read} methods) as well as * any exceptions thrown within resource provider methods (e.g. {@link Search} and {@link Read} methods) as well as
* any runtime exceptions thrown by the server itself. This also includes any {@link AuthenticationException}s * any runtime exceptions thrown by the server itself. This also includes any {@link AuthenticationException}
* thrown. * thrown.
* <p> * <p>
* Hooks may accept the following parameters: * Hooks may accept the following parameters:
@ -543,6 +543,7 @@ public enum Pointcut {
), ),
/** /**
* <b>Subscription Hook:</b>
* Invoked whenever a persisted resource has been modified and is being submitted to the * Invoked whenever a persisted resource has been modified and is being submitted to the
* subscription processing pipeline. This method is called before the resource is placed * subscription processing pipeline. This method is called before the resource is placed
* on any queues for processing and executes synchronously during the resource modification * on any queues for processing and executes synchronously during the resource modification
@ -563,6 +564,7 @@ public enum Pointcut {
/** /**
* <b>Subscription Hook:</b>
* Invoked any time that a resource is matched by an individual subscription, and * Invoked any time that a resource is matched by an individual subscription, and
* is about to be queued for delivery. * is about to be queued for delivery.
* <p> * <p>
@ -585,6 +587,7 @@ public enum Pointcut {
SUBSCRIPTION_RESOURCE_MATCHED(boolean.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage", "ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult"), SUBSCRIPTION_RESOURCE_MATCHED(boolean.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage", "ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult"),
/** /**
* <b>Subscription Hook:</b>
* Invoked whenever a persisted resource was checked against all active subscriptions, and did not * Invoked whenever a persisted resource was checked against all active subscriptions, and did not
* match any. * match any.
* <p> * <p>
@ -600,6 +603,7 @@ public enum Pointcut {
SUBSCRIPTION_RESOURCE_DID_NOT_MATCH_ANY_SUBSCRIPTIONS(void.class, "ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage"), SUBSCRIPTION_RESOURCE_DID_NOT_MATCH_ANY_SUBSCRIPTIONS(void.class, "ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage"),
/** /**
* <b>Subscription Hook:</b>
* Invoked immediately before the delivery of a subscription, and right before any channel-specific * Invoked immediately before the delivery of a subscription, and right before any channel-specific
* hooks are invoked (e.g. {@link #SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY}. * hooks are invoked (e.g. {@link #SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY}.
* <p> * <p>
@ -621,6 +625,7 @@ public enum Pointcut {
SUBSCRIPTION_BEFORE_DELIVERY(boolean.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"), SUBSCRIPTION_BEFORE_DELIVERY(boolean.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
/** /**
* <b>Subscription Hook:</b>
* Invoked immediately after the delivery of a subscription, and right before any channel-specific * Invoked immediately after the delivery of a subscription, and right before any channel-specific
* hooks are invoked (e.g. {@link #SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY}. * hooks are invoked (e.g. {@link #SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY}.
* <p> * <p>
@ -638,6 +643,7 @@ public enum Pointcut {
/** /**
* <b>Subscription Hook:</b>
* Invoked immediately after the attempted delivery of a subscription, if the delivery * Invoked immediately after the attempted delivery of a subscription, if the delivery
* failed. * failed.
* <p> * <p>
@ -660,6 +666,7 @@ public enum Pointcut {
SUBSCRIPTION_AFTER_DELIVERY_FAILED(boolean.class, "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage", "java.lang.Exception"), SUBSCRIPTION_AFTER_DELIVERY_FAILED(boolean.class, "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage", "java.lang.Exception"),
/** /**
* <b>Subscription Hook:</b>
* Invoked immediately after the delivery of a REST HOOK subscription. * Invoked immediately after the delivery of a REST HOOK subscription.
* <p> * <p>
* When this hook is called, all processing is complete so this hook should not * When this hook is called, all processing is complete so this hook should not
@ -677,6 +684,7 @@ public enum Pointcut {
SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY(void.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"), SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY(void.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
/** /**
* <b>Subscription Hook:</b>
* Invoked immediately before the delivery of a REST HOOK subscription. * Invoked immediately before the delivery of a REST HOOK subscription.
* <p> * <p>
* Hooks may make changes to the delivery payload, or make changes to the * Hooks may make changes to the delivery payload, or make changes to the
@ -697,6 +705,7 @@ public enum Pointcut {
SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY(boolean.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"), SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY(boolean.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
/** /**
* <b>Subscription Hook:</b>
* Invoked whenever a persisted resource (a resource that has just been stored in the * Invoked whenever a persisted resource (a resource that has just been stored in the
* database via a create/update/patch/etc.) is about to be checked for whether any subscriptions * database via a create/update/patch/etc.) is about to be checked for whether any subscriptions
* were triggered as a result of the operation. * were triggered as a result of the operation.
@ -716,6 +725,7 @@ public enum Pointcut {
/** /**
* <b>Subscription Hook:</b>
* Invoked whenever a persisted resource (a resource that has just been stored in the * Invoked whenever a persisted resource (a resource that has just been stored in the
* database via a create/update/patch/etc.) has been checked for whether any subscriptions * database via a create/update/patch/etc.) has been checked for whether any subscriptions
* were triggered as a result of the operation. * were triggered as a result of the operation.
@ -733,6 +743,7 @@ public enum Pointcut {
/** /**
* <b>Subscription Hook:</b>
* Invoked immediately after an active subscription is "registered". In HAPI FHIR, when * Invoked immediately after an active subscription is "registered". In HAPI FHIR, when
* a subscription * a subscription
* <p> * <p>
@ -752,6 +763,7 @@ public enum Pointcut {
/** /**
* <b>Storage Hook:</b>
* Invoked when a resource is being deleted in a cascaded delete. This means that * Invoked when a resource is being deleted in a cascaded delete. This means that
* some other resource is being deleted, but per use request or other * some other resource is being deleted, but per use request or other
* policy, the given resource (the one supplied as a parameter to this hook) * policy, the given resource (the one supplied as a parameter to this hook)
@ -797,6 +809,7 @@ public enum Pointcut {
), ),
/** /**
* <b>Storage Hook:</b>
* Invoked when one or more resources may be returned to the user, whether as a part of a READ, * Invoked when one or more resources may be returned to the user, whether as a part of a READ,
* a SEARCH, or even as the response to a CREATE/UPDATE, etc. * a SEARCH, or even as the response to a CREATE/UPDATE, etc.
* <p> * <p>
@ -843,6 +856,7 @@ public enum Pointcut {
), ),
/** /**
* <b>Storage Hook:</b>
* Invoked when the storage engine is about to check for the existence of a pre-cached search * Invoked when the storage engine is about to check for the existence of a pre-cached search
* whose results match the given search parameters. * whose results match the given search parameters.
* <p> * <p>
@ -880,6 +894,7 @@ public enum Pointcut {
), ),
/** /**
* <b>Storage Hook:</b>
* Invoked when a search is starting, prior to creating a record for the search. * Invoked when a search is starting, prior to creating a record for the search.
* <p> * <p>
* Hooks may accept the following parameters: * Hooks may accept the following parameters:
@ -915,6 +930,7 @@ public enum Pointcut {
), ),
/** /**
* <b>Storage Hook:</b>
* Invoked when one or more resources may be returned to the user, whether as a part of a READ, * Invoked when one or more resources may be returned to the user, whether as a part of a READ,
* a SEARCH, or even as the response to a CREATE/UPDATE, etc. * a SEARCH, or even as the response to a CREATE/UPDATE, etc.
* <p> * <p>
@ -963,6 +979,7 @@ public enum Pointcut {
), ),
/** /**
* <b>Storage Hook:</b>
* Invoked before a resource will be created, immediately before the resource * Invoked before a resource will be created, immediately before the resource
* is persisted to the database. * is persisted to the database.
* <p> * <p>
@ -998,6 +1015,7 @@ public enum Pointcut {
), ),
/** /**
* <b>Storage Hook:</b>
* Invoked before a resource will be updated, immediately before the resource * Invoked before a resource will be updated, immediately before the resource
* is persisted to the database. * is persisted to the database.
* <p> * <p>
@ -1036,6 +1054,7 @@ public enum Pointcut {
), ),
/** /**
* <b>Storage Hook:</b>
* Invoked before a resource will be created, immediately before the resource * Invoked before a resource will be created, immediately before the resource
* is persisted to the database. * is persisted to the database.
* <p> * <p>
@ -1072,6 +1091,7 @@ public enum Pointcut {
/** /**
* <b>Storage Hook:</b>
* Invoked before a resource will be created, immediately before the transaction * Invoked before a resource will be created, immediately before the transaction
* is committed (after all validation and other business rules have successfully * is committed (after all validation and other business rules have successfully
* completed, and any other database activity is complete. * completed, and any other database activity is complete.
@ -1109,6 +1129,7 @@ public enum Pointcut {
), ),
/** /**
* <b>Storage Hook:</b>
* Invoked before a resource will be updated, immediately before the transaction * Invoked before a resource will be updated, immediately before the transaction
* is committed (after all validation and other business rules have successfully * is committed (after all validation and other business rules have successfully
* completed, and any other database activity is complete. * completed, and any other database activity is complete.
@ -1149,6 +1170,7 @@ public enum Pointcut {
/** /**
* <b>Storage Hook:</b>
* Invoked before a resource will be created * Invoked before a resource will be created
* <p> * <p>
* Hooks will have access to the contents of the resource being deleted * Hooks will have access to the contents of the resource being deleted
@ -1182,6 +1204,7 @@ public enum Pointcut {
), ),
/** /**
* <b>Storage Hook:</b>
* Invoked when a resource delete operation is about to fail due to referential integrity conflicts. * Invoked when a resource delete operation is about to fail due to referential integrity conflicts.
* <p> * <p>
* Hooks will have access to the list of resources that have references to the resource being deleted. * Hooks will have access to the list of resources that have references to the resource being deleted.
@ -1219,6 +1242,7 @@ public enum Pointcut {
), ),
/** /**
* <b>Storage Hook:</b>
* Invoked before a resource is about to be expunged via the <code>$expunge</code> operation. * Invoked before a resource is about to be expunged via the <code>$expunge</code> operation.
* <p> * <p>
* Hooks will be passed a reference to a counter containing the current number of records that have been deleted. * Hooks will be passed a reference to a counter containing the current number of records that have been deleted.
@ -1261,7 +1285,8 @@ public enum Pointcut {
), ),
/** /**
* Invoked before expungeEverything is called. * <b>Storage Hook:</b>
* Invoked before an <code>$expunge</code> operation on all data (expungeEverything) is called.
* <p> * <p>
* Hooks will be passed a reference to a counter containing the current number of records that have been deleted. * Hooks will be passed a reference to a counter containing the current number of records that have been deleted.
* If the hook deletes any records, the hook is expected to increment this counter by the number of records deleted. * If the hook deletes any records, the hook is expected to increment this counter by the number of records deleted.
@ -1297,12 +1322,13 @@ public enum Pointcut {
), ),
/** /**
* Note that this is a performance tracing hook. Use with caution in production * <b>Performance Tracing Hook:</b>
* systems, since calling it may (or may not) carry a cost.
* <p>
* This hook is invoked when any informational messages generated by the * This hook is invoked when any informational messages generated by the
* SearchCoordinator are created. It is typically used to provide logging * SearchCoordinator are created. It is typically used to provide logging
* or capture details related to a specific request. * or capture details related to a specific request.
* <p>
* Note that this is a performance tracing hook. Use with caution in production
* systems, since calling it may (or may not) carry a cost.
* </p> * </p>
* Hooks may accept the following parameters: * Hooks may accept the following parameters:
* <ul> * <ul>
@ -1334,12 +1360,13 @@ public enum Pointcut {
), ),
/** /**
* Note that this is a performance tracing hook. Use with caution in production * <b>Performance Tracing Hook:</b>
* systems, since calling it may (or may not) carry a cost.
* <p>
* This hook is invoked when any warning messages generated by the * This hook is invoked when any warning messages generated by the
* SearchCoordinator are created. It is typically used to provide logging * SearchCoordinator are created. It is typically used to provide logging
* or capture details related to a specific request. * or capture details related to a specific request.
* <p>
* Note that this is a performance tracing hook. Use with caution in production
* systems, since calling it may (or may not) carry a cost.
* </p> * </p>
* Hooks may accept the following parameters: * Hooks may accept the following parameters:
* <ul> * <ul>
@ -1371,12 +1398,13 @@ public enum Pointcut {
), ),
/** /**
* Note that this is a performance tracing hook. Use with caution in production * <b>Performance Tracing Hook:</b>
* systems, since calling it may (or may not) carry a cost.
* <p>
* This hook is invoked when a search has returned the very first result * This hook is invoked when a search has returned the very first result
* from the database. The timing on this call can be a good indicator of how * from the database. The timing on this call can be a good indicator of how
* performant a query is in general. * performant a query is in general.
* <p>
* Note that this is a performance tracing hook. Use with caution in production
* systems, since calling it may (or may not) carry a cost.
* </p> * </p>
* Hooks may accept the following parameters: * Hooks may accept the following parameters:
* <ul> * <ul>
@ -1409,14 +1437,15 @@ public enum Pointcut {
), ),
/** /**
* Note that this is a performance tracing hook. Use with caution in production * <b>Performance Tracing Hook:</b>
* systems, since calling it may (or may not) carry a cost.
* <p>
* This hook is invoked when an individual search query SQL SELECT statement * This hook is invoked when an individual search query SQL SELECT statement
* has completed and no more results are available from that query. Note that this * has completed and no more results are available from that query. Note that this
* doesn't necessarily mean that no more matching results exist in the database, * doesn't necessarily mean that no more matching results exist in the database,
* since HAPI FHIR JPA batch loads results in to the query cache in chunks in order * since HAPI FHIR JPA batch loads results in to the query cache in chunks in order
* to provide predicable results without overloading memory or the database. * to provide predicable results without overloading memory or the database.
* <p>
* Note that this is a performance tracing hook. Use with caution in production
* systems, since calling it may (or may not) carry a cost.
* </p> * </p>
* Hooks may accept the following parameters: * Hooks may accept the following parameters:
* <ul> * <ul>
@ -1450,11 +1479,12 @@ public enum Pointcut {
/** /**
* Note that this is a performance tracing hook. Use with caution in production * <b>Performance Tracing Hook:</b>
* systems, since calling it may (or may not) carry a cost.
* <p>
* This hook is invoked when a search has failed for any reason. When this pointcut * This hook is invoked when a search has failed for any reason. When this pointcut
* is invoked, the search has completed unsuccessfully and will not be continued. * is invoked, the search has completed unsuccessfully and will not be continued.
* <p>
* Note that this is a performance tracing hook. Use with caution in production
* systems, since calling it may (or may not) carry a cost.
* </p> * </p>
* Hooks may accept the following parameters: * Hooks may accept the following parameters:
* <ul> * <ul>
@ -1487,13 +1517,14 @@ public enum Pointcut {
), ),
/** /**
* Note that this is a performance tracing hook. Use with caution in production * <b>Performance Tracing Hook:</b>
* systems, since calling it may (or may not) carry a cost.
* <p>
* This hook is invoked when a search has failed for any reason. When this pointcut * This hook is invoked when a search has failed for any reason. When this pointcut
* is invoked, a pass in the Search Coordinator has completed successfully, but * is invoked, a pass in the Search Coordinator has completed successfully, but
* not all possible resources have been loaded yet so a future paging request * not all possible resources have been loaded yet so a future paging request
* may trigger a new task that will load further resources. * may trigger a new task that will load further resources.
* <p>
* Note that this is a performance tracing hook. Use with caution in production
* systems, since calling it may (or may not) carry a cost.
* </p> * </p>
* Hooks may accept the following parameters: * Hooks may accept the following parameters:
* <ul> * <ul>
@ -1526,9 +1557,14 @@ public enum Pointcut {
), ),
/** /**
* <b>Performance Tracing Hook:</b>
* Invoked when the storage engine is about to reuse the results of * Invoked when the storage engine is about to reuse the results of
* a previously cached search. * a previously cached search.
* <p> * <p>
* Note that this is a performance tracing hook. Use with caution in production
* systems, since calling it may (or may not) carry a cost.
* </p>
* <p>
* Hooks may accept the following parameters: * Hooks may accept the following parameters:
* </p> * </p>
* <ul> * <ul>
@ -1561,12 +1597,13 @@ public enum Pointcut {
), ),
/** /**
* Note that this is a performance tracing hook. Use with caution in production * <b>Performance Tracing Hook:</b>
* systems, since calling it may (or may not) carry a cost.
* <p>
* This hook is invoked when a search has failed for any reason. When this pointcut * This hook is invoked when a search has failed for any reason. When this pointcut
* is invoked, a pass in the Search Coordinator has completed successfully, and all * is invoked, a pass in the Search Coordinator has completed successfully, and all
* possible results have been fetched and loaded into the query cache. * possible results have been fetched and loaded into the query cache.
* <p>
* Note that this is a performance tracing hook. Use with caution in production
* systems, since calling it may (or may not) carry a cost.
* </p> * </p>
* Hooks may accept the following parameters: * Hooks may accept the following parameters:
* <ul> * <ul>
@ -1600,14 +1637,20 @@ public enum Pointcut {
/** /**
* THIS IS AN EXPERIMENTAL HOOK AND MAY BE REMOVED OR CHANGED WITHOUT WARNING. * <b>Performance Tracing Hook:</b>
*
* Note that this is a performance tracing hook. Use with caution in production
* systems, since calling it may (or may not) carry a cost.
* <p> * <p>
* This hook is invoked when a search has found an individual ID. * This hook is invoked when a search has found an individual ID.
* </p> * </p>
* <p>
* THIS IS AN EXPERIMENTAL HOOK AND MAY BE REMOVED OR CHANGED WITHOUT WARNING.
* </p>
* <p>
* Note that this is a performance tracing hook. Use with caution in production
* systems, since calling it may (or may not) carry a cost.
* </p>
* <p>
* Hooks may accept the following parameters: * Hooks may accept the following parameters:
* </p>
* <ul> * <ul>
* <li> * <li>
* java.lang.Integer - The query ID * java.lang.Integer - The query ID
@ -1627,13 +1670,16 @@ public enum Pointcut {
/** /**
* Note that this is a performance tracing hook. Use with caution in production * <b>Performance Tracing Hook:</b>
* systems, since calling it may (or may not) carry a cost.
* <p>
* This hook is invoked when a query has executed, and includes the raw SQL * This hook is invoked when a query has executed, and includes the raw SQL
* statements that were executed against the database. * statements that were executed against the database.
* <p>
* Note that this is a performance tracing hook. Use with caution in production
* systems, since calling it may (or may not) carry a cost.
* </p> * </p>
* <p>
* Hooks may accept the following parameters: * Hooks may accept the following parameters:
* </p>
* <ul> * <ul>
* <li> * <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the

View File

@ -139,7 +139,7 @@ public interface IGenericClient extends IRestfulClient {
* Register a new interceptor for this client. An interceptor can be used to add additional logging, or add security headers, or pre-process responses, etc. * Register a new interceptor for this client. An interceptor can be used to add additional logging, or add security headers, or pre-process responses, etc.
*/ */
@Override @Override
void registerInterceptor(IClientInterceptor theInterceptor); void registerInterceptor(Object theInterceptor);
/** /**
* Search for resources matching a given set of criteria. Searching is a very powerful * Search for resources matching a given set of criteria. Searching is a very powerful
@ -168,10 +168,10 @@ public interface IGenericClient extends IRestfulClient {
ITransaction transaction(); ITransaction transaction();
/** /**
* Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)} * Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(Object)}
*/ */
@Override @Override
void unregisterInterceptor(IClientInterceptor theInterceptor); void unregisterInterceptor(Object theInterceptor);
/** /**
* Fluent method for the "update" operation, which performs a logical delete on a server resource * Fluent method for the "update" operation, which performs a logical delete on a server resource

View File

@ -97,7 +97,7 @@ public interface IRestfulClient {
* <code>getInterceptorService().registerInterceptor(theInterceptor)</code> * <code>getInterceptorService().registerInterceptor(theInterceptor)</code>
* </p> * </p>
*/ */
void registerInterceptor(IClientInterceptor theInterceptor); void registerInterceptor(Object theInterceptor);
/** /**
* Specifies that the client should request that the server respond with "pretty printing" * Specifies that the client should request that the server respond with "pretty printing"
@ -115,13 +115,13 @@ public interface IRestfulClient {
void setSummary(SummaryEnum theSummary); void setSummary(SummaryEnum theSummary);
/** /**
* Remove an interceptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}. * Remove an interceptor that was previously registered using {@link IRestfulClient#registerInterceptor(Object)}.
* <p> * <p>
* This is a convenience method for performing the following call: * This is a convenience method for performing the following call:
* <code>getInterceptorService().unregisterInterceptor(theInterceptor)</code> * <code>getInterceptorService().unregisterInterceptor(theInterceptor)</code>
* </p> * </p>
*/ */
void unregisterInterceptor(IClientInterceptor theInterceptor); void unregisterInterceptor(Object theInterceptor);
/** /**
* Configures what style of _format parameter should be used in requests * Configures what style of _format parameter should be used in requests

View File

@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.client.api.*;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException; import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException; import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException; import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.client.interceptor.AdditionalRequestHeadersInterceptor;
import ca.uhn.fhir.rest.client.method.HttpGetClientInvocation; import ca.uhn.fhir.rest.client.method.HttpGetClientInvocation;
import ca.uhn.fhir.rest.client.method.IClientResponseHandler; import ca.uhn.fhir.rest.client.method.IClientResponseHandler;
import ca.uhn.fhir.rest.client.method.IClientResponseHandlerHandlesBinary; import ca.uhn.fhir.rest.client.method.IClientResponseHandlerHandlesBinary;
@ -137,7 +138,7 @@ public abstract class BaseClient implements IRestfulClient {
@Override @Override
public <T extends IBaseResource> T fetchResourceFromUrl(Class<T> theResourceType, String theUrl) { public <T extends IBaseResource> T fetchResourceFromUrl(Class<T> theResourceType, String theUrl) {
BaseHttpClientInvocation clientInvocation = new HttpGetClientInvocation(getFhirContext(), theUrl); BaseHttpClientInvocation clientInvocation = new HttpGetClientInvocation(getFhirContext(), theUrl);
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theResourceType); ResourceResponseHandler<T> binding = new ResourceResponseHandler<>(theResourceType);
return invokeClient(getFhirContext(), binding, clientInvocation, null, false, false, null, null, null, null, null); return invokeClient(getFhirContext(), binding, clientInvocation, null, false, false, null, null, null, null, null);
} }
@ -274,21 +275,13 @@ public abstract class BaseClient implements IRestfulClient {
addToCacheControlHeader(b, Constants.CACHE_CONTROL_NO_CACHE, theCacheControlDirective.isNoCache()); addToCacheControlHeader(b, Constants.CACHE_CONTROL_NO_CACHE, theCacheControlDirective.isNoCache());
addToCacheControlHeader(b, Constants.CACHE_CONTROL_NO_STORE, theCacheControlDirective.isNoStore()); addToCacheControlHeader(b, Constants.CACHE_CONTROL_NO_STORE, theCacheControlDirective.isNoStore());
if (theCacheControlDirective.getMaxResults() != null) { if (theCacheControlDirective.getMaxResults() != null) {
addToCacheControlHeader(b, Constants.CACHE_CONTROL_MAX_RESULTS + "=" + Integer.toString(theCacheControlDirective.getMaxResults().intValue()), true); addToCacheControlHeader(b, Constants.CACHE_CONTROL_MAX_RESULTS + "=" + theCacheControlDirective.getMaxResults().intValue(), true);
} }
if (b.length() > 0) { if (b.length() > 0) {
httpRequest.addHeader(Constants.HEADER_CACHE_CONTROL, b.toString()); httpRequest.addHeader(Constants.HEADER_CACHE_CONTROL, b.toString());
} }
} }
if (theCustomHeaders != null) {
for (Map.Entry<String, List<String>> customHeader: theCustomHeaders.entrySet()) {
for (String value: customHeader.getValue()) {
httpRequest.addHeader(customHeader.getKey(), value);
}
}
}
if (theLogRequestAndResponse) { if (theLogRequestAndResponse) {
ourLog.info("Client invoking: {}", httpRequest); ourLog.info("Client invoking: {}", httpRequest);
String body = httpRequest.getRequestBodyFromStream(); String body = httpRequest.getRequestBodyFromStream();
@ -297,6 +290,11 @@ public abstract class BaseClient implements IRestfulClient {
} }
} }
if (theCustomHeaders != null) {
AdditionalRequestHeadersInterceptor interceptor = new AdditionalRequestHeadersInterceptor(theCustomHeaders);
interceptor.interceptRequest(httpRequest);
}
HookParams requestParams = new HookParams(); HookParams requestParams = new HookParams();
requestParams.add(IHttpRequest.class, httpRequest); requestParams.add(IHttpRequest.class, httpRequest);
getInterceptorService().callHooks(Pointcut.CLIENT_REQUEST, requestParams); getInterceptorService().callHooks(Pointcut.CLIENT_REQUEST, requestParams);
@ -458,7 +456,7 @@ public abstract class BaseClient implements IRestfulClient {
if (StringUtils.isNotBlank(responseString)) { if (StringUtils.isNotBlank(responseString)) {
ourLog.info("Client response: {}\n{}", message, responseString); ourLog.info("Client response: {}\n{}", message, responseString);
} else { } else {
ourLog.info("Client response: {}", message, responseString); ourLog.info("Client response: {}", message);
} }
} else { } else {
ourLog.trace("FHIR response:\n{}\n{}", response, responseString); ourLog.trace("FHIR response:\n{}\n{}", response, responseString);
@ -466,7 +464,7 @@ public abstract class BaseClient implements IRestfulClient {
} }
@Override @Override
public void registerInterceptor(IClientInterceptor theInterceptor) { public void registerInterceptor(Object theInterceptor) {
Validate.notNull(theInterceptor, "Interceptor can not be null"); Validate.notNull(theInterceptor, "Interceptor can not be null");
getInterceptorService().registerInterceptor(theInterceptor); getInterceptorService().registerInterceptor(theInterceptor);
} }
@ -474,14 +472,14 @@ public abstract class BaseClient implements IRestfulClient {
/** /**
* This method is an internal part of the HAPI API and may change, use with caution. If you want to disable the * This method is an internal part of the HAPI API and may change, use with caution. If you want to disable the
* loading of conformance statements, use * loading of conformance statements, use
* {@link IRestfulClientFactory#setServerValidationModeEnum(ServerValidationModeEnum)} * {@link IRestfulClientFactory#setServerValidationMode(ServerValidationModeEnum)}
*/ */
public void setDontValidateConformance(boolean theDontValidateConformance) { public void setDontValidateConformance(boolean theDontValidateConformance) {
myDontValidateConformance = theDontValidateConformance; myDontValidateConformance = theDontValidateConformance;
} }
@Override @Override
public void unregisterInterceptor(IClientInterceptor theInterceptor) { public void unregisterInterceptor(Object theInterceptor) {
Validate.notNull(theInterceptor, "Interceptor can not be null"); Validate.notNull(theInterceptor, "Interceptor can not be null");
getInterceptorService().unregisterInterceptor(theInterceptor); getInterceptorService().unregisterInterceptor(theInterceptor);
} }
@ -612,7 +610,7 @@ public abstract class BaseClient implements IRestfulClient {
static ArrayList<Class<? extends IBaseResource>> toTypeList(Class<? extends IBaseResource> thePreferResponseType) { static ArrayList<Class<? extends IBaseResource>> toTypeList(Class<? extends IBaseResource> thePreferResponseType) {
ArrayList<Class<? extends IBaseResource>> preferResponseTypes = null; ArrayList<Class<? extends IBaseResource>> preferResponseTypes = null;
if (thePreferResponseType != null) { if (thePreferResponseType != null) {
preferResponseTypes = new ArrayList<Class<? extends IBaseResource>>(1); preferResponseTypes = new ArrayList<>(1);
preferResponseTypes.add(thePreferResponseType); preferResponseTypes.add(thePreferResponseType);
} }
return preferResponseTypes; return preferResponseTypes;

View File

@ -20,11 +20,10 @@ package ca.uhn.fhir.rest.client.interceptor;
* #L% * #L%
*/ */
import ca.uhn.fhir.rest.client.api.IClientInterceptor; import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -37,17 +36,25 @@ import java.util.Objects;
* This is now also possible directly on the Fluent Client API by calling * This is now also possible directly on the Fluent Client API by calling
* {@link ca.uhn.fhir.rest.gclient.IClientExecutable#withAdditionalHeader(String, String)} * {@link ca.uhn.fhir.rest.gclient.IClientExecutable#withAdditionalHeader(String, String)}
*/ */
public class AdditionalRequestHeadersInterceptor implements IClientInterceptor { public class AdditionalRequestHeadersInterceptor {
private final Map<String, List<String>> additionalHttpHeaders = new HashMap<>(); private final Map<String, List<String>> myAdditionalHttpHeaders;
/**
* Constructor
*/
public AdditionalRequestHeadersInterceptor() { public AdditionalRequestHeadersInterceptor() {
this(new HashMap<>()); myAdditionalHttpHeaders = new HashMap<>();
} }
public AdditionalRequestHeadersInterceptor(Map<String, List<String>> additionalHttpHeaders) { /**
super(); * Constructor
if (additionalHttpHeaders != null) { *
this.additionalHttpHeaders.putAll(additionalHttpHeaders); * @param theHeaders The additional headers to add to every request
*/
public AdditionalRequestHeadersInterceptor(Map<String, List<String>> theHeaders) {
this();
if (theHeaders != null) {
myAdditionalHttpHeaders.putAll(theHeaders);
} }
} }
@ -86,19 +93,19 @@ public class AdditionalRequestHeadersInterceptor implements IClientInterceptor {
* @return the list of values for the header * @return the list of values for the header
*/ */
private List<String> getHeaderValues(String headerName) { private List<String> getHeaderValues(String headerName) {
if (additionalHttpHeaders.get(headerName) == null) { if (myAdditionalHttpHeaders.get(headerName) == null) {
additionalHttpHeaders.put(headerName, new ArrayList<>()); myAdditionalHttpHeaders.put(headerName, new ArrayList<>());
} }
return additionalHttpHeaders.get(headerName); return myAdditionalHttpHeaders.get(headerName);
} }
/** /**
* Adds the additional header values to the HTTP request. * Adds the additional header values to the HTTP request.
* @param theRequest the HTTP request * @param theRequest the HTTP request
*/ */
@Override @Hook(Pointcut.CLIENT_REQUEST)
public void interceptRequest(IHttpRequest theRequest) { public void interceptRequest(IHttpRequest theRequest) {
for (Map.Entry<String, List<String>> header : additionalHttpHeaders.entrySet()) { for (Map.Entry<String, List<String>> header : myAdditionalHttpHeaders.entrySet()) {
for (String headerValue : header.getValue()) { for (String headerValue : header.getValue()) {
if (headerValue != null) { if (headerValue != null) {
theRequest.addHeader(header.getKey(), headerValue); theRequest.addHeader(header.getKey(), headerValue);
@ -107,13 +114,4 @@ public class AdditionalRequestHeadersInterceptor implements IClientInterceptor {
} }
} }
/**
* Does nothing since this interceptor is not concerned with the response.
* @param theResponse the HTTP response
* @throws IOException
*/
@Override
public void interceptResponse(IHttpResponse theResponse) throws IOException {
// Do nothing. This interceptor is not concerned with the response.
}
} }

View File

@ -94,6 +94,16 @@
<version>4.2.0-SNAPSHOT</version> <version>4.2.0-SNAPSHOT</version>
<classifier>classes</classifier> <classifier>classes</classifier>
</dependency> </dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -57,6 +57,7 @@ public class ChangelogMigrator {
int releaseCount = 0; int releaseCount = 0;
Element docElement = document.getRootElement(); Element docElement = document.getRootElement();
Element bodyElement = docElement.getChild("body", NS); Element bodyElement = docElement.getChild("body", NS);
List<Element> releases = bodyElement.getChildren("release", NS); List<Element> releases = bodyElement.getChildren("release", NS);
@ -68,6 +69,8 @@ public class ChangelogMigrator {
releaseCount++; releaseCount++;
for (Element nextAction : nextRelease.getChildren("action", NS)) { for (Element nextAction : nextRelease.getChildren("action", NS)) {
String type = nextAction.getAttribute("type").getValue();
String issue = nextAction.getAttribute("issue") != null ? nextAction.getAttribute("issue").getValue() : null;
StringBuilder contentBuilder = new StringBuilder(); StringBuilder contentBuilder = new StringBuilder();
for (Content nextContents : nextAction.getContent()) { for (Content nextContents : nextAction.getContent()) {
if (nextContents instanceof Text) { if (nextContents instanceof Text) {

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IClientInterceptor; import ca.uhn.fhir.rest.client.api.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.IRestfulClientFactory; import ca.uhn.fhir.rest.client.api.IRestfulClientFactory;
import ca.uhn.fhir.rest.client.interceptor.AdditionalRequestHeadersInterceptor;
import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor;
import ca.uhn.fhir.rest.client.interceptor.BearerTokenAuthInterceptor; import ca.uhn.fhir.rest.client.interceptor.BearerTokenAuthInterceptor;
import ca.uhn.fhir.rest.client.interceptor.CookieInterceptor; import ca.uhn.fhir.rest.client.interceptor.CookieInterceptor;
@ -45,7 +46,7 @@ public class ClientExamples {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public void createProxy() { public void createProxy() {
// START SNIPPET: proxy // START SNIPPET: proxy
FhirContext ctx = FhirContext.forDstu2(); FhirContext ctx = FhirContext.forR4();
// Set connections to access the network via the HTTP proxy at // Set connections to access the network via the HTTP proxy at
// example.com : 8888 // example.com : 8888
@ -115,7 +116,7 @@ public class ClientExamples {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public void createTimeouts() { public void createTimeouts() {
// START SNIPPET: timeouts // START SNIPPET: timeouts
FhirContext ctx = FhirContext.forDstu2(); FhirContext ctx = FhirContext.forR4();
// Set how long to try and establish the initial TCP connection (in ms) // Set how long to try and establish the initial TCP connection (in ms)
ctx.getRestfulClientFactory().setConnectTimeout(20 * 1000); ctx.getRestfulClientFactory().setConnectTimeout(20 * 1000);
@ -132,7 +133,7 @@ public class ClientExamples {
public void createSecurity() { public void createSecurity() {
// START SNIPPET: security // START SNIPPET: security
// Create a context and get the client factory so it can be configured // Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2(); FhirContext ctx = FhirContext.forR4();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory(); IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// Create an HTTP basic auth interceptor // Create an HTTP basic auth interceptor
@ -155,7 +156,7 @@ public class ClientExamples {
public void createCookie() { public void createCookie() {
// START SNIPPET: cookie // START SNIPPET: cookie
// Create a context and get the client factory so it can be configured // Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2(); FhirContext ctx = FhirContext.forR4();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory(); IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// Create a cookie interceptor. This cookie will have the name "mycookie" and // Create a cookie interceptor. This cookie will have the name "mycookie" and
@ -171,11 +172,40 @@ public class ClientExamples {
// END SNIPPET: cookie // END SNIPPET: cookie
} }
@SuppressWarnings("unused")
public void addHeaders() {
// START SNIPPET: addHeaders
// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forR4();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// Create a client
IGenericClient client = ctx.newRestfulGenericClient( "http://localhost:9999/fhir");
// Register an additional headers interceptor and add one header to it
AdditionalRequestHeadersInterceptor interceptor = new AdditionalRequestHeadersInterceptor();
interceptor.addHeaderValue("X-Message", "Help I'm a Bug");
client.registerInterceptor(interceptor);
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
client.registerInterceptor(interceptor);
// END SNIPPET: addHeaders
// START SNIPPET: addHeadersNoInterceptor
Patient p = client
.read()
.resource(Patient.class)
.withId(123L)
.withAdditionalHeader("X-Message", "Help I'm a Bug")
.execute();
// END SNIPPET: addHeadersNoInterceptor
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
public void gzip() { public void gzip() {
// START SNIPPET: gzip // START SNIPPET: gzip
// Create a context and get the client factory so it can be configured // Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2(); FhirContext ctx = FhirContext.forR4();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory(); IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// Register the interceptor with your client (either style) // Register the interceptor with your client (either style)
@ -188,7 +218,7 @@ public class ClientExamples {
public void createSecurityBearer() { public void createSecurityBearer() {
// START SNIPPET: securityBearer // START SNIPPET: securityBearer
// Create a context and get the client factory so it can be configured // Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2(); FhirContext ctx = FhirContext.forR4();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory(); IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// In reality the token would have come from an authorization server // In reality the token would have come from an authorization server
@ -210,7 +240,7 @@ public class ClientExamples {
{ {
// START SNIPPET: logging // START SNIPPET: logging
// Create a context and get the client factory so it can be configured // Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2(); FhirContext ctx = FhirContext.forR4();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory(); IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// Create a logging interceptor // Create a logging interceptor
@ -234,7 +264,7 @@ public class ClientExamples {
{ {
// START SNIPPET: clientConfig // START SNIPPET: clientConfig
// Create a client // Create a client
FhirContext ctx = FhirContext.forDstu2(); FhirContext ctx = FhirContext.forR4();
IPatientClient client = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/"); IPatientClient client = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/");
// Request JSON encoding from the server (_format=json) // Request JSON encoding from the server (_format=json)

View File

@ -0,0 +1,69 @@
package ca.uhn.hapi.fhir.docs;
/*-
* #%L
* HAPI FHIR - Docs
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import org.hl7.fhir.r4.model.Patient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Interceptors {
// START SNIPPET: sampleClass
@Interceptor
public class SimpleServerLoggingInterceptor {
private final Logger ourLog = LoggerFactory.getLogger(SimpleServerLoggingInterceptor.class);
@Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED)
public void logRequests(RequestDetails theRequest) {
ourLog.info("Request of type {} with request ID: {}", theRequest.getOperation(), theRequest.getRequestId());
}
}
// END SNIPPET: sampleClass
public void registerClient() {
// START SNIPPET: registerClient
FhirContext ctx = FhirContext.forR4();
// Create a new client instance
IGenericClient client = ctx.newRestfulGenericClient("http://hapi.fhir.org/baseR4");
// Register an interceptor against the client
client.registerInterceptor(new LoggingInterceptor());
// Perform client actions...
Patient pt = client.read().resource(Patient.class).withId("example").execute();
// END SNIPPET: registerClient
}
}

View File

@ -1,43 +0,0 @@
package ca.uhn.hapi.fhir.docs;
/*-
* #%L
* HAPI FHIR - Docs
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// START SNIPPET: sampleClass
@Interceptor
public class MyInterceptor {
private static final Logger ourLog = LoggerFactory.getLogger(MyInterceptor.class);
@Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED)
public void logRequests(RequestDetails theRequest) {
ourLog.info("Request of type {} with request ID: {}", theRequest.getOperation(), theRequest.getRequestId());
}
}
// END SNIPPET: sampleClass

View File

@ -23,6 +23,7 @@ package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import com.google.common.collect.Sets;
import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient;
import java.io.IOException; import java.io.IOException;
@ -30,8 +31,30 @@ import java.io.IOException;
public class Parser { public class Parser {
public static void main(String[] args) throws DataFormatException, IOException { public static void main(String[] args) throws DataFormatException, IOException {
{ {
//START SNIPPET: createParser //START SNIPPET: parsing
// Create a FHIR context
FhirContext ctx = FhirContext.forR4();
// The following example is a simple serialized Patient resource to parse
String input = "{" +
"\"resourceType\" : \"Patient\"," +
" \"name\" : [{" +
" \"family\": \"Simpson\"" +
" }]" +
"}";
// Instantiate a new parser
IParser parser = ctx.newJsonParser();
// Parse it
Patient parsed = parser.parseResource(Patient.class, input);
System.out.println(parsed.getName().get(0).getFamily());
//END SNIPPET: parsing
}
{
//START SNIPPET: encoding
// Create a FHIR context // Create a FHIR context
FhirContext ctx = FhirContext.forR4(); FhirContext ctx = FhirContext.forR4();
@ -39,13 +62,62 @@ public class Parser {
Patient patient = new Patient(); Patient patient = new Patient();
patient.addName().setFamily("Simpson").addGiven("James"); patient.addName().setFamily("Simpson").addGiven("James");
// Instantiate a new parser // Instantiate a new JSON parser
IParser parser = ctx.newJsonParser(); IParser parser = ctx.newJsonParser();
// Serialize it // Serialize it
String serialized = parser.encodeResourceToString(patient); String serialized = parser.encodeResourceToString(patient);
System.out.println(serialized); System.out.println(serialized);
//END SNIPPET: createParser
// Using XML instead
serialized = ctx.newXmlParser().encodeResourceToString(patient);
System.out.println(serialized);
//END SNIPPET: encoding
}
{
// Create a FHIR context
FhirContext ctx = FhirContext.forR4();
Patient patient = new Patient();
patient.addName().setFamily("Simpson").addGiven("James");
//START SNIPPET: encodingPretty
// Create a parser
IParser parser = ctx.newJsonParser();
// Indent the output
parser.setPrettyPrint(true);
// Serialize it
String serialized = parser.encodeResourceToString(patient);
System.out.println(serialized);
// You can also chain these statements together
ctx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
//END SNIPPET: encodingPretty
}
{
// Create a FHIR context
FhirContext ctx = FhirContext.forR4();
Patient patient = new Patient();
patient.addName().setFamily("Simpson").addGiven("James");
//START SNIPPET: encodingConfig
// Create a parser
IParser parser = ctx.newJsonParser();
// Blacklist certain fields from being encoded
parser.setDontEncodeElements(Sets.newHashSet("Patient.identifier", "Patient.active"));
// Don't include resource narratives
parser.setSuppressNarratives(true);
// Use versioned references for these reference elements
parser.setDontStripVersionsFromReferencesAtPaths("Patient.organization");
// Serialize it
String serialized = parser.encodeResourceToString(patient);
System.out.println(serialized);
//END SNIPPET: encodingConfig
} }
{ {
//START SNIPPET: disableStripVersions //START SNIPPET: disableStripVersions

View File

@ -20,13 +20,17 @@ package ca.uhn.hapi.fhir.docs;
* #L% * #L%
*/ */
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
//START SNIPPET: interceptor //START SNIPPET: interceptor
public class RequestCounterInterceptor extends InterceptorAdapter @Interceptor
public class RequestCounterInterceptor
{ {
private int myRequestCount; private int myRequestCount;
@ -39,7 +43,7 @@ public class RequestCounterInterceptor extends InterceptorAdapter
* Override the incomingRequestPreProcessed method, which is called * Override the incomingRequestPreProcessed method, which is called
* for each incoming request before any processing is done * for each incoming request before any processing is done
*/ */
@Override @Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_PROCESSED)
public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse) { public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse) {
myRequestCount++; myRequestCount++;
return true; return true;

View File

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

View File

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

View File

@ -275,6 +275,36 @@
"lat": 14.6914, "lat": 14.6914,
"lon": 120.9686, "lon": 120.9686,
"added": "2019-11-25" "added": "2019-11-25"
},
{
"title": "Software Interoperability Lab for Asia",
"description": "SIL-A maintains a suite of tools used to promote interoperability and medical informatics across Asia.",
"link": "http://sil-asia.org/",
"city": "Manila, Philippines",
"lat": 14.6914,
"lon": 120.9686,
"added": "2019-11-25"
},
{
"title": "Lifen",
"description": "Lifen is a messaging platform to simplify data exchange between medical professionals, patients and EHRs systems",
"link": "https://www.lifen.fr",
"city": "Paris",
"lat": 48.8647,
"lon": 2.349014,
"added": "2019-11-22"
},
{
"title": "Aksara Digital Indonesia, PT.",
"description": "SaaS ERP for Healthcare Services",
"link": "https://ehealth.co.id",
"contactName": "Ibrohim Kholilul Islam",
"contactEmail": "ibrohim@ehealth.co.id",
"city": "Bandung, Indonesia",
"lat": -6.914744,
"lon": 107.609810,
"added": "2019-11-22"
} }
] ]
} }

View File

@ -0,0 +1,14 @@
# JavaDocs
See the [Modules Page](/docs/introduction/modules.html) for more information on the various modules in HAPI FHIR.
* [Core API](/apidocs/hapi-fhir-base/) - hapi-fhir-base
* [Model API (DSTU2)](/apidocs/hapi-fhir-structures-dstu2/) - hapi-fhir-structures-dstu2
* [Model API (DSTU2)](/apidocs/hapi-fhir-structures-dstu3/) - hapi-fhir-structures-dstu3
* [Model API (R4)](/apidocs/hapi-fhir-structures-r4/) - hapi-fhir-structures-r4
* [Model API (R5)](/apidocs/hapi-fhir-structures-r5/) - hapi-fhir-structures-r5
* [Client API](/apidocs/hapi-fhir-client/) - hapi-fhir-client
* [Server API (Plain)](/apidocs/hapi-fhir-server/) - hapi-fhir-server
* [Server API (JPA)](/apidocs/hapi-fhir-jpaserver-base/) - hapi-fhir-jpaserver-base
* [Version Converter API](/apidocs/hapi-fhir-converter/) - hapi-fhir-converter
* [Server API (JAX-RS)](/apidocs/hapi-fhir-jaxrsserver-base/) - hapi-fhir-jaxrsserver-base

View File

@ -58,9 +58,9 @@ Note that some popular libraries (e.g. Spring Framework) also use commons-loggin
# Client Payload Logging # Client Payload Logging
To enable detailed logging of client requests and responses (what URL is being requested, what headers and payload are being received, etc.), an interceptor may be added to the client which logs each transaction. See [Logging Requests and Responses](/docs/interceptors/built_in_client_interceptors.html#req_resp_logging) for more information. To enable detailed logging of client requests and responses (what URL is being requested, what headers and payload are being received, etc.), an interceptor may be added to the client which logs each transaction. See [Logging Requests and Responses](/docs/interceptors/built_in_client_interceptors.html#logging_interceptor) for more information.
# Server Request Logging # Server Request Logging
To enable detailed logging of server requests and responses, an interceptor may be added to the server which logs each transaction. See [Logging Server Requests](/docs/interceptors/built_in_server_interceptors.html#logging_server_requests) for more information. To enable detailed logging of server requests and responses, an interceptor may be added to the server which logs each transaction. See [Logging Interceptr](/docs/interceptors/built_in_server_interceptors.html#logging_interceptor) for more information.

View File

@ -26,13 +26,13 @@ HAPI is built primary using [Apache Maven](http://maven.apache.org/). Even if yo
Execute the build with the following command: Execute the build with the following command:
``` ```bash
mvn install mvn install
``` ```
Note that this complete build takes a long time because of all of the unit tests being executed. At the end you should expect to see a screen resembling: Note that this complete build takes a long time because of all of the unit tests being executed. At the end you should expect to see a screen resembling:
``` ```bash
------------------------------------------------------------------------ ------------------------------------------------------------------------
[INFO] Reactor Summary: [INFO] Reactor Summary:
[INFO] [INFO]
@ -57,7 +57,7 @@ If the build fails to execute successfully, try the following:
* The first thing to try is always a fresh clean build when things aren't working: * The first thing to try is always a fresh clean build when things aren't working:
``` ```bash
mvn clean install mvn clean install
``` ```
@ -65,7 +65,7 @@ If the build fails to execute successfully, try the following:
* If the build fails with memory issues (or mysteriously dies during unit tests), your build environment may be running out of memory. By default, the HAPI build executes unit tests in multiple parallel JVMs in order to save time. This can consume a lot of RAM and sometimes causes issues. Try executing with the following command to disable this behaviour: * If the build fails with memory issues (or mysteriously dies during unit tests), your build environment may be running out of memory. By default, the HAPI build executes unit tests in multiple parallel JVMs in order to save time. This can consume a lot of RAM and sometimes causes issues. Try executing with the following command to disable this behaviour:
``` ```bash
mvn -P ALLMODULES,NOPARALLEL install mvn -P ALLMODULES,NOPARALLEL install
``` ```

View File

@ -18,44 +18,46 @@ page.model.narrative_generation=Narrative Generation
section.client.title=Client section.client.title=Client
page.client.introduction=Introduction page.client.introduction=Introduction
page.client.get_started=Get Started ⚡
page.client.generic_client=Generic (Fluent) Client page.client.generic_client=Generic (Fluent) Client
page.client.annotation_client=Annotation Client page.client.annotation_client=Annotation Client
page.client.client_configuration=Client Configuration page.client.client_configuration=Client Configuration
page.client.get_started=Get Started
page.client.examples=Client Examples page.client.examples=Client Examples
section.server_plain.title=Plain Server section.server_plain.title=Plain Server
page.server_plain.server_types=REST Server Types page.server_plain.server_types=REST Server Types
page.server_plain.introduction=Plain Server Introduction page.server_plain.introduction=Plain Server Introduction
page.server_plain.get_started=Getting Started with Plain Server page.server_plain.get_started=Get Started ⚡
page.server_plain.resource_providers=Resource Providers and Plan Providers page.server_plain.resource_providers=Resource Providers and Plan Providers
page.server_plain.rest_operations=REST Operations: Overview page.server_plain.rest_operations=REST Operations: Overview
page.server_plain.rest_operations_search=REST Operations: Search page.server_plain.rest_operations_search=REST Operations: Search
page.server_plain.rest_operations_operations=REST Operations: Extended Operations page.server_plain.rest_operations_operations=REST Operations: Extended Operations
page.server_plain.paging=Paging Search Results page.server_plain.paging=Paging Search Results
page.server_plain.cors=CORS
page.server_plain.web_testpage_overlay=Web Testpage Overlay page.server_plain.web_testpage_overlay=Web Testpage Overlay
page.server_plain.multitenency=Multitenency page.server_plain.multitenency=Multitenency
page.server_plain.jax_rs=JAX-RS Support page.server_plain.jax_rs=JAX-RS Support
section.server_jpa.title=JPA Server section.server_jpa.title=JPA Server
page.server_jpa.introduction=Introduction page.server_jpa.introduction=Introduction
page.server_jpa.get_started=Get Started page.server_jpa.get_started=Get Started
page.server_jpa.architecture=Architecture page.server_jpa.architecture=Architecture
page.server_jpa.upgrading=Upgrade Guide page.server_jpa.upgrading=Upgrade Guide
section.interceptors.title=Interceptors section.interceptors.title=Interceptors
page.interceptors.interceptors=Interceptors Overview page.interceptors.interceptors=Interceptors Overview
page.interceptors.client_interceptors=Client Interceptors page.interceptors.client_interceptors=Client Interceptors
page.interceptors.client_pointcuts=Client Pointcuts
page.interceptors.built_in_client_interceptors=Built-In Client Interceptors page.interceptors.built_in_client_interceptors=Built-In Client Interceptors
page.interceptors.server_interceptors=Server Interceptors page.interceptors.server_interceptors=Server Interceptors
page.interceptors.server_pointcuts=Server Pointcuts
page.interceptors.built_in_server_interceptors=Built-In Server Interceptors page.interceptors.built_in_server_interceptors=Built-In Server Interceptors
section.security.title=Security section.security.title=Security
page.security.introduction=Introduction page.security.introduction=Introduction
page.security.authorization_interceptor=Authorization Interceptor page.security.authorization_interceptor=Authorization Interceptor
page.security.consent_interceptor=Consent Interceptor page.security.consent_interceptor=Consent Interceptor
page.security.narrowing_interceptor=Narrowing Interceptor page.security.search_narrowing_interceptor=Search Narrowing Interceptor
page.security.cors=CORS
section.validation.title=Validation section.validation.title=Validation
page.validation.introduction=Introduction page.validation.introduction=Introduction
@ -76,3 +78,4 @@ page.contributing.hacking_guide=Hacking Guide
section.appendix.title=Appendix section.appendix.title=Appendix
page.appendix.logging=Logging page.appendix.logging=Logging
page.appendix.faq=FAQ page.appendix.faq=FAQ
page.appendix.javadocs=JavaDocs

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 58 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 209 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 254 KiB

View File

@ -1,3 +1,101 @@
# Built-In Client Interceptors # Built-In Client Interceptors
<a name="req_resp_logging"/> This page describes some client interceptors that are shipped with HAPI FHIR out of the box. Of course, you are also welcome to create your own.
<a name="logging_interceptor"/>
# Logging: Logging Interceptor
The LoggingInterceptor logs details about each request and/or response that is performed using the client. All logging is performed using SLF4j.
* [LoggingInterceptor JavaDoc](/apidocs/hapi-fhir-client/ca/uhn/fhir/rest/client/interceptor/LoggingInterceptor.html)
* [LoggingInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/LoggingInterceptor.java)
LoggingInterceptor is highly configurable in terms of its output. It can be configured to log simple details about requests, or detailed output including payload bodies and header contents. The following example shows how to enable LoggingInterceptor.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ClientExamples.java|logging}}
```
# Security: HTTP Basic Authorization
The BasicAuthInterceptor adds an `Authorization` header containing an HTTP Basic Auth (username+password) token in every outgoing request.
* [BasicAuthInterceptor JavaDoc](/apidocs/hapi-fhir-client/ca/uhn/fhir/rest/client/interceptor/BasicAuthInterceptor.html)
* [BasicAuthInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/BasicAuthInterceptor.java)
The following example shows how to configure your client to use a specific username and password in every request.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ClientExamples.java|security}}
```
# Security: HTTP Bearer Token Authorization
The BearerTokenAuthInterceptor can be used to add an `Authorization` header containing a bearer token (typically used for OIDC/OAuth2/SMART security flows) to every outgoing request.
* [BearerTokenAuthInterceptor JavaDoc](/apidocs/hapi-fhir-client/ca/uhn/fhir/rest/client/interceptor/BearerTokenAuthInterceptor.html)
* [BearerTokenAuthInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/BearerTokenAuthInterceptor.java)
The following example shows how to configure your client to inject a bearer token authorization header into every request.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ClientExamples.java|securityBearer}}
```
# Misc: Add Headers to Request
The AdditionlRequestHeadersInterceptor can be used to add arbitrary headers to each request created by the client.
* [AdditionalRequestHeadersInterceptor JavaDoc](/apidocs/hapi-fhir-client/ca/uhn/fhir/rest/client/interceptor/AdditionalRequestHeadersInterceptor.html)
* [AdditionalRequestHeadersInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/AdditionalRequestHeadersInterceptor.java)
The following example shows how to configure your client to inject a bearer token authorization header into every request.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ClientExamples.java|addHeaders}}
```
Note that headers can also be added to individual [Generic Client](/docs/client/generic_client.html) invocations inline. The example below will produce the same additional request header as the example above, although it applies only to the one request.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ClientExamples.java|addHeadersNoInterceptor}}
```
# Misc: Add Cookies to Request
The CookieInterceptor can be used to add an HTTP Cookie header to each request created by the client.
* [CookieInterceptor JavaDoc](/apidocs/hapi-fhir-client/ca/uhn/fhir/rest/client/interceptor/CookieInterceptor.html)
* [CookieInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/CookieInterceptor.java)
The following example shows how to configure your client to inject a bearer token authorization header into every request.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ClientExamples.java|cookie}}
```
# Performance: GZip Outgoing Request Bodies
The GZipContentInterceptor compresses outgoing contents. With this interceptor, if the client is transmitting resources to the server (e.g. for a create, update, transaction, etc.) the content will be GZipped before transmission to the server.
* [GZipContentInterceptor JavaDoc](/apidocs/hapi-fhir-client/ca/uhn/fhir/rest/client/apache/GZipContentInterceptor.html)
* [GZipContentInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/GZipContentInterceptor.java)
The following example shows how to enable the GZipContentInterceptor.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ClientExamples.java|gzip}}
```
# Capture: Programmatically Capturing Request/Response Details
The CapturingInterceptor can be used to capture the details of the last request that was sent by the client, as well as the corresponding response that was received.
* [CapturingInterceptor JavaDoc](/apidocs/hapi-fhir-client/ca/uhn/fhir/rest/client/interceptor/CapturingInterceptor.html)
* [CapturingInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/CapturingInterceptor.java)
A separate but related interceptor called ThreadLocalCapturingInterceptor also captures request/response pairs but stores these in a Java ThreadLocal so it is suitable for use in multithreaded environments.
* [ThreadLocalCapturingInterceptor JavaDoc](/apidocs/hapi-fhir-client/ca/uhn/fhir/rest/client/interceptor/ThreadLocalCapturingInterceptor.html)
* [ThreadLocalCapturingInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/ThreadLocalCapturingInterceptor.java)

View File

@ -1,3 +1,110 @@
# Built-In Server Interceptors # Built-In Server Interceptors
<a name="logging_server_requests"/> This page describes some server interceptors that are shipped with HAPI FHIR out of the box. Of course, you are also welcome to create your own.
<a name="logging_interceptor"/>
# Logging: Logging Interceptor
The LoggingInterceptor can be used to generate a new log line (via SLF4j) for each incoming request. LoggingInterceptor provides a flexible message format that can be used to provide a customized level detail about each incoming request.
* [LoggingInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.html)
* [LoggingInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java)
The following example shows how to register a logging interceptor within a FHIR RESTful server.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ServletExamples.java|loggingInterceptor}}
```
This interceptor will then produce output similar to the following:
```bash
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[]
```
# Response Customizing: Syntax Highlighting
The ResponseHighlighterInterceptor detects when a request is coming from a browser and returns HTML with syntax highlighted XML/JSON instead of just the raw text. In other words, if a user uses a browser to request `http://foo/Patient/1` by typing this address into their URL bar, they will get nice formatted HTML back with a human readable version of the content. This is particularly helpful for testers and public/development APIs where users are likely to invoke the API directly to see how it works.
* [ResponseHighlighterInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.html)
* [ResponseHighlighterInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java)
To see an example of how the output of this interceptor looks, see our demo server using the following example query: [http://hapi.fhir.org/baseR4/Patient](http://hapi.fhir.org/baseR4/Patient). The HTML view you see no that page with colour and indenting is provided by ResponseHighlighterInterceptor. Without this interceptor the respnose will simply by raw JSON/XML (as it will also be with this interceptor if the request is not coming from a browser, or is invoked by JavaScript).
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ServletExamples.java|responseHighlighterInterceptor}}
```
# Response Customizing: Exception Handling
The ExceptionHandlingInterceptor can be used to customize what is returned to the client and what is logged when the server throws an exception for any reason (including routine things like UnprocessableEntityExceptions thrown as a matter of normal processing in a create method, but also including unexpected exceptions thrown by client code).
* [ExceptionHandlingInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.html)
* [ExceptionHandlingInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java)
The following example shows how to register the ExceptionHandlingInterceptor.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ServletExamples.java|exceptionInterceptor}}
```
# Validation: Request and Response Validation
HAPI FHIR provides a pair of interceptors that can be used to validate incoming requests received by the server, as well as outgoing responses generated by the server.
The RequestValidatingInterceptor and ResponseValidatingInterceptor can be used to perform validation of resources on their way into and out of the server respectively.
* [RequestValidatingInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.html)
* [RequestValidatingInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.java)
* [ResponseValidatingInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/ResponseValidatingInterceptor.html)
* [ResponseValidatingInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseValidatingInterceptor.java)
The RequestValidatingInterceptor looks at resources coming into the server (e.g. for create, update, $operations, transactions, etc.) and validates them. The ResponseValidatingInterceptor looks at resources being returned by the server (e.g. for read, search, $operations, etc.) and validates them.
These interceptors can be configured to add headers to the response, fail the response (returning an HTTP 422 and throwing an exception in the process), or to add to the OperationOutcome returned by the server.
See [Profile Validator](/docs/validation/profile_validator.html) for information on how validation works in HAPI FHIR.
The following example shows how to register this interceptor within a HAPI FHIR REST server.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ServletExamples.java|validatingInterceptor}}
```
# Security: CORS
HAPI FHIR includes an interceptor which can be used to implement CORS support on your server. See [Server CORS Documentation](/docs/security/cors.html#cors_interceptor) for information on how to use this interceptor.
# Security: Authorization
HAPI FHIR provides a powerful interceptor that can be used to implement user- and system-level authorization rules that are aware of FHIR semantics. See [Authorization](/docs/security/authorization_interceptor.html) for more information.
# Security: Consent
HAPI FHIR provides an interceptor that can be used to implement consent rules and directives. See [Consent Interceptor](/docs/security/consent_interceptor.html) for more information.
# Security: Search Narrowing
HAPI FHIR provides an interceptor that can be used to implement consent rules and directives. See [Consent Interceptor](/docs/security/consent_interceptor.html) for more information.
# Security: Rejecting Unsupported HTTP Verbs
Some security audit tools require that servers return an HTTP 405 if an unsupported HTTP verb is received (e.g. TRACE). The BanUnsupportedHttpMethodsInterceptor can be used to accomplish this.
* [BanUnsupportedHttpMethodsInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/BanUnsupportedHttpMethodsInterceptor.html)
* [BanUnsupportedHttpMethodsInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/BanUnsupportedHttpMethodsInterceptor.java)
# Request Pre-Processing: Override Meta.source
If you wish to override the value of `Resource.meta.source` using the value supplied in an HTTP header, you can use the CaptureResourceSourceFromHeaderInterceptor to accomplish this.
* [CaptureResourceSourceFromHeaderInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/CaptureResourceSourceFromHeaderInterceptor.html)
* [CaptureResourceSourceFromHeaderInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/CaptureResourceSourceFromHeaderInterceptor.java)

View File

@ -1,3 +1,11 @@
# Client Interceptors # Client Interceptors
<a name="req_resp_logging"/> Client interceptors may be used to examine requests and responses before and after they are sent to the remote server.
# Registering Client Interceptors
Interceptors for the client are registered against individual client instances, as shown in the example below.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/Interceptors.java|registerClient}}
```

View File

@ -0,0 +1,9 @@
# Client Pointcuts
The following diagram shows the request processing pipeline for processing a client request.
<a href="/hapi-fhir/docs/images/interceptors-client-pipeline.svg" target="_blank">Expand</a>
<img src="/hapi-fhir/docs/images/interceptors-client-pipeline.svg" alt="Client Pipeline"/>
See the [Pointcut](/apidocs/hapi-fhir-base/ca/uhn/fhir/interceptor/api/Pointcut.html) JavaDoc for details about these pointcuts, what arguments are allowed etc.

View File

@ -1,10 +1,46 @@
# Interceptors: Overview # Interceptors: Overview
HAPI FHIR 3.8.0 introduced a new interceptor framework that is used across the entire library. In previous versions of HAPI FHIR, a "Server Interceptor" framework existed and a separate "Client Interceptor" framework existed. These have now been combined into a single unified (and very powerful) framework. HAPI FHIR 3.8.0 introduced a new interceptor framework that is used across the entire library. In previous versions of HAPI FHIR, a "Server Interceptor" framework existed and a separate "Client Interceptor" framework existed. These have now been combined into a single unified (and much more powerful) framework.
Interceptor classes may "hook into" various points in the processing chain in both the client and the server. Interceptor classes may "hook into" various points in the processing chain in both the client and the server. The interceptor framework has been designed do be flexible enough to hook into almost every part of the library. When trying to figure out "how would I make HAPI FHIR do X", the answer is very often to create an interceptor.
The following example shows a very simple interceptor example.
```java ```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/MyInterceptor.java|sampleClass}} {{snippet:classpath:/ca/uhn/hapi/fhir/docs/Interceptors.java|sampleClass}}
```
## Interceptors Glossary
The HAPI FHIR interceptor framework uses a couple of key terms that are important to understand as you read the rest of this documentation:
* **Interceptor** &ndash; A class such as the example above that has one or more *Hook* methods on it. An optional marker annotation called [@Interceptor](/apidocs/hapi-fhir-base/ca/uhn/fhir/interceptor/api/Interceptor.html) exists and can be used, but is not required.
* **Hook** &ndash; An individual interceptor method that is invoked in response to a specific action taking place in the HAPI FHIR framework. Hook methods must be annotated with the [@Hook](/apidocs/hapi-fhir-base/ca/uhn/fhir/interceptor/api/Hook.html) annotation.
* **Pointcut** &ndash; A pointcut is a specific point in the HAPI FHIR processing pipeline that is being intercepted. Each Hook Method must declare which pointcut it is intercepting. All available pointcuts are defined by HAPI FHIR in the [Pointcut](/apidocs/hapi-fhir-base/ca/uhn/fhir/interceptor/api/Pointcut.html) enum.
* **Hook Params** &ndash; Every Pointcut defines a list of parameters that may be passed to a Hook Method for a given Pointcut. For example, the definition of the [SERVER_INCOMING_REQUEST_PRE_HANDLED](/apidocs/hapi-fhir-base/ca/uhn/fhir/interceptor/api/Pointcut.html#SERVER_INCOMING_REQUEST_PRE_HANDLED) pointcut used in the example above defines 4 parameter types. A hook method for that parameter type can include any/all of these parameter types in its parameter list. The example above is only using one.
# Creating Interceptors
Creating your own interceptors is easy. Custom interceptor classes do not need to extend any other class or implement any particular interface.
* They must have at least one method annotated with the [@Hook](/apidocs/hapi-fhir-base/ca/uhn/fhir/interceptor/api/Pointcut.html) annotation.
* The method must have an appropriate return value for the chosen [Pointcut](/apidocs/hapi-fhir-base/ca/uhn/fhir/interceptor/api/Pointcut.html).
* The method may have any of the parameters specified for the given [Pointcut](/apidocs/hapi-fhir-base/ca/uhn/fhir/interceptor/api/Pointcut.html).
The following example shows a simple request counter interceptor.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/RequestCounterInterceptor.java|interceptor}}
```
The following example shows an exception handling interceptor which overrides the built-in exception handling by providing a custom response.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/RequestExceptionInterceptor.java|interceptor}}
``` ```

View File

@ -1 +1,8 @@
# Server Interceptors # Server Interceptors
There are many different Pointcuts available to server developers. In general, a server can be thought of as playing two roles: Server and Storage.
In the case of a Plain Server, HAPI FHIR itself performs the role of the Server and your [Resource Provider](/docs/server_plain/resource_providers.html) classes perform the role of Storage.
In the case of a JPA Server, HAPI FHIR itself performs both roles. This means that **SERVER_xxx** Pointcuts may be intercepted by interceptors on any HAPI FHIR server. However, if you want to intercept **STORAGE_xxx** Pointcuts on a plain server, you will need to trigger them yourself.

View File

@ -0,0 +1,14 @@
# Plain Server Pointcuts
The following diagram shows the request processing pipeline for processing a server request. You may click on the diagram to enlarge it.
<a href="/hapi-fhir/docs/images/interceptors-server-pipeline.svg" target="_blank">Expand</a>
<img src="/hapi-fhir/docs/images/interceptors-server-pipeline.svg" alt="Server Pipeline"/>
# Storage / JPA Server Pointcuts
The following diagram shows the request processing pipeline for processing a server request. You may click on the diagram to enlarge it.
<a href="/hapi-fhir/docs/images/interceptors-server-jpa-pipeline.svg" target="_blank">Expand</a>
<img src="/hapi-fhir/docs/images/interceptors-server-jpa-pipeline.svg" alt="Server Pipeline"/>

View File

@ -2,17 +2,18 @@
The following table shows the modules that make up the HAPI FHIR library. The following table shows the modules that make up the HAPI FHIR library.
<table class="table table-condensed"> <table class="modules-table">
<tbody> <tbody>
<tr> <tr>
<td colspan="3" style="text-align: center; font-size: 1.2em; background: #DDE; padding: 3px;"> <td colspan="3" class="modules-section-row">
Core Libraries Core Libraries
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-base</td> <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-base</td>
<td> <td>
<a href="/hapi-fhir/apidocs/hapi-fhir-base/">JavaDoc</a> <a href="/hapi-fhir/apidocs/hapi-fhir-base/">JavaDoc&nbsp;&rsaquo;</a>
<a href="https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-base">Sources&nbsp;&rsaquo;</a>
</td> </td>
<td> <td>
This is the core HAPI FHIR library and is always required in order to use This is the core HAPI FHIR library and is always required in order to use
@ -20,22 +21,14 @@ The following table shows the modules that make up the HAPI FHIR library.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="text-align: center; font-size: 1.2em; background: #DDE; padding: 3px;" colspan="3"> <td class="modules-section-row" colspan="3">
Structures Structures
</td> </td>
</tr> </tr>
<tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-structures-dstu (retired)</td>
<td>
</td>
<td>
This module contained FHIR DSTU1 model classes. It was retired in HAPI FHIR 3.0.0.
</td>
</tr>
<tr> <tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-structures-dstu2</td> <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-structures-dstu2</td>
<td> <td>
<a href="/hapi-fhir/apidocs/hapi-fhir-structures-dstu2/">JavaDoc</a> <a href="/hapi-fhir/apidocs/hapi-fhir-structures-dstu2/">JavaDoc&nbsp;&rsaquo;</a>
</td> </td>
<td> <td>
This module contains FHIR DSTU2 model classes. This module contains FHIR DSTU2 model classes.
@ -44,7 +37,7 @@ The following table shows the modules that make up the HAPI FHIR library.
<tr> <tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-structures-hl7org-dstu2</td> <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-structures-hl7org-dstu2</td>
<td> <td>
<a href="/hapi-fhir/apidocs/hapi-fhir-structures-hl7org-dstu2/">JavaDoc</a> <a href="/hapi-fhir/apidocs/hapi-fhir-structures-hl7org-dstu2/">JavaDoc&nbsp;&rsaquo;</a>
</td> </td>
<td> <td>
This module contains alternate FHIR DSTU2 model classes. The HAPI FHIR and FHIR "Java Reference This module contains alternate FHIR DSTU2 model classes. The HAPI FHIR and FHIR "Java Reference
@ -58,7 +51,7 @@ The following table shows the modules that make up the HAPI FHIR library.
<tr> <tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-structures-dstu3</td> <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-structures-dstu3</td>
<td> <td>
<a href="/hapi-fhir/apidocs/hapi-fhir-structures-dstu3/">JavaDoc</a> <a href="/hapi-fhir/apidocs/hapi-fhir-structures-dstu3/">JavaDoc&nbsp;&rsaquo;</a>
</td> </td>
<td> <td>
This module contains FHIR DSTU3 model classes. This module contains FHIR DSTU3 model classes.
@ -67,7 +60,7 @@ The following table shows the modules that make up the HAPI FHIR library.
<tr> <tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-structures-r4</td> <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-structures-r4</td>
<td> <td>
<a href="/hapi-fhir/apidocs/hapi-fhir-structures-r4/">JavaDoc</a> <a href="/hapi-fhir/apidocs/hapi-fhir-structures-r4/">JavaDoc&nbsp;&rsaquo;</a>
</td> </td>
<td> <td>
This module contains FHIR R4 model classes. This module contains FHIR R4 model classes.
@ -76,7 +69,7 @@ The following table shows the modules that make up the HAPI FHIR library.
<tr> <tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-structures-r5</td> <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-structures-r5</td>
<td> <td>
<a href="/hapi-fhir/apidocs/hapi-fhir-structures-r5/">JavaDoc</a> <a href="/hapi-fhir/apidocs/hapi-fhir-structures-r5/">JavaDoc&nbsp;&rsaquo;</a>
</td> </td>
<td> <td>
This module contains FHIR R5 model classes. This module contains FHIR R5 model classes.
@ -85,21 +78,32 @@ The following table shows the modules that make up the HAPI FHIR library.
<tr> <tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-converter</td> <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-converter</td>
<td> <td>
<a href="/hapi-fhir/apidocs/hapi-fhir-converter/">JavaDoc</a> <a href="/hapi-fhir/docs/model/converter.html">Documentation&nbsp;&rsaquo;</a><br/>
<a href="/hapi-fhir/apidocs/hapi-fhir-converter/">JavaDoc&nbsp;&rsaquo;</a>
</td> </td>
<td> <td>
This module contains converters for converting between FHIR versions. This module contains converters for converting between FHIR versions.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="text-align: center; font-size: 1.2em; background: #DDE; padding: 3px;" colspan="3">Client <td style="font-weight: bold; white-space: nowrap;"><i>hapi-fhir-structures-dstu (retired)</i></td>
<td>
</td>
<td>
This module contained FHIR DSTU1 model classes. It was retired in HAPI FHIR 3.0.0.
</td>
</tr>
<tr>
<td class="modules-section-row" colspan="3">Client
Framework Framework
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-client</td> <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-client</td>
<td> <td>
<a href="/hapi-fhir/apidocs/hapi-fhir-client/">JavaDoc</a> <a href="/hapi-fhir/docs/client/">Documentation&nbsp;&rsaquo;</a><br/>
<a href="/hapi-fhir/apidocs/hapi-fhir-client/">JavaDoc&nbsp;&rsaquo;</a>
<a href="https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-client">Sources&nbsp;&rsaquo;</a>
</td> </td>
<td> <td>
This module contains the core FHIR client framework, including an This module contains the core FHIR client framework, including an
@ -111,6 +115,7 @@ The following table shows the modules that make up the HAPI FHIR library.
<tr> <tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-client-okhttp</td> <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-client-okhttp</td>
<td> <td>
<a href="https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-client-okhttp">Sources&nbsp;&rsaquo;</a>
</td> </td>
<td> <td>
This module contains an alternate HTTP implementation based on This module contains an alternate HTTP implementation based on
@ -120,6 +125,8 @@ The following table shows the modules that make up the HAPI FHIR library.
<tr> <tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-android</td> <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-android</td>
<td> <td>
<a href="/hapi-fhir/docs/android/">Documentation&nbsp;&rsaquo;</a>
<a href="https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-android/">Sources&nbsp;&rsaquo;</a>
</td> </td>
<td> <td>
This module contains the Android HAPI FHIR framework, which is a FHIR This module contains the Android HAPI FHIR framework, which is a FHIR
@ -127,13 +134,14 @@ The following table shows the modules that make up the HAPI FHIR library.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="text-align: center; font-size: 1.2em; background: #DDE; padding: 3px;" colspan="3"> <td class="modules-section-row" colspan="3">
Validation Validation
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-validation</td> <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-validation</td>
<td> <td>
<a href="/hapi-fhir/docs/validation/">Documentation&nbsp;&rsaquo;</a>
</td> </td>
<td> <td>
This module contains the FHIR Profile Validator, which is used to This module contains the FHIR Profile Validator, which is used to
@ -187,12 +195,14 @@ The following table shows the modules that make up the HAPI FHIR library.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="text-align: center; font-size: 1.2em; background: #DDE; padding: 3px;" colspan="3">Server</td> <td class="modules-section-row" colspan="3">Server</td>
</tr> </tr>
<tr> <tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-server</td> <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-server</td>
<td> <td>
<a href="/hapi-fhir/apidocs/hapi-fhir-server/">JavaDoc</a> <a href="/hapi-fhir/docs/server_plain/">Documentation&nbsp;&rsaquo;</a><br/>
<a href="/hapi-fhir/apidocs/hapi-fhir-server/">JavaDoc&nbsp;&rsaquo;</a>
<a href="https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-server/">Sources&nbsp;&rsaquo;</a>
</td> </td>
<td> <td>
This module contains the HAPI FHIR Server framework, which can be used to This module contains the HAPI FHIR Server framework, which can be used to
@ -202,7 +212,9 @@ The following table shows the modules that make up the HAPI FHIR library.
<tr> <tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-jpaserver-base</td> <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-jpaserver-base</td>
<td> <td>
<a href="/hapi-fhir/apidocs/hapi-fhir-jpaserver-base/">JavaDoc</a> <a href="/hapi-fhir/docs/server_jpa/">Documentation&nbsp;&rsaquo;</a><br/>
<a href="/hapi-fhir/apidocs/hapi-fhir-jpaserver-base/">JavaDoc&nbsp;&rsaquo;</a>
<a href="https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-jpaserver-base/">Sources&nbsp;&rsaquo;</a>
</td> </td>
<td> <td>
This module contains the HAPI FHIR "JPA Server", which is a complete This module contains the HAPI FHIR "JPA Server", which is a complete
@ -213,6 +225,8 @@ The following table shows the modules that make up the HAPI FHIR library.
<tr> <tr>
<td style="font-weight: bold; white-space: nowrap;">hapi-fhir-testpage-overlay</td> <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-testpage-overlay</td>
<td> <td>
<a href="/hapi-fhir/docs/server_plain/web_testpage_overlay.html">Documentation&nbsp;&rsaquo;</a><br/>
<a href="https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-testpage-overlay/">Sources&nbsp;&rsaquo;</a>
</td> </td>
<td> <td>
This module contains the web based "testpage overlay", which is the This module contains the web based "testpage overlay", which is the

View File

@ -1,4 +1,3 @@
# HL7 FHIR Converter # HL7 FHIR Converter
Beginning in HAPI FHIR 2.3, a new module called `hapi-fhir-converter` has been added to the project. This is an <b>experimental feature</b> so use it with caution! Beginning in HAPI FHIR 2.3, a new module called `hapi-fhir-converter` has been added to the project. This is an <b>experimental feature</b> so use it with caution!

View File

@ -4,14 +4,38 @@ HAPI FHIR has built-in support for the FHIR [JSON](http://hl7.org/fhir/json.html
A built in parser can be used to convert HAPI FHIR Java objects into a serialized form, and to parse serialized data into Java objects. Note that unlike some other frameworks, HAPI FHIR does not have separate parsers and serializers. Both of these functions are handled by a single object called the **Parser**. A built in parser can be used to convert HAPI FHIR Java objects into a serialized form, and to parse serialized data into Java objects. Note that unlike some other frameworks, HAPI FHIR does not have separate parsers and serializers. Both of these functions are handled by a single object called the **Parser**.
# Serializing # Parsing (aka Deserializing)
As with many parts of the HAPI FHIR API, parsing beginis with a [FhirContext](/apidocs/hapi-fhr-base/ca/uhn/fhir/context/FhirContext.html) object. The FhirContext can be used to request an [IParser](/apidocs/hapi-fhir-base/ca/uhn/fhir/parser/IParser.html) for your chosen encodng style that is then used to serialize. As with many parts of the HAPI FHIR API, parsing begins with a [FhirContext](/apidocs/hapi-fhir-base/ca/uhn/fhir/context/FhirContext.html) object. The FhirContext can be used to request an [IParser](/apidocs/hapi-fhir-base/ca/uhn/fhir/parser/IParser.html) for your chosen encodng style that is then used to parse.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/Parser.java|parsing}}
```
# Encoding (aka Serializing)
As with many parts of the HAPI FHIR API, parsing begins with a [FhirContext](/apidocs/hapi-fhir-base/ca/uhn/fhir/context/FhirContext.html) object. The FhirContext can be used to request an [IParser](/apidocs/hapi-fhir-base/ca/uhn/fhir/parser/IParser.html) for your chosen encodng style that is then used to serialize.
The following example shows a JSON Parser being used to serialize a FHIR resource. The following example shows a JSON Parser being used to serialize a FHIR resource.
```java ```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/Parser.java|createParser}} {{snippet:classpath:/ca/uhn/hapi/fhir/docs/Parser.java|encoding}}
``` ```
HapiWorkerContext ## Pretty Printing
By default, the parser will output in condensed form, with no newlines or indenting. This is good for machine-to-machine communication since it reduces the amount of data to be transferred but it is harder to read. To enable pretty printed outout:
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/Parser.java|encodingPretty}}
```
## Encoding Configuration
There are plenty of other options too that can be used to control the output by the parser. A few examples are shown below. See the [IParser](/apidocs/hapi-fhir-base/ca/uhn/fhir/parser/IParser.html) JavaDoc for more information.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/Parser.java|encodingConfig}}
```

View File

@ -1,9 +1,12 @@
# Authorization Interceptor # Authorization Interceptor
HAPI FHIR 1.5 introduced a new interceptor: [AuthorizationInterceptor](/hapi-fhir/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.html). HAPI FHIR 1.5 introduced a new interceptor: [AuthorizationInterceptor](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.html).
This interceptor can help with the complicated task of determining whether a user has the appropriate permission to perform a given task on a FHIR server. This is done by declaring a set of rules that can selectively allow (whitelist) and/or selectively block (blacklist) requests. This interceptor can help with the complicated task of determining whether a user has the appropriate permission to perform a given task on a FHIR server. This is done by declaring a set of rules that can selectively allow (whitelist) and/or selectively block (blacklist) requests.
* [AuthorizationInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.html)
* [AuthorizationInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java)
<p class="helpInfoCalloutBox"> <p class="helpInfoCalloutBox">
AuthorizationInterceptor has been well tested, but it is impossible to predict every scenario and environment in which HAPI FHIR will be used. Use with caution, and do lots of testing! We welcome feedback and suggestions on this feature. Please get in touch if you'd like to help test, have suggestions, etc. AuthorizationInterceptor has been well tested, but it is impossible to predict every scenario and environment in which HAPI FHIR will be used. Use with caution, and do lots of testing! We welcome feedback and suggestions on this feature. Please get in touch if you'd like to help test, have suggestions, etc.
</p> </p>

View File

@ -4,6 +4,9 @@ HAPI FHIR 4.0.0 introduced a new interceptor, the [ConsentInterceptor](/hapi-fhi
The consent interceptor may be used to examine client requests to apply consent directives and create audit trail events. Like the AuthorizationInterceptor above, this interceptor is not a complete working solution, but instead is a framework designed to make it easier to implement local policies. The consent interceptor may be used to examine client requests to apply consent directives and create audit trail events. Like the AuthorizationInterceptor above, this interceptor is not a complete working solution, but instead is a framework designed to make it easier to implement local policies.
* [ConsentInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/consent/ConsentInterceptor.html)
* [ConsentInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentInterceptor.java)
The consent interceptor has several primary purposes: The consent interceptor has several primary purposes:
** It can reject a resource from being disclosed to the user by examining it while calculating search results. This calculation is performed very early in the process of building search results, in order to ensure that in many cases the user is unaware that results have been removed. ** It can reject a resource from being disclosed to the user by examining it while calculating search results. This calculation is performed very early in the process of building search results, in order to ensure that in many cases the user is unaware that results have been removed.

View File

@ -1,20 +1,21 @@
# CORS # CORS (Cross-Origin Resource Sharing)
<p class="doc_info_bubble">
Note that in previous revisions of this document we recommended using the <a href="https://github.com/ebay/cors-filter">eBay CORS Filter</a>, but as of 2016 the eBay filter is no longer being maintained and contains known bugs. We now recommend against using this filter.
</p>
If you are intending to support JavaScript clients in your server application, you will generally need to enable Cross Origin Resource Sharing (CORS). There are a number of ways of supporting this, so two are shown here: If you are intending to support JavaScript clients in your server application, you will generally need to enable Cross Origin Resource Sharing (CORS). There are a number of ways of supporting this, so two are shown here:
* An approach using a HAPI FHIR Server Interceptor (Requires SpringFramework) * An approach using a HAPI FHIR Server Interceptor (Requires SpringFramework)
* An approach using a servlet Filter (Container Specific) * An approach using a servlet Filter (Container Specific)
## HAPI FHIR Server Interceptor <a name="cors_interceptor"/>
# HAPI FHIR CORS Interceptor
The HAPI FHIR server framework includes an interceptor that can be used to provide CORS functionality on your server. This mechanism is nice because it relies purely on Java configuration (no messing around with web.xml files). HAPI's interceptor is a thin wrapper around Spring Framework's CorsProcessor class, so it requires Spring to be present on your classpath. The HAPI FHIR server framework includes an interceptor that can be used to provide CORS functionality on your server. This mechanism is nice because it relies purely on Java configuration (no messing around with web.xml files). HAPI's interceptor is a thin wrapper around Spring Framework's CorsProcessor class, so it requires Spring to be present on your classpath.
Spring is generally unlikely to conflict with other libraries so it is usually safe to add it to your classpath, but it is a fairly large library so if size is a concern you might opt to use a filter instead. Spring is generally unlikely to conflict with other libraries so it is usually safe to add it to your classpath, but it is a fairly large library so if size is a concern you might opt to use a filter instead.
* [CorsInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/CorsInterceptor.html)
* [CorsInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/CorsInterceptor.java)
The following steps outline how to enable HAPI's CorsInterceptor: The following steps outline how to enable HAPI's CorsInterceptor:
Add the following dependency to your POM. Note the exclusion of commons-logging, as we are using SLF4j without commons-logging in most of our examples. If your application uses commons-logging you don't need to exclude that dependency. Add the following dependency to your POM. Note the exclusion of commons-logging, as we are using SLF4j without commons-logging in most of our examples. If your application uses commons-logging you don't need to exclude that dependency.
@ -39,7 +40,11 @@ In your server's initialization method, create and register a CorsInterceptor:
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ServletExamples.java|corsInterceptor}} {{snippet:classpath:/ca/uhn/hapi/fhir/docs/ServletExamples.java|corsInterceptor}}
``` ```
## Add the Dependency # CORS Servlet Filter
<p class="doc_info_bubble">
Note that in previous revisions of HAPI FHIR documentation we recommended using the <a href="https://github.com/ebay/cors-filter">eBay CORS Filter</a>, but as of 2016 the eBay filter is no longer being maintained and contains known bugs. We now recommend against using this filter.
</p>
The following examples show how to use the Apache Tomcat CorsFilter to enable CORS support. The filter being used (`org.apache.catalina.filters.CorsFilter`) is bundled with Apache Tomcat so if you are deploying to that server you can use the filter. The following examples show how to use the Apache Tomcat CorsFilter to enable CORS support. The filter being used (`org.apache.catalina.filters.CorsFilter`) is bundled with Apache Tomcat so if you are deploying to that server you can use the filter.

View File

@ -2,6 +2,9 @@
HAPI FHIR 3.7.0 introduced a new interceptor, the [SearchNarrowingInterceptor](/hapi-fhir/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.html). HAPI FHIR 3.7.0 introduced a new interceptor, the [SearchNarrowingInterceptor](/hapi-fhir/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.html).
* [SearchNarrowingInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.html)
* [SearchNarrowingInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.java)
This interceptor is designed to be used in conjunction with AuthorizationInterceptor. It uses a similar strategy where a dynamic list is built up for each request, but the purpose of this interceptor is to modify client searches that are received (after HAPI FHIR received the HTTP request, but before the search is actually performed) to restrict the search to only search for specific resources or compartments that the user has access to. This interceptor is designed to be used in conjunction with AuthorizationInterceptor. It uses a similar strategy where a dynamic list is built up for each request, but the purpose of this interceptor is to modify client searches that are received (after HAPI FHIR received the HTTP request, but before the search is actually performed) to restrict the search to only search for specific resources or compartments that the user has access to.
This could be used, for example, to allow the user to perform a search for: This could be used, for example, to allow the user to perform a search for:
@ -13,7 +16,7 @@ http://baseurl/Observation?category=laboratory
...and then receive results as though they had requested: ...and then receive results as though they had requested:
```url ```url
http://baseurl/Observation?subject=Patient/123&amp;category=laboratory http://baseurl/Observation?subject=Patient/123&category=laboratory
``` ```
An example of this interceptor follows: An example of this interceptor follows:

View File

@ -1,3 +1,6 @@
# Getting Started with HAPI FHIR JPA Server # Getting Started with HAPI FHIR JPA Server
The recommended way to get started with the JPA Server is to use the [hapi-fhir-jpaserver-starter](https://github.com/hapifhir/hapi-fhir-jpaserver-starter The recommended way to get started with the HAPI FHIR JPA server module is
to begin with the starter project. This project can be found at the following link: [https://github.com/hapifhir/hapi-fhir-jpaserver-starter](https://github.com/hapifhir/hapi-fhir-jpaserver-starter)
This project is a fully contained FHIR server, supporting all standard operations (read/create/delete/etc). It bundles an embedded instance of the [H2 Java Database](http://h2database.com) so that the server can run without depending on any external database, but it can also be configured to use an installation of Oracle, Postgres, etc.

View File

@ -4,13 +4,6 @@ The HAPI FHIR [Plain Server](/hapi-fhir/docs/server_plain/introduction.html) mod
HAPI also provides a persistence module which can be used to provide a complete RESTful server implementation, backed by a database of your choosing. This module uses the [JPA 2.0](http://en.wikipedia.org/wiki/Java_Persistence_API) API to store data in a database without depending on any specific database technology. HAPI also provides a persistence module which can be used to provide a complete RESTful server implementation, backed by a database of your choosing. This module uses the [JPA 2.0](http://en.wikipedia.org/wiki/Java_Persistence_API) API to store data in a database without depending on any specific database technology.
# Getting Started
The recommended way to get started with HAPI's JPA server module is
to begin with the starter project. This project can be found at the following link: [https://github.com/hapifhir/hapi-fhir-jpaserver-starter](https://github.com/hapifhir/hapi-fhir-jpaserver-starter)
This project is a fully contained FHIR server, supporting all standard operations (read/create/delete/etc). It bundles an embedded instance of the <a href="http://h2database.com">H2 Java Database</a> so that the server can run without depending on any external database, but it can also be configured to use an installation of Oracle, Postgres, etc.
# Configuration Options # Configuration Options
## External/Absolute Resource References ## External/Absolute Resource References

View File

@ -32,6 +32,9 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.method.BaseResourceReturningMethodBinding; import ca.uhn.fhir.rest.server.method.BaseResourceReturningMethodBinding;
@ -48,13 +51,14 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
import ca.uhn.fhir.util.OperationOutcomeUtil; import ca.uhn.fhir.util.OperationOutcomeUtil;
public class ExceptionHandlingInterceptor extends InterceptorAdapter { @Interceptor
public class ExceptionHandlingInterceptor {
public static final String PROCESSING = Constants.OO_INFOSTATUS_PROCESSING; public static final String PROCESSING = Constants.OO_INFOSTATUS_PROCESSING;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionHandlingInterceptor.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionHandlingInterceptor.class);
private Class<?>[] myReturnStackTracesForExceptionTypes; private Class<?>[] myReturnStackTracesForExceptionTypes;
@Override @Hook(Pointcut.SERVER_HANDLE_EXCEPTION)
public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException { public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException {
Closeable writer = (Closeable) handleException(theRequestDetails, theException); Closeable writer = (Closeable) handleException(theRequestDetails, theException);
writer.close(); writer.close();
@ -100,7 +104,7 @@ public class ExceptionHandlingInterceptor extends InterceptorAdapter {
} }
@Override @Hook(Pointcut.SERVER_PRE_PROCESS_OUTGOING_EXCEPTION)
public BaseServerResponseException preProcessOutgoingException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest) throws ServletException { public BaseServerResponseException preProcessOutgoingException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest) throws ServletException {
BaseServerResponseException retVal; BaseServerResponseException retVal;
if (theException instanceof DataFormatException) { if (theException instanceof DataFormatException) {

View File

@ -41,7 +41,7 @@ public class CompressOutgoingContentInterceptorTest {
@Test @Test
public void testCreate() throws Exception { public void testCreate() {
ourClient.registerInterceptor(new GZipContentInterceptor()); ourClient.registerInterceptor(new GZipContentInterceptor());
Patient p = new Patient(); Patient p = new Patient();

View File

@ -0,0 +1,116 @@
package ca.uhn.fhir.rest.client.interceptor;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.util.TestUtil;
import com.google.common.collect.Lists;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.hamcrest.Matchers;
import org.hl7.fhir.r4.model.Bundle;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class AdditionalHeadersInterceptorTest {
private FhirContext ctx;
private HttpClient httpClient;
private HttpResponse httpResponse;
// atom-document-large.xml
@Before
public void before() {
ctx = FhirContext.forR4();
httpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ctx.getRestfulClientFactory().setHttpClient(httpClient);
ctx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
httpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
@Test
public void testNoHeaders() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML_NEW + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(createBundle()), StandardCharsets.UTF_8));
IGenericClient client = ctx.newRestfulGenericClient("http://foo");
client.registerInterceptor(new AdditionalRequestHeadersInterceptor());
client.search().forResource("Patient").returnBundle(Bundle.class).execute();
assertEquals(HttpGet.class, capt.getValue().getClass());
HttpGet get = (HttpGet) capt.getValue();
assertEquals("http://foo/Patient", get.getURI().toString());
}
@Test
public void testManyHeaders() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML_NEW + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(createBundle()), StandardCharsets.UTF_8));
IGenericClient client = ctx.newRestfulGenericClient("http://foo");
Map<String, List<String>> map = new HashMap<>();
map.put("X-0", Lists.newArrayList("X-0-VAL"));
AdditionalRequestHeadersInterceptor interceptor = new AdditionalRequestHeadersInterceptor(map);
client.registerInterceptor(interceptor);
interceptor.addHeaderValue("X-1", "X-1-VAL");
interceptor.addAllHeaderValues("X-2", Lists.newArrayList("X-2-VAL1", "X-2-VAL2"));
client.search().forResource("Patient").returnBundle(Bundle.class).execute();
assertEquals(HttpGet.class, capt.getValue().getClass());
HttpGet get = (HttpGet) capt.getValue();
assertEquals("http://foo/Patient", get.getURI().toString());
assertThat(Arrays.asList(get.getHeaders("X-0")), Matchers.contains("X-0-VAL"));
assertThat(Arrays.asList(get.getHeaders("X-1")), Matchers.contains("X-1-VAL"));
assertThat(Arrays.asList(get.getHeaders("X-2")), Matchers.contains("X-2-VAL1", "X-2-VAL2"));
}
private String createBundle() {
return ctx.newXmlParser().encodeResourceToString(new Bundle().setTotal(0));
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -58,6 +58,17 @@
The constructor for Verdict.java was inadvertantly made private, preventing custom The constructor for Verdict.java was inadvertantly made private, preventing custom
rules from being written. Thanks to Jafer Khan for the pull request! rules from being written. Thanks to Jafer Khan for the pull request!
</action> </action>
<action type="change">
The
<![CDATA[
<code>IRestfulClient#registerInterceptor</code>
and
<code>IRestfulClient#unregisterInterceptor</code>
]]>
methods now take Object as an argument instead of IClientInterceptor, allowing
client interceptors to now be migrated to the new interceptor
framework.
</action>
</release> </release>
<release version="4.1.0" date="2019-11-13" description="Jitterbug"> <release version="4.1.0" date="2019-11-13" description="Jitterbug">
<action type="add"> <action type="add">

View File

@ -1,105 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd"> xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
<properties> <!-- this content has been migrated -->
<title>RESTful Client</title>
<author email="jamesagnew@users.sourceforge.net">James Agnew</author> <properties>
</properties> <title>RESTful Client</title>
<author email="jamesagnew@users.sourceforge.net">James Agnew</author>
<body> </properties>
<section name="Client Interceptors"> <body>
<p> <section name="Client Interceptors">
Both generic clients and annotation-driven clients support
<a href="./apidocs/ca/uhn/fhir/rest/client/IClientInterceptor.html">Client Interceptors</a>, <p>
which may be registered in order to provide specific behaviour to each Both generic clients and annotation-driven clients support
client request. <a href="./apidocs/ca/uhn/fhir/rest/client/IClientInterceptor.html">Client Interceptors</a>,
</p> which may be registered in order to provide specific behaviour to each
client request.
<p> </p>
The following section shows some sample interceptors which may be used.
</p> <p>
The following section shows some sample interceptors which may be used.
<subsection name="Security: HTTP Basic Authorization"> </p>
<p> <subsection name="Security: HTTP Basic Authorization">
The following example shows how to configure your client to
use a specific username and password in every request. <p>
</p> The following example shows how to configure your client to
use a specific username and password in every request.
<macro name="snippet"> </p>
<param name="id" value="security" />
<param name="file" value="examples/src/main/java/example/ClientExamples.java" /> <macro name="snippet">
</macro> <param name="id" value="security" />
<param name="file" value="examples/src/main/java/example/ClientExamples.java" />
</subsection> </macro>
<subsection name="Security: HTTP Bearer Token Authorization"> </subsection>
<p> <subsection name="Security: HTTP Bearer Token Authorization">
The following example shows how to configure your client to
inject a bearer token authorization header into every request. This <p>
is used to satisfy servers which are protected using OAUTH2. The following example shows how to configure your client to
</p> inject a bearer token authorization header into every request. This
is used to satisfy servers which are protected using OAUTH2.
<macro name="snippet"> </p>
<param name="id" value="securityBearer" />
<param name="file" value="examples/src/main/java/example/ClientExamples.java" /> <macro name="snippet">
</macro> <param name="id" value="securityBearer" />
<param name="file" value="examples/src/main/java/example/ClientExamples.java" />
</subsection> </macro>
<a name="req_resp_logging"/> </subsection>
<subsection name="Logging: Log Requests and Responses">
<a name="req_resp_logging"/>
<p> <subsection name="Logging: Log Requests and Responses">
The <code>LoggingInterceptor</code> can be used to
log every transaction. The interceptor is flexible and can be configured to be extremely <p>
verbose (logging entire transactions including HTTP headers and payload bodies) The <code>LoggingInterceptor</code> can be used to
or simply to log request URLs, or some combination in between. log every transaction. The interceptor is flexible and can be configured to be extremely
</p> verbose (logging entire transactions including HTTP headers and payload bodies)
or simply to log request URLs, or some combination in between.
<macro name="snippet"> </p>
<param name="id" value="logging" />
<param name="file" value="examples/src/main/java/example/ClientExamples.java" /> <macro name="snippet">
</macro> <param name="id" value="logging" />
<param name="file" value="examples/src/main/java/example/ClientExamples.java" />
</subsection> </macro>
<subsection name="Cookies: Add an HTTP Cookie Header to Client Requests"> </subsection>
<p> <subsection name="Cookies: Add an HTTP Cookie Header to Client Requests">
The <code>CookieInterceptor</code> can be used to
add an HTTP Cookie header to each request created by the client. <p>
</p> The <code>CookieInterceptor</code> can be used to
add an HTTP Cookie header to each request created by the client.
<macro name="snippet"> </p>
<param name="id" value="cookie" />
<param name="file" value="examples/src/main/java/example/ClientExamples.java" /> <macro name="snippet">
</macro> <param name="id" value="cookie" />
<param name="file" value="examples/src/main/java/example/ClientExamples.java" />
</subsection> </macro>
<subsection name="Performance: GZip Outgoing Request Bodies"> </subsection>
<p> <subsection name="Performance: GZip Outgoing Request Bodies">
The <code>GZipContentInterceptor</code> compresses outgoing contents.
With this interceptor, if the client is transmitting resources to the server <p>
(e.g. for a create, update, transaction, etc.) the content will be GZipped The <code>GZipContentInterceptor</code> compresses outgoing contents.
before transmission to the server. With this interceptor, if the client is transmitting resources to the server
</p> (e.g. for a create, update, transaction, etc.) the content will be GZipped
before transmission to the server.
<macro name="snippet"> </p>
<param name="id" value="gzip" />
<param name="file" value="examples/src/main/java/example/ClientExamples.java" /> <macro name="snippet">
</macro> <param name="id" value="gzip" />
<param name="file" value="examples/src/main/java/example/ClientExamples.java" />
</subsection> </macro>
</section> </subsection>
</body> </section>
</document> </body>
</document>

View File

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd"> <document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
<!-- This content is migrated -->
<properties> <properties>
<title>Server Interceptors</title> <title>Server Interceptors</title>
<author email="jamesagnew@users.sourceforge.net">James Agnew</author> <author email="jamesagnew@users.sourceforge.net">James Agnew</author>

View File

@ -94,7 +94,7 @@
<p> <p>
HAPI FHIR 1.5 introduced a new interceptor, the HAPI FHIR 1.5 introduced a new interceptor, the
<a href="./apidocs-server/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.html">AuthorizationInterceptor</a>. <a href="/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.html">AuthorizationInterceptor</a>.
</p> </p>
<p> <p>
This interceptor can help with the complicated task of determining whether a user This interceptor can help with the complicated task of determining whether a user