Add request/response validator interceptors

This commit is contained in:
jamesagnew 2016-01-03 21:52:11 -05:00
parent ccc71c3bd3
commit 685bd9345b
74 changed files with 1536 additions and 2052 deletions

View File

@ -3,11 +3,16 @@ package example;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;
@SuppressWarnings("serial")
public class ServletExamples {
@ -38,6 +43,42 @@ public class ServletExamples {
}
// END SNIPPET: loggingInterceptor
// START SNIPPET: validatingInterceptor
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
public class ValidatingServerWithLogging extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ... define your resource providers here ...
// Create an interceptor to validate incoming requests
RequestValidatingInterceptor requestInterceptor = new RequestValidatingInterceptor();
// Register a validator module (you could also use SchemaBaseValidator and/or SchematronBaseValidator)
requestInterceptor.addValidatorModule(new FhirInstanceValidator());
requestInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
requestInterceptor.setAddResponseHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
requestInterceptor.setResponseHeaderValue("Validation on ${line}: ${message} ${severity}");
requestInterceptor.setResponseHeaderValueNoIssues("No issues detected");
// Now register the validating interceptor
registerInterceptor(requestInterceptor);
// Create an interceptor to validate responses
// This is configured in the same way as above
ResponseValidatingInterceptor responseInterceptor = new ResponseValidatingInterceptor();
responseInterceptor.addValidatorModule(new FhirInstanceValidator());
responseInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
responseInterceptor.setAddResponseHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
responseInterceptor.setResponseHeaderValue("Validation on ${line}: ${message} ${severity}");
responseInterceptor.setResponseHeaderValueNoIssues("No issues detected");
registerInterceptor(responseInterceptor);
}
}
// END SNIPPET: validatingInterceptor
// START SNIPPET: exceptionInterceptor
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")

View File

@ -38,6 +38,7 @@ public class MethodOutcome {
* Constructor
*/
public MethodOutcome() {
super();
}
/**

View File

@ -32,6 +32,7 @@ import java.util.Set;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@ -48,11 +49,13 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IRestfulServer;
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.util.OperationOutcomeUtil;
abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<MethodOutcome> {
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class);
@ -64,8 +67,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
if (!theMethod.getReturnType().equals(MethodOutcome.class)) {
if (!allowVoidReturnType()) {
throw new ConfigurationException("Method " + theMethod.getName() + " in type " + theMethod.getDeclaringClass().getCanonicalName() + " is a @" + theMethodAnnotation.getSimpleName()
+ " method but it does not return " + MethodOutcome.class);
throw new ConfigurationException("Method " + theMethod.getName() + " in type " + theMethod.getDeclaringClass().getCanonicalName() + " is a @" + theMethodAnnotation.getSimpleName() + " method but it does not return " + MethodOutcome.class);
} else if (theMethod.getReturnType() == void.class) {
myReturnVoid = true;
}
@ -84,7 +86,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
protected abstract BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource resource);
/**
* For servers, this method will match only incoming requests that match the given operation, or which have no operation in the URL if this method returns null.
* For servers, this method will match only incoming requests that match the given operation, or which have no
* operation in the URL if this method returns null.
*/
protected abstract String getMatchingOperation();
@ -109,51 +112,29 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
@Override
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
switch (theResponseStatusCode) {
case Constants.STATUS_HTTP_200_OK:
case Constants.STATUS_HTTP_201_CREATED:
case Constants.STATUS_HTTP_204_NO_CONTENT:
if (theResponseStatusCode >= 200 && theResponseStatusCode < 300) {
if (myReturnVoid) {
return null;
}
MethodOutcome retVal = MethodUtil.process2xxResponse(getContext(), getResourceName(), theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
return retVal;
default:
} else {
throw processNon2xxResponseAndReturnExceptionToThrow(theResponseStatusCode, theResponseMimeType, theResponseReader);
}
}
@Override
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
// if (requestContainsResource()) {
// requestContents = parseIncomingServerResource(theRequest);
// } else {
// requestContents = null;
// }
Object[] params = createParametersForServerRequest(theRequest);
addParametersForServerRequest(theRequest, params);
/*
* No need to catch nd handle exceptions here, we already handle them one level up
* including invoking interceptors on them
* No need to catch and handle exceptions here, we already handle them one level up including invoking interceptors
* on them
*/
MethodOutcome response;
// try {
response = (MethodOutcome) invokeServerMethod(theServer, theRequest, params);
// } catch (InternalErrorException e) {
// ourLog.error("Internal error during method invocation", e);
// EncodingEnum encodingNotNull = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
// streamOperationOutcome(e, theServer, encodingNotNull, servletResponse, theRequest);
// return;
// } catch (BaseServerResponseException e) {
// ourLog.info("Exception during method invocation: " + e.getMessage());
// EncodingEnum encodingNotNull = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
// streamOperationOutcome(e, theServer, encodingNotNull, servletResponse, theRequest);
// return;
// }
response = (MethodOutcome) invokeServerMethod(theServer, theRequest, params);
if (response != null && response.getId() != null && response.getId().hasResourceType()) {
if (getContext().getResourceDefinition(response.getId().getResourceType()) == null) {
@ -161,25 +142,17 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
}
}
IBaseResource outcome = response != null ? response.getOperationOutcome() : null;
IBaseOperationOutcome outcome = response != null ? response.getOperationOutcome() : null;
IBaseResource resource = response != null ? response.getResource() : null;
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
IServerInterceptor next = theServer.getInterceptors().get(i);
boolean continueProcessing = next.outgoingResponse(theRequest, outcome);
if (!continueProcessing) {
return null;
}
}
return returnResponse(theRequest, response, outcome, resource);
return returnResponse(theServer, theRequest, response, outcome, resource);
}
private int getOperationStatus(MethodOutcome response) {
switch (getRestOperationType()) {
case CREATE:
if (response == null) {
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName()
+ " returned null, which is not allowed for create operation");
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null, which is not allowed for create operation");
}
if (response.getCreated() == null || Boolean.TRUE.equals(response.getCreated())) {
return Constants.STATUS_HTTP_201_CREATED;
@ -212,15 +185,14 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
}
}
private Object returnResponse(RequestDetails theRequest, MethodOutcome response, IBaseResource originalOutcome, IBaseResource resource) throws IOException {
private Object returnResponse(IRestfulServer<?> theServer, RequestDetails theRequest, MethodOutcome response, IBaseResource originalOutcome, IBaseResource resource) throws IOException {
boolean allowPrefer = false;
int operationStatus = getOperationStatus(response);
IBaseResource outcome = originalOutcome;
if(EnumSet.of(RestOperationTypeEnum.CREATE, RestOperationTypeEnum.UPDATE).contains(getRestOperationType())) {
if (EnumSet.of(RestOperationTypeEnum.CREATE, RestOperationTypeEnum.UPDATE).contains(getRestOperationType())) {
allowPrefer = true;
}
if (resource != null && allowPrefer) {
String prefer = theRequest.getHeader(Constants.HEADER_PREFER);
@ -230,8 +202,16 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
outcome = resource;
}
}
}
}
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
IServerInterceptor next = theServer.getInterceptors().get(i);
boolean continueProcessing = next.outgoingResponse(theRequest, outcome);
if (!continueProcessing) {
return null;
}
}
return theRequest.getResponse().returnResponse(ParseAction.create(outcome), operationStatus, allowPrefer, response, getResourceName());
}
@ -241,8 +221,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
protected abstract Set<RequestTypeEnum> provideAllowableRequestTypes();
protected void streamOperationOutcome(BaseServerResponseException theE, RestfulServer theServer, EncodingEnum theEncodingNotNull, HttpServletResponse theResponse, RequestDetails theRequest)
throws IOException {
protected void streamOperationOutcome(BaseServerResponseException theE, RestfulServer theServer, EncodingEnum theEncodingNotNull, HttpServletResponse theResponse, RequestDetails theRequest) throws IOException {
theResponse.setStatus(theE.getStatusCode());
theServer.addHeadersToResponse(theResponse);

View File

@ -94,41 +94,38 @@ public class ResourceParameter implements IParameter {
}
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, IBaseResource theTargetResource)
throws InternalErrorException {
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException {
// TODO Auto-generated method stub
}
@Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding)
throws InternalErrorException, InvalidRequestException {
switch (myMode) {
case BODY:
try {
return IOUtils.toString(createRequestReader(theRequest));
}
catch (IOException e) {
// Shouldn't happen since we're reading from a byte array
throw new InternalErrorException("Failed to load request");
}
case ENCODING:
return RestfulServerUtils.determineRequestEncoding(theRequest);
case RESOURCE:
default:
return parseResourceFromRequest(theRequest, theMethodBinding, myResourceType);
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
switch (myMode) {
case BODY:
try {
return IOUtils.toString(createRequestReader(theRequest));
} catch (IOException e) {
// Shouldn't happen since we're reading from a byte array
throw new InternalErrorException("Failed to load request");
}
// }
case ENCODING:
return RestfulServerUtils.determineRequestEncoding(theRequest);
case RESOURCE:
default:
return parseResourceFromRequest(theRequest, theMethodBinding, myResourceType);
}
// }
}
public static Reader createRequestReader(RequestDetails theRequest, Charset charset) {
Reader requestReader = new InputStreamReader(new ByteArrayInputStream(theRequest.loadRequestContents()), charset);
return requestReader;
}
public static Reader createRequestReader(RequestDetails theRequest) throws IOException {
public static Reader createRequestReader(RequestDetails theRequest) {
return createRequestReader(theRequest, determineRequestCharset(theRequest));
}
}
public static Charset determineRequestCharset(RequestDetails theRequest) {
String ct = theRequest.getHeader(Constants.HEADER_CONTENT_TYPE);
@ -142,11 +139,10 @@ public class ResourceParameter implements IParameter {
charset = Charset.forName("UTF-8");
}
return charset;
}
}
@SuppressWarnings("unchecked")
public static <T extends IBaseResource> T loadResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding,
Class<T> theResourceType) {
public static <T extends IBaseResource> T loadResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding, Class<T> theResourceType) {
FhirContext ctx = theRequest.getServer().getFhirContext();
final Charset charset = determineRequestCharset(theRequest);
@ -170,7 +166,7 @@ public class ResourceParameter implements IParameter {
String body;
try {
body = IOUtils.toString(requestReader);
} catch (IOException e) {
} catch (IOException e) {
// This shouldn't happen since we're reading from a byte array..
throw new InternalErrorException(e);
}
@ -210,8 +206,8 @@ public class ResourceParameter implements IParameter {
}
}
return retVal;
}
}
public static IBaseResource parseResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding, Class<? extends IBaseResource> theResourceType) {
IBaseResource retVal;
if (IBaseBinary.class.isAssignableFrom(theResourceType)) {
@ -226,7 +222,7 @@ public class ResourceParameter implements IParameter {
retVal = loadResourceFromRequest(theRequest, theMethodBinding, theResourceType);
}
return retVal;
}
}
public enum Mode {
BODY, ENCODING, RESOURCE

View File

@ -1,7 +1,5 @@
package ca.uhn.fhir.rest.param;
import java.io.IOException;
/*
* #%L
* HAPI FHIR - Core Library
@ -108,13 +106,7 @@ public class TransactionParameter implements IParameter {
IParser parser = encoding.newParser(theRequest.getServer().getFhirContext());
Reader reader;
try {
reader = ResourceParameter.createRequestReader(theRequest);
}
catch (IOException e) {
ourLog.error("Could not load request resource", e);
throw new InvalidRequestException(String.format("Could not load request resource: %s", e.getMessage()));
}
reader = ResourceParameter.createRequestReader(theRequest);
switch (myParamStyle) {
case DSTU1_BUNDLE: {

View File

@ -35,17 +35,18 @@ import ca.uhn.fhir.rest.method.ParseAction;
public interface IRestfulResponse {
Object streamResponseAsResource(IBaseResource resource, boolean prettyPrint, Set<SummaryEnum> summaryMode, int operationStatus, boolean respondGzip, boolean addContentLocationHeader)
throws IOException;
Object streamResponseAsBundle(Bundle bundle, Set<SummaryEnum> summaryMode, boolean respondGzip, boolean requestIsBrowser)
throws IOException;
Object streamResponseAsResource(IBaseResource resource, boolean prettyPrint, Set<SummaryEnum> summaryMode, int operationStatus, boolean respondGzip, boolean addContentLocationHeader) throws IOException;
Object returnResponse(ParseAction<?> outcome, int operationStatus, boolean allowPrefer, MethodOutcome response, String resourceName) throws IOException;
Writer getResponseWriter(int statusCode, String contentType, String charset, boolean respondGzip) throws UnsupportedEncodingException, IOException;
Object sendWriterResponse(int status, String contentType, String charset, Writer writer) throws IOException;
void addHeader(String headerKey, String headerValue);
Object streamResponseAsBundle(Bundle bundle, Set<SummaryEnum> summaryMode, boolean respondGzip, boolean requestIsBrowser) throws IOException;
Object returnResponse(ParseAction<?> outcome, int operationStatus, boolean allowPrefer, MethodOutcome response, String resourceName) throws IOException;
Writer getResponseWriter(int statusCode, String contentType, String charset, boolean respondGzip) throws UnsupportedEncodingException, IOException;
Object sendWriterResponse(int status, String contentType, String charset, Writer writer) throws IOException;
void addHeader(String headerKey, String headerValue);
Object sendAttachmentResponse(IBaseBinary bin, int stausCode, String contentType) throws IOException;
Object sendAttachmentResponse(IBaseBinary bin, int stausCode, String contentType) throws IOException;
}

View File

@ -0,0 +1,235 @@
package ca.uhn.fhir.rest.server.interceptor;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.text.StrLookup;
import org.apache.commons.lang3.text.StrSubstitutor;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.IValidatorModule;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult;
/**
* This interceptor intercepts each incoming request and if it contains a FHIR resource, validates that resource. The
* interceptor may be configured to run any validator modules, and will then add headers to the response or fail the
* request with an {@link UnprocessableEntityException HTTP 422 Unprocessable Entity}.
*/
abstract class BaseValidatingInterceptor<T> extends InterceptorAdapter {
/**
* Default value:<br/>
* <code>
* ${row}:${col} ${severity} ${message} (${location})
* </code>
*/
public static final String DEFAULT_RESPONSE_HEADER_VALUE = "${row}:${col} ${severity} ${message} (${location})";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseValidatingInterceptor.class);
private Integer myAddResponseHeaderOnSeverity = ResultSeverityEnum.INFORMATION.ordinal();
private Integer myFailOnSeverity = ResultSeverityEnum.ERROR.ordinal();
private String myResponseHeaderName = provideDefaultResponseHeaderName();
private String myResponseHeaderValue = DEFAULT_RESPONSE_HEADER_VALUE;
private String myResponseHeaderValueNoIssues = null;
private List<IValidatorModule> myValidatorModules;
private void addResponseHeader(RequestDetails theRequestDetails, SingleValidationMessage theNext) {
// Perform any string substitutions from the message format
StrLookup<?> lookup = new MyLookup(theNext);
StrSubstitutor subs = new StrSubstitutor(lookup, "${", "}", '\\');
// Log the header
String headerValue = subs.replace(myResponseHeaderValue);
ourLog.trace("Adding header to response: {}", headerValue);
theRequestDetails.getResponse().addHeader(myResponseHeaderName, headerValue);
}
public BaseValidatingInterceptor addValidatorModule(IValidatorModule theModule) {
Validate.notNull(theModule, "theModule must not be null");
if (getValidatorModules() == null) {
setValidatorModules(new ArrayList<IValidatorModule>());
}
getValidatorModules().add(theModule);
return this;
}
abstract ValidationResult doValidate(FhirValidator theValidator, T theRequest);
/**
* Fail the request by throwing an {@link UnprocessableEntityException} as a result of a validation failure.
* Subclasses may change this behaviour by providing alternate behaviour.
*/
protected void fail(RequestDetails theRequestDetails, ValidationResult theValidationResult) {
throw new UnprocessableEntityException(theRequestDetails.getServer().getFhirContext(), theValidationResult.toOperationOutcome());
}
public List<IValidatorModule> getValidatorModules() {
return myValidatorModules;
}
abstract String provideDefaultResponseHeaderName();
/**
* Sets the minimum severity at which an issue detected by the validator will result in a header being added to the
* response. Default is {@link ResultSeverityEnum#INFORMATION}. Set to <code>null</code> to disable this behaviour.
*
* @see #setResponseHeaderName(String)
* @see #setResponseHeaderValue(String)
*/
public void setAddResponseHeaderOnSeverity(ResultSeverityEnum theSeverity) {
myAddResponseHeaderOnSeverity = theSeverity != null ? theSeverity.ordinal() : null;
}
/**
* Sets the minimum severity at which an issue detected by the validator will fail/reject the request. Default is
* {@link ResultSeverityEnum#ERROR}. Set to <code>null</code> to disable this behaviour.
*/
public void setFailOnSeverity(ResultSeverityEnum theSeverity) {
myFailOnSeverity = theSeverity != null ? theSeverity.ordinal() : null;
}
/**
* Sets the name of the response header to add validation failures to
*
* @see #setAddResponseHeaderOnSeverity(ResultSeverityEnum)
*/
protected void setResponseHeaderName(String theResponseHeaderName) {
Validate.notBlank(theResponseHeaderName, "theResponseHeaderName must not be blank or null");
myResponseHeaderName = theResponseHeaderName;
}
/**
* Sets the value to add to the response header with the name specified by {@link #setResponseHeaderName(String)}
* when validation produces a message of severity equal to or greater than
* {@link #setAddResponseHeaderOnSeverity(ResultSeverityEnum)}
* <p>
* This field allows the following substitutions:
* </p>
* <table>
* <tr>
* <td>Name</td>
* <td>Value</td>
* </tr>
* <tr>
* <td>${line}</td>
* <td>The line in the request</td>
* </tr>
* <tr>
* <td>${col}</td>
* <td>The column in the request</td>
* </tr>
* <tr>
* <td>${location}</td>
* <td>The location in the payload as a string (typically this will be a path)</td>
* </tr>
* <tr>
* <td>${severity}</td>
* <td>The severity of the issue</td>
* </tr>
* <tr>
* <td>${message}</td>
* <td>The validation message</td>
* </tr>
* </table>
*
* @see #DEFAULT_RESPONSE_HEADER_VALUE
* @see #setAddResponseHeaderOnSeverity(ResultSeverityEnum)
*/
public void setResponseHeaderValue(String theResponseHeaderValue) {
Validate.notBlank(theResponseHeaderValue, "theResponseHeaderValue must not be blank or null");
myResponseHeaderValue = theResponseHeaderValue;
}
/**
* Sets the header value to add when no issues are found at or exceeding the
* threshold specified by {@link #setAddResponseHeaderOnSeverity(ResultSeverityEnum)}
*/
public void setResponseHeaderValueNoIssues(String theResponseHeaderValueNoIssues) {
myResponseHeaderValueNoIssues = theResponseHeaderValueNoIssues;
}
public void setValidatorModules(List<IValidatorModule> theValidatorModules) {
myValidatorModules = theValidatorModules;
}
protected void validate(T theRequest, RequestDetails theRequestDetails) {
FhirValidator validator = theRequestDetails.getServer().getFhirContext().newValidator();
if (myValidatorModules != null) {
for (IValidatorModule next : myValidatorModules) {
validator.registerValidatorModule(next);
}
}
ValidationResult validationResult = doValidate(validator, theRequest);
if (myAddResponseHeaderOnSeverity != null) {
boolean found = false;
for (SingleValidationMessage next : validationResult.getMessages()) {
if (next.getSeverity().ordinal() >= myAddResponseHeaderOnSeverity) {
addResponseHeader(theRequestDetails, next);
found = true;
}
}
if (!found) {
if (isNotBlank(myResponseHeaderValueNoIssues)) {
theRequestDetails.getResponse().addHeader(myResponseHeaderName, myResponseHeaderValueNoIssues);
}
}
}
if (myFailOnSeverity != null) {
for (SingleValidationMessage next : validationResult.getMessages()) {
if (next.getSeverity().ordinal() >= myFailOnSeverity) {
fail(theRequestDetails, validationResult);
return;
}
}
}
}
private static class MyLookup extends StrLookup<String> {
private SingleValidationMessage myMessage;
public MyLookup(SingleValidationMessage theMessage) {
myMessage = theMessage;
}
@Override
public String lookup(String theKey) {
if ("line".equals(theKey)) {
return toString(myMessage.getLocationLine());
}
if ("col".equals(theKey)) {
return toString(myMessage.getLocationCol());
}
if ("message".equals(theKey)) {
return toString(myMessage.getMessage());
}
if ("location".equals(theKey)) {
return toString(myMessage.getLocationString());
}
if ("severity".equals(theKey)) {
return myMessage.getSeverity() != null ? myMessage.getSeverity().name() : null;
}
return "";
}
private static String toString(Object theInt) {
return theInt != null ? theInt.toString() : "";
}
}
}

View File

@ -29,6 +29,7 @@ import javax.servlet.http.HttpServletResponse;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
@ -41,41 +42,50 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
/**
* Provides methods to intercept requests and responses. Note that implementations of this interface may wish to use {@link InterceptorAdapter} in order to not need to implement every method.
* Provides methods to intercept requests and responses. Note that implementations of this interface may wish to use
* {@link InterceptorAdapter} in order to not need to implement every method.
* <p>
* <b>See:</b> See the <a href="http://jamesagnew.github.io/hapi-fhir/doc_rest_server_interceptor.html">server interceptor documentation</a> for more information on how to use this class.
* <b>See:</b> See the <a href="http://jamesagnew.github.io/hapi-fhir/doc_rest_server_interceptor.html">server
* interceptor documentation</a> for more information on how to use this class.
* </p>
*/
public interface IServerInterceptor {
/**
* This method is called 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 runtime exceptions thrown by the server itself. This also includes any {@link AuthenticationException}s thrown.
* This method is called 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 runtime exceptions thrown by the server itself. This also includes any {@link AuthenticationException}s
* thrown.
* <p>
* Implementations of this method may choose to ignore/log/count/etc exceptions, and return <code>true</code>. In this case, processing will continue, and the server will automatically generate an
* {@link BaseOperationOutcome OperationOutcome}. Implementations may also choose to provide their own response to the client. In this case, they should return <code>false</code>, to indicate that
* they have handled the request and processing should stop.
* Implementations of this method may choose to ignore/log/count/etc exceptions, and return <code>true</code>. In
* this case, processing will continue, and the server will automatically generate an {@link BaseOperationOutcome
* OperationOutcome}. Implementations may also choose to provide their own response to the client. In this case, they
* should return <code>false</code>, to indicate that they have handled the request and processing should stop.
* </p>
*
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including details such as the resource type and logical ID (if any) and other FHIR-specific aspects of the
* request which have been pulled out of the {@link javax.servlet.http.HttpServletRequest servlet request}. Note that the bean properties are not all guaranteed to be populated, depending
* on how early during processing the exception occurred.
* A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the {@link javax.servlet.http.HttpServletRequest servlet request}. Note that the bean
* properties are not all guaranteed to be populated, depending on how early during processing the
* exception occurred.
* @param theServletRequest
* The incoming request
* @param theServletResponse
* The response. Note that interceptors may choose to provide a response (i.e. by calling {@link javax.servlet.http.HttpServletResponse#getWriter()}) but in that case it is important to
* return <code>false</code> to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
* The response. Note that interceptors may choose to provide a response (i.e. by calling
* {@link javax.servlet.http.HttpServletResponse#getWriter()}) but in that case it is important to return
* <code>false</code> to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
* will be called.
* @throws ServletException
* If this exception is thrown, it will be re-thrown up to the container for handling.
* @throws IOException
* If this exception is thrown, it will be re-thrown up to the container for handling.
*/
boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
throws ServletException, IOException;
boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws ServletException, IOException;
/**
* This method is called just before the actual implementing server method is invoked.
@ -84,18 +94,22 @@ public interface IServerInterceptor {
* </p>
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including details such as the resource type and logical ID (if any) and other FHIR-specific aspects of the
* request which have been pulled out of the {@link HttpServletRequest servlet request}.
* A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the {@link HttpServletRequest servlet request}.
* @param theRequest
* The incoming request
* @param theResponse
* The response. Note that interceptors may choose to provide a response (i.e. by calling {@link HttpServletResponse#getWriter()}) but in that case it is important to return
* <code>false</code> to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
* The response. Note that interceptors may choose to provide a response (i.e. by calling
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
* to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
* will be called.
* @throws AuthenticationException
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
* client.
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException;
@ -107,14 +121,14 @@ public interface IServerInterceptor {
* @param theOperation
* The type of operation that the FHIR server has determined that the client is trying to invoke
* @param theProcessedRequest
* An object which will be populated with the details which were extracted from the raw request by the server,
* e.g. the FHIR operation type and the parsed resource body (if any).
* An object which will be populated with the details which were extracted from the raw request by the
* server, e.g. the FHIR operation type and the parsed resource body (if any).
*/
void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theProcessedRequest);
/**
* This method 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.
* This method 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.
* <p>
* Note that any exceptions thrown by this method will not be trapped by HAPI (they will be passed up to the server)
* </p>
@ -122,15 +136,19 @@ public interface IServerInterceptor {
* @param theRequest
* The incoming request
* @param theResponse
* The response. Note that interceptors may choose to provide a response (i.e. by calling {@link HttpServletResponse#getWriter()}) but in that case it is important to return
* <code>false</code> to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
* The response. Note that interceptors may choose to provide a response (i.e. by calling
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
* to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
* will be called.
*/
boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse);
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
* This method is called after the server implementation method has been called, but before any attempt to stream the
* response back to the client
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including
@ -139,159 +157,198 @@ public interface IServerInterceptor {
* @param theServletRequest
* The incoming request
* @param theServletResponse
* The response. Note that interceptors may choose to provide a response (i.e. by calling {@link HttpServletResponse#getWriter()}) but in that case it is important to return
* <code>false</code> to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
* The response. Note that interceptors may choose to provide a response (i.e. by calling
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
* to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
* will be called.
* @throws AuthenticationException
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
* client.
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including
* @param theResponseObject
* The actual object which is being streamed to the client as a response
* @param theServletRequest
* The incoming request
* @param theServletResponse
* The response. Note that interceptors may choose to provide a response (i.e. by calling {@link HttpServletResponse#getWriter()}) but in that case it is important to return
* <code>false</code> to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
* @throws AuthenticationException
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
* client.
*/
boolean outgoingResponse(RequestDetails theRequest, Bundle bundle);
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
* This method is called after the server implementation method has been called, but before any attempt to stream the
* response back to the client
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including details such as the resource type and logical ID (if any) and other FHIR-specific aspects of the
* request which have been pulled out of the {@link HttpServletRequest servlet request}.
* A bean containing details about the request that is about to be processed, including
* @param theResponseObject
* The actual object which is being streamed to the client as a response
* @param theServletRequest
* The incoming request
* @param theServletResponse
* The response. Note that interceptors may choose to provide a response (i.e. by calling {@link HttpServletResponse#getWriter()}) but in that case it is important to return
* <code>false</code> to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
* The response. Note that interceptors may choose to provide a response (i.e. by calling
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
* to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
* will be called.
* @throws AuthenticationException
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
* client.
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
boolean outgoingResponse(RequestDetails theRequest, Bundle bundle);
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the
* response back to the client
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the {@link HttpServletRequest servlet request}.
* @param theServletRequest
* The incoming request
* @param theServletResponse
* The response. Note that interceptors may choose to provide a response (i.e. by calling
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
* to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
* will be called.
* @throws AuthenticationException
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including details such as the resource type and logical ID (if any) and other FHIR-specific aspects of the
* request which have been pulled out of the {@link HttpServletRequest servlet request}.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
* @throws AuthenticationException
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
* client.
*/
boolean outgoingResponse(RequestDetails theRequestDetails);
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
* This method is called after the server implementation method has been called, but before any attempt to stream the
* response back to the client
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including details such as the resource type and logical ID (if any) and other FHIR-specific aspects of the
* request which have been pulled out of the {@link HttpServletRequest servlet request}.
* @param theResponseObject
* The actual object which is being streamed to the client as a response
* @param theServletRequest
* The incoming request
* @param theServletResponse
* The response. Note that interceptors may choose to provide a response (i.e. by calling {@link HttpServletResponse#getWriter()}) but in that case it is important to return
* <code>false</code> to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
* A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the {@link HttpServletRequest servlet request}.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
* will be called.
* @throws AuthenticationException
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
* client.
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
throws AuthenticationException;
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including details such as the resource type and logical ID (if any) and other FHIR-specific aspects of the
* request which have been pulled out of the {@link HttpServletRequest servlet request}.
* @param theResponseObject
* The actual object which is being streamed to the client as a response
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
* @throws AuthenticationException
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
* client.
*/
boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject);
boolean outgoingResponse(RequestDetails theRequestDetails);
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
* This method is called after the server implementation method has been called, but before any attempt to stream the
* response back to the client
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including details such as the resource type and logical ID (if any) and other FHIR-specific aspects of the
* request which have been pulled out of the {@link HttpServletRequest servlet request}.
* A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the {@link HttpServletRequest servlet request}.
* @param theResponseObject
* The actual object which is being streamed to the client as a response
* @param theServletRequest
* The incoming request
* @param theServletResponse
* The response. Note that interceptors may choose to provide a response (i.e. by calling {@link HttpServletResponse#getWriter()}) but in that case it is important to return
* <code>false</code> to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
* The response. Note that interceptors may choose to provide a response (i.e. by calling
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
* to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
* will be called.
* @throws AuthenticationException
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
* client.
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the
* response back to the client
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the {@link HttpServletRequest servlet request}.
* @param theResponseObject
* The actual object which is being streamed to the client as a response
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
* will be called.
* @throws AuthenticationException
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject);
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the
* response back to the client
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the {@link HttpServletRequest servlet request}.
* @param theResponseObject
* The actual object which is being streamed to the client as a response
* @param theServletRequest
* The incoming request
* @param theServletResponse
* The response. Note that interceptors may choose to provide a response (i.e. by calling
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
* to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
* will be called.
* @throws AuthenticationException
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including details such as the resource type and logical ID (if any) and other FHIR-specific aspects of the
* request which have been pulled out of the {@link HttpServletRequest servlet request}.
* @param theResponseObject
* The actual object which is being streamed to the client as a response
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
* @throws AuthenticationException
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
* client.
*/
boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject);
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the
* response back to the client
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the {@link HttpServletRequest servlet request}.
* @param theResponseObject
* The actual object which is being streamed to the client as a response
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
* will be called.
* @throws AuthenticationException
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject);
/**
* This method is called 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 runtime exceptions thrown by the server itself. This method is invoked for each interceptor (until one of them returns a non-<code>null</code> response or
* the end of the list is reached), after which {@link #handleException(RequestDetails, BaseServerResponseException, HttpServletRequest, HttpServletResponse)} is called for each interceptor.
* This method is called 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 runtime exceptions thrown by the server itself. This method is invoked for each interceptor (until one of them
* returns a non-<code>null</code> response or the end of the list is reached), after which
* {@link #handleException(RequestDetails, BaseServerResponseException, HttpServletRequest, HttpServletResponse)} is
* called for each interceptor.
* <p>
* This may be used to add an OperationOutcome to a response, or to convert between exception types for any reason.
* </p>
* <p>
* Implementations of this method may choose to ignore/log/count/etc exceptions, and return <code>null</code>. In this case, processing will continue, and the server will automatically generate an
* {@link BaseOperationOutcome OperationOutcome}. Implementations may also choose to provide their own response to the client. In this case, they should return a non-<code>null</code>, to indicate
* that they have handled the request and processing should stop.
* Implementations of this method may choose to ignore/log/count/etc exceptions, and return <code>null</code>. In
* this case, processing will continue, and the server will automatically generate an {@link BaseOperationOutcome
* OperationOutcome}. Implementations may also choose to provide their own response to the client. In this case, they
* should return a non-<code>null</code>, to indicate that they have handled the request and processing should stop.
* </p>
*
* @return Returns the new exception to use for processing, or <code>null</code> if this interceptor is not trying to modify the exception. For example, if this interceptor has nothing to do with
* exception processing, it should always return <code>null</code>. If this interceptor adds an OperationOutcome to the exception, it should return an exception.
* @return Returns the new exception to use for processing, or <code>null</code> if this interceptor is not trying to
* modify the exception. For example, if this interceptor has nothing to do with exception processing, it
* should always return <code>null</code>. If this interceptor adds an OperationOutcome to the exception, it
* should return an exception.
*/
BaseServerResponseException preProcessOutgoingException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest) throws ServletException;
@ -300,19 +357,26 @@ public interface IServerInterceptor {
private boolean myLocked = false;
private IBaseResource myResource;
private final String myResourceType;
private final FhirContext myContext;
public ActionRequestDetails(IIdType theId, String theResourceType) {
public ActionRequestDetails(IIdType theId, String theResourceType, FhirContext theContext) {
myId = theId;
myResourceType = theResourceType;
myContext = theContext;
}
public ActionRequestDetails(RequestDetails theRequestDetails) {
myId = theRequestDetails.getId();
myResourceType = theRequestDetails.getResourceName();
myContext = theRequestDetails.getServer().getFhirContext();
}
public ActionRequestDetails(IIdType theId, String theResourceType, IBaseResource theResource) {
this(theId, theResourceType);
public FhirContext getContext() {
return myContext;
}
public ActionRequestDetails(IIdType theId, String theResourceType, IBaseResource theResource, FhirContext theContext) {
this(theId, theResourceType, theContext);
myResource = theResource;
}
@ -324,10 +388,12 @@ public interface IServerInterceptor {
}
/**
* For requests where a resource is passed from the client to the server (e.g. create, update, etc.) this method will return the resource which was provided by the client. Otherwise, this method
* will return <code>null</code>.
* For requests where a resource is passed from the client to the server (e.g. create, update, etc.) this method
* will return the resource which was provided by the client. Otherwise, this method will return <code>null</code>
* .
* <p>
* Note that this method is currently only populated if the handling method has a parameter annotated with the {@link ResourceParam} annotation.
* Note that this method is currently only populated if the handling method has a parameter annotated with the
* {@link ResourceParam} annotation.
* </p>
*/
public IBaseResource getResource() {
@ -335,7 +401,8 @@ public interface IServerInterceptor {
}
/**
* Returns the resource type this request pertains to, or <code>null</code> if this request is not type specific (e.g. server-history)
* Returns the resource type this request pertains to, or <code>null</code> if this request is not type specific
* (e.g. server-history)
*/
public String getResourceType() {
return myResourceType;

View File

@ -0,0 +1,71 @@
package ca.uhn.fhir.rest.server.interceptor;
import java.nio.charset.Charset;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.param.ResourceParameter;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhir.validation.ValidationResult;
/**
* This interceptor intercepts each incoming request and if it contains a FHIR resource, validates that resource. The
* interceptor may be configured to run any validator modules, and will then add headers to the response or fail the
* request with an {@link UnprocessableEntityException HTTP 422 Unprocessable Entity}.
*/
public class RequestValidatingInterceptor extends BaseValidatingInterceptor<String> {
/**
* X-HAPI-Request-Validation
*/
public static final String DEFAULT_RESPONSE_HEADER_NAME = "X-HAPI-Request-Validation";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RequestValidatingInterceptor.class);
@Override
public boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
EncodingEnum encoding = RestfulServerUtils.determineRequestEncodingNoDefault(theRequestDetails);
if (encoding == null) {
ourLog.trace("Incoming request does not appear to be FHIR, not going to validate");
return true;
}
Charset charset = ResourceParameter.determineRequestCharset(theRequestDetails);
String requestText = new String(theRequestDetails.loadRequestContents(), charset);
validate(requestText, theRequestDetails);
return true;
}
/**
* Sets the name of the response header to add validation failures to
*
* @see #DEFAULT_RESPONSE_HEADER_NAME
* @see #setAddResponseHeaderOnSeverity(ResultSeverityEnum)
*/
@Override
public void setResponseHeaderName(String theResponseHeaderName) {
super.setResponseHeaderName(theResponseHeaderName);
}
@Override
String provideDefaultResponseHeaderName() {
return DEFAULT_RESPONSE_HEADER_NAME;
}
@Override
ValidationResult doValidate(FhirValidator theValidator, String theRequest) {
return theValidator.validateWithResult(theRequest);
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.server.interceptor;
import java.nio.charset.Charset;
/*
* #%L
* HAPI FHIR - Core Library
@ -29,49 +31,54 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.param.ResourceParameter;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhir.validation.ValidationResult;
/**
* Not yet complete!
*
* TODO: complete this
* This interceptor intercepts each outgoing response and if it contains a FHIR resource, validates that resource. The
* interceptor may be configured to run any validator modules, and will then add headers to the response or fail the
* request with an {@link UnprocessableEntityException HTTP 422 Unprocessable Entity}.
*/
class ResponseValidatingInterceptor extends InterceptorAdapter {
public class ResponseValidatingInterceptor extends BaseValidatingInterceptor<IBaseResource> {
private FhirValidator myValidator;
/**
* Returns the validator used by this interceptor
* X-HAPI-Request-Validation
*/
public FhirValidator getValidator() {
return myValidator;
public static final String DEFAULT_RESPONSE_HEADER_NAME = "X-HAPI-Response-Validation";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResponseValidatingInterceptor.class);
@Override
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject) {
validate(theResponseObject, theRequestDetails);
return true;
}
/**
* Sets the validator instance to use. Must not be null.
* Sets the name of the response header to add validation failures to
*
* @see #DEFAULT_RESPONSE_HEADER_NAME
* @see #setAddResponseHeaderOnSeverity(ResultSeverityEnum)
*/
public void setValidator(FhirValidator theValidator) {
Validate.notNull(theValidator, "Validator must not be null");
myValidator = theValidator;
@Override
public void setResponseHeaderName(String theResponseHeaderName) {
super.setResponseHeaderName(theResponseHeaderName);
}
@Override
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
return true;
String provideDefaultResponseHeaderName() {
return DEFAULT_RESPONSE_HEADER_NAME;
}
@Override
public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
return true;
ValidationResult doValidate(FhirValidator theValidator, IBaseResource theRequest) {
return theValidator.validateWithResult(theRequest);
}
@Override
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
return true;
}
}

View File

@ -38,60 +38,60 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.RestfulResponse;
public class ServletRestfulResponse extends RestfulResponse<ServletRequestDetails> {
public ServletRestfulResponse(ServletRequestDetails servletRequestDetails) {
super(servletRequestDetails);
}
@Override
public Object sendAttachmentResponse(IBaseBinary bin, int stausCode, String contentType) throws IOException {
addHeaders();
HttpServletResponse theHttpResponse = getRequestDetails().getServletResponse();
theHttpResponse.setStatus(stausCode);
theHttpResponse.setContentType(contentType);
if (bin.getContent() == null || bin.getContent().length == 0) {
return null;
} else {
theHttpResponse.setContentLength(bin.getContent().length);
ServletOutputStream oos = theHttpResponse.getOutputStream();
oos.write(bin.getContent());
oos.close();
return null;
}
}
@Override
public Writer getResponseWriter(int statusCode, String contentType, String charset, boolean theRespondGzip) throws UnsupportedEncodingException, IOException {
addHeaders();
HttpServletResponse theHttpResponse = getRequestDetails().getServletResponse();
theHttpResponse.setCharacterEncoding(charset);
theHttpResponse.setStatus(statusCode);
theHttpResponse.setContentType(contentType);
public ServletRestfulResponse(ServletRequestDetails servletRequestDetails) {
super(servletRequestDetails);
}
@Override
public Object sendAttachmentResponse(IBaseBinary bin, int stausCode, String contentType) throws IOException {
addHeaders();
HttpServletResponse theHttpResponse = getRequestDetails().getServletResponse();
theHttpResponse.setStatus(stausCode);
theHttpResponse.setContentType(contentType);
if (bin.getContent() == null || bin.getContent().length == 0) {
return null;
} else {
theHttpResponse.setContentLength(bin.getContent().length);
ServletOutputStream oos = theHttpResponse.getOutputStream();
oos.write(bin.getContent());
oos.close();
return null;
}
}
@Override
public Writer getResponseWriter(int statusCode, String contentType, String charset, boolean theRespondGzip) throws UnsupportedEncodingException, IOException {
addHeaders();
HttpServletResponse theHttpResponse = getRequestDetails().getServletResponse();
theHttpResponse.setCharacterEncoding(charset);
theHttpResponse.setStatus(statusCode);
theHttpResponse.setContentType(contentType);
if (theRespondGzip) {
theHttpResponse.addHeader(Constants.HEADER_CONTENT_ENCODING, Constants.ENCODING_GZIP);
return new OutputStreamWriter(new GZIPOutputStream(theHttpResponse.getOutputStream()), Constants.CHARSET_NAME_UTF8);
} else {
return theHttpResponse.getWriter();
}
}
}
private void addHeaders() {
HttpServletResponse theHttpResponse = getRequestDetails().getServletResponse();
getRequestDetails().getServer().addHeadersToResponse(theHttpResponse);
for (Entry<String, String> header : getHeaders().entrySet()) {
theHttpResponse.setHeader(header.getKey(), header.getValue());
}
}
private void addHeaders() {
HttpServletResponse theHttpResponse = getRequestDetails().getServletResponse();
getRequestDetails().getServer().addHeadersToResponse(theHttpResponse);
for (Entry<String, String> header : getHeaders().entrySet()) {
theHttpResponse.setHeader(header.getKey(), header.getValue());
}
}
@Override
public final Object sendWriterResponse(int status, String contentType, String charset, Writer writer) throws IOException {
writer.close();
return null;
}
writer.close();
return null;
}
@Override
public Object returnResponse(ParseAction<?> outcome, int operationStatus, boolean allowPrefer, MethodOutcome response,
String resourceName) throws IOException {
return getRequestDetails().getServer().returnResponse(getRequestDetails(), outcome, operationStatus, allowPrefer, response, resourceName);
}
@Override
public Object returnResponse(ParseAction<?> outcome, int operationStatus, boolean allowPrefer, MethodOutcome response, String resourceName) throws IOException {
addHeaders();
return getRequestDetails().getServer().returnResponse(getRequestDetails(), outcome, operationStatus, allowPrefer, response, resourceName);
}
}

View File

@ -1,5 +1,12 @@
package ca.uhn.fhir.jpa.config;
import org.hl7.fhir.dstu21.hapi.validation.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu21.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu21.hapi.validation.FhirQuestionnaireResponseValidator;
import org.hl7.fhir.dstu21.hapi.validation.IValidationSupport;
import org.hl7.fhir.dstu21.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.dstu21.validation.IResourceValidator.BestPracticeWarningLevel;
/*
* #%L
* HAPI FHIR JPA Server
@ -21,8 +28,11 @@ package ca.uhn.fhir.jpa.config;
*/
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@ -30,6 +40,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.FhirSearchDao;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.dao.ISearchDao;
import ca.uhn.fhir.validation.IValidatorModule;
@Configuration
@EnableTransactionManagement
@ -48,22 +59,48 @@ public class BaseDstu21Config extends BaseConfig {
}
@Bean(name = "mySystemProviderDstu21")
public ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu21 systemProviderDstu2() {
public ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu21 systemProviderDstu21() {
ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu21 retVal = new ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu21();
retVal.setDao(systemDaoDstu21());
return retVal;
}
@Bean(name = "myJpaValidationSupportDstu21", autowire = Autowire.BY_NAME)
public ca.uhn.fhir.jpa.dao.IJpaValidationSupportDstu21 jpaValidationSupportDstu2() {
public ca.uhn.fhir.jpa.dao.IJpaValidationSupportDstu21 jpaValidationSupportDstu21() {
ca.uhn.fhir.jpa.dao.JpaValidationSupportDstu21 retVal = new ca.uhn.fhir.jpa.dao.JpaValidationSupportDstu21();
return retVal;
}
@Qualifier("myJpaValidationSupportDstu21")
private IValidationSupport myJpaValidationSupportDstu21;
@Bean(autowire = Autowire.BY_TYPE)
public ISearchDao searchDao() {
public ISearchDao searchDaoDstu21() {
FhirSearchDao searchDao = new FhirSearchDao();
return searchDao;
}
@Bean(name="myInstanceValidatorDstu21")
@Lazy
public IValidatorModule instanceValidatorDstu21() {
FhirInstanceValidator val = new FhirInstanceValidator();
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
val.setValidationSupport(validationSupportChainDstu21());
return val;
}
@Bean(name="myQuestionnaireResponseValidatorDstu21")
@Lazy
public IValidatorModule questionnaireResponseValidatorDstu21() {
FhirQuestionnaireResponseValidator module = new FhirQuestionnaireResponseValidator();
module.setValidationSupport(validationSupportChainDstu21());
return module;
}
@Bean
public IValidationSupport validationSupportChainDstu21() {
return new ValidationSupportChain(new DefaultProfileValidationSupport(), myJpaValidationSupportDstu21);
// return new ValidationSupportChain();
}
}

View File

@ -154,26 +154,26 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
if (getContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
if (theResource.getIdElement().isIdPartValidLong()) {
String message = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getIdElement().getIdPart());
throw new InvalidRequestException(message, createErrorOperationOutcome(message));
throw new InvalidRequestException(message, createErrorOperationOutcome(message, "processing"));
}
} else {
String message = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedId", theResource.getIdElement().getIdPart());
throw new InvalidRequestException(message, createErrorOperationOutcome(message));
throw new InvalidRequestException(message, createErrorOperationOutcome(message, "processing"));
}
}
return doCreate(theResource, theIfNoneExist, thePerformIndexing, new Date());
}
public IBaseOperationOutcome createErrorOperationOutcome(String theMessage) {
return createOperationOutcome(OO_SEVERITY_ERROR, theMessage);
public IBaseOperationOutcome createErrorOperationOutcome(String theMessage, String theCode) {
return createOperationOutcome(OO_SEVERITY_ERROR, theMessage, theCode);
}
public IBaseOperationOutcome createInfoOperationOutcome(String theMessage) {
return createOperationOutcome(OO_SEVERITY_INFO, theMessage);
return createOperationOutcome(OO_SEVERITY_INFO, theMessage, "informational");
}
protected abstract IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage);
protected abstract IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage, String theCode);
@Override
public DaoMethodOutcome delete(IIdType theId) {
@ -201,7 +201,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
validateOkToDelete(deleteConflicts, entity);
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, theId.getResourceType());
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, theId.getResourceType(), getContext());
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
Date updateTime = new Date();
@ -251,7 +251,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
// Notify interceptors
IdDt idToDelete = entity.getIdDt();
ActionRequestDetails requestDetails = new ActionRequestDetails(idToDelete, idToDelete.getResourceType());
ActionRequestDetails requestDetails = new ActionRequestDetails(idToDelete, idToDelete.getResourceType(), getContext());
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
// Perform delete
@ -308,7 +308,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theResource.getIdElement(), toResourceName(theResource), theResource);
ActionRequestDetails requestDetails = new ActionRequestDetails(theResource.getIdElement(), toResourceName(theResource), theResource, getContext());
notifyInterceptors(RestOperationTypeEnum.CREATE, requestDetails);
// Perform actual DB update
@ -333,7 +333,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public TagList getAllResourceTags() {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext());
notifyInterceptors(RestOperationTypeEnum.GET_TAGS, requestDetails);
StopWatch w = new StopWatch();
@ -356,7 +356,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public TagList getTags(IIdType theResourceId) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, null);
ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, null, getContext());
notifyInterceptors(RestOperationTypeEnum.GET_TAGS, requestDetails);
StopWatch w = new StopWatch();
@ -368,7 +368,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public IBundleProvider history(Date theSince) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext());
notifyInterceptors(RestOperationTypeEnum.HISTORY_SYSTEM, requestDetails);
StopWatch w = new StopWatch();
@ -380,7 +380,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public IBundleProvider history(final IIdType theId, final Date theSince) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName());
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName(), getContext());
notifyInterceptors(RestOperationTypeEnum.HISTORY_INSTANCE, requestDetails);
final InstantDt end = createHistoryToTimestamp();
@ -495,7 +495,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public IBundleProvider history(Long theId, Date theSince) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName());
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext());
notifyInterceptors(RestOperationTypeEnum.HISTORY_TYPE, requestDetails);
StopWatch w = new StopWatch();
@ -507,7 +507,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public <MT extends IBaseMetaType> MT metaAddOperation(IIdType theResourceId, MT theMetaAdd) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, getResourceName());
ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, getResourceName(), getContext());
notifyInterceptors(RestOperationTypeEnum.META_ADD, requestDetails);
StopWatch w = new StopWatch();
@ -634,7 +634,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public <MT extends IBaseMetaType> MT metaDeleteOperation(IIdType theResourceId, MT theMetaDel) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, getResourceName());
ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, getResourceName(), getContext());
notifyInterceptors(RestOperationTypeEnum.META_DELETE, requestDetails);
StopWatch w = new StopWatch();
@ -675,7 +675,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName());
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext());
notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t WHERE t.myResourceType = :res_type)";
@ -714,7 +714,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType, IIdType theId) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName());
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName(), getContext());
notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
Set<TagDefinition> tagDefs = new HashSet<TagDefinition>();
@ -771,7 +771,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
validateResourceTypeAndThrowIllegalArgumentException(theId);
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName());
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName(), getContext());
RestOperationTypeEnum operationType = theId.hasVersionIdPart() ? RestOperationTypeEnum.VREAD : RestOperationTypeEnum.READ;
notifyInterceptors(operationType, requestDetails);
@ -863,7 +863,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public void removeTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName());
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName(), getContext());
notifyInterceptors(RestOperationTypeEnum.DELETE_TAGS, requestDetails);
StopWatch w = new StopWatch();
@ -904,7 +904,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public IBundleProvider search(final SearchParameterMap theParams) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName());
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext());
notifyInterceptors(RestOperationTypeEnum.SEARCH_TYPE, requestDetails);
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this);
@ -1051,7 +1051,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(resourceId, getResourceName(), theResource);
ActionRequestDetails requestDetails = new ActionRequestDetails(resourceId, getResourceName(), theResource, getContext());
notifyInterceptors(RestOperationTypeEnum.UPDATE, requestDetails);
// Perform update

View File

@ -64,7 +64,7 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
@Override
public void deleteAllTagsOnServer() {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext());
notifyInterceptors(RestOperationTypeEnum.DELETE_TAGS, requestDetails);
myEntityManager.createQuery("DELETE from ResourceTag t").executeUpdate();
@ -122,7 +122,7 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
@Override
public TagList getAllTags() {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext());
notifyInterceptors(RestOperationTypeEnum.GET_TAGS, requestDetails);
StopWatch w = new StopWatch();
@ -157,7 +157,7 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
@Override
public IBundleProvider history(Date theSince) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext());
notifyInterceptors(RestOperationTypeEnum.HISTORY_SYSTEM, requestDetails);
StopWatch w = new StopWatch();

View File

@ -60,7 +60,7 @@ public class FhirResourceDaoDstu1<T extends IResource> extends BaseHapiFhirResou
}
@Override
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage) {
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage, String theCode) {
OperationOutcome oo = new OperationOutcome();
oo.getIssueFirstRep().getSeverityElement().setValue(theSeverity);
oo.getIssueFirstRep().getDetailsElement().setValue(theMessage);

View File

@ -89,16 +89,17 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
}
@Override
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage) {
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage, String theCode) {
OperationOutcome oo = new OperationOutcome();
oo.getIssueFirstRep().getSeverityElement().setValue(theSeverity);
oo.getIssueFirstRep().getDiagnosticsElement().setValue(theMessage);
oo.getIssueFirstRep().getCodeElement().setValue(theCode);
return oo;
}
@Override
public MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile) {
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, null, theResource);
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, null, theResource, getContext());
notifyInterceptors(RestOperationTypeEnum.VALIDATE, requestDetails);
if (theMode == ValidationModeEnum.DELETE) {

View File

@ -26,13 +26,12 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.hl7.fhir.dstu21.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu21.hapi.validation.IValidationSupport;
import org.hl7.fhir.dstu21.exceptions.FHIRException;
import org.hl7.fhir.dstu21.model.IdType;
import org.hl7.fhir.dstu21.model.OperationOutcome;
import org.hl7.fhir.dstu21.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu21.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.hl7.fhir.dstu21.validation.IResourceValidator.BestPracticeWarningLevel;
import org.hl7.fhir.instance.model.OperationOutcome.IssueType;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -63,11 +62,26 @@ import ca.uhn.fhir.validation.ValidationResult;
public class FhirResourceDaoDstu21<T extends IAnyResource> extends BaseHapiFhirResourceDao<T> {
@Autowired()
@Qualifier("myJpaValidationSupportDstu21")
private IValidationSupport myJpaValidationSupport;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu21.class);
@Autowired()
@Qualifier("myInstanceValidatorDstu21")
private IValidatorModule myInstanceValidator;
@Override
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage, String theCode) {
OperationOutcome oo = new OperationOutcome();
OperationOutcomeIssueComponent issue = oo.addIssue();
issue.getSeverityElement().setValueAsString(theSeverity);
issue.setDiagnostics(theMessage);
try {
issue.setCode(org.hl7.fhir.dstu21.model.OperationOutcome.IssueType.fromCode(theCode));
} catch (FHIRException e) {
ourLog.error("Unknown code: {}", theCode);
}
return oo;
}
@Override
protected List<Object> getIncludeValues(FhirTerser theTerser, Include theInclude, IBaseResource theResource, RuntimeResourceDefinition theResourceDef) {
List<Object> values;
@ -86,18 +100,9 @@ public class FhirResourceDaoDstu21<T extends IAnyResource> extends BaseHapiFhirR
return values;
}
@Override
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage) {
OperationOutcome oo = new OperationOutcome();
OperationOutcomeIssueComponent issue = oo.addIssue();
issue.getSeverityElement().setValueAsString(theSeverity);
issue.setDiagnostics(theMessage);
return oo;
}
@Override
public MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile) {
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, null, theResource);
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, null, theResource, getContext());
notifyInterceptors(RestOperationTypeEnum.VALIDATE, requestDetails);
if (theMode == ValidationModeEnum.DELETE) {
@ -111,7 +116,7 @@ public class FhirResourceDaoDstu21<T extends IAnyResource> extends BaseHapiFhirR
List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>();
validateOkToDelete(deleteConflicts, entity);
validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setSeverity(IssueSeverity.INFORMATION).setDiagnostics("Ok to delete");
return new MethodOutcome(new IdType(theId.getValue()), oo);
@ -119,10 +124,7 @@ public class FhirResourceDaoDstu21<T extends IAnyResource> extends BaseHapiFhirR
FhirValidator validator = getContext().newValidator();
FhirInstanceValidator val = new FhirInstanceValidator();
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
// val.setValidationSupport(new ValidationSupportChain(new DefaultProfileValidationSupport(), myJpaValidationSupport));
validator.registerValidatorModule(val);
validator.registerValidatorModule(myInstanceValidator);
validator.registerValidatorModule(new IdChecker(theMode));
@ -151,6 +153,12 @@ public class FhirResourceDaoDstu21<T extends IAnyResource> extends BaseHapiFhirR
myMode = theMode;
}
@CoverageIgnore
@Override
public void validateBundle(IValidationContext<Bundle> theContext) {
throw new UnsupportedOperationException();
}
@Override
public void validateResource(IValidationContext<IBaseResource> theCtx) {
boolean hasId = theCtx.getResource().getIdElement().hasIdPart();
@ -166,12 +174,6 @@ public class FhirResourceDaoDstu21<T extends IAnyResource> extends BaseHapiFhirR
}
@CoverageIgnore
@Override
public void validateBundle(IValidationContext<Bundle> theContext) {
throw new UnsupportedOperationException();
}
}
}

View File

@ -68,7 +68,7 @@ public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2<Patient>im
@Override
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName());
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext());
notifyInterceptors(RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE, requestDetails);
return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative);
@ -77,7 +77,7 @@ public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2<Patient>im
@Override
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName());
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext());
notifyInterceptors(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, requestDetails);
return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative);

View File

@ -68,7 +68,7 @@ public class FhirResourceDaoPatientDstu21 extends FhirResourceDaoDstu21<Patient>
@Override
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName());
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext());
notifyInterceptors(RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE, requestDetails);
return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative);
@ -77,7 +77,7 @@ public class FhirResourceDaoPatientDstu21 extends FhirResourceDaoDstu21<Patient>
@Override
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName());
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext());
notifyInterceptors(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, requestDetails);
return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative);

View File

@ -22,7 +22,6 @@ package ca.uhn.fhir.jpa.dao;
import javax.annotation.PostConstruct;
import org.hl7.fhir.dstu21.hapi.validation.FhirQuestionnaireResponseValidator;
import org.hl7.fhir.dstu21.model.OperationOutcome;
import org.hl7.fhir.dstu21.model.Questionnaire;
import org.hl7.fhir.dstu21.model.QuestionnaireResponse;
@ -31,20 +30,24 @@ import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.IResourceLoader;
import ca.uhn.fhir.validation.IValidatorModule;
import ca.uhn.fhir.validation.ValidationResult;
public class FhirResourceDaoQuestionnaireResponseDstu21 extends FhirResourceDaoDstu21<QuestionnaireResponse> {
private Boolean myValidateResponses;
@Autowired
private IJpaValidationSupportDstu21 myValidationSupport;
@Qualifier("myQuestionnaireResponseValidatorDstu21")
private IValidatorModule myQuestionnaireResponseValidatorDstu21;
/**
* Initialize the bean
@ -75,9 +78,7 @@ public class FhirResourceDaoQuestionnaireResponseDstu21 extends FhirResourceDaoD
val.setValidateAgainstStandardSchema(false);
val.setValidateAgainstStandardSchematron(false);
FhirQuestionnaireResponseValidator module = new FhirQuestionnaireResponseValidator();
module.setValidationSupport(myValidationSupport);
val.registerValidatorModule(module);
val.registerValidatorModule(myQuestionnaireResponseValidatorDstu21);
ValidationResult result = val.validateWithResult(getContext().newJsonParser().parseResource(getContext().newJsonParser().encodeResourceToString(qa)));
if (!result.isSuccessful()) {

View File

@ -64,7 +64,7 @@ public class FhirSystemDaoDstu1 extends BaseHapiFhirSystemDao<List<IResource>, M
ourLog.info("Beginning transaction with {} resources", theResources.size());
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext());
notifyInterceptors(RestOperationTypeEnum.TRANSACTION, requestDetails);
long start = System.currentTimeMillis();

View File

@ -200,7 +200,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
@Override
public MetaDt metaGetOperation() {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext());
notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)";
@ -259,7 +259,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
@Transactional(propagation = Propagation.REQUIRED)
@Override
public Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest) {
ActionRequestDetails requestDetails = new ActionRequestDetails(null, "Bundle", theRequest);
ActionRequestDetails requestDetails = new ActionRequestDetails(null, "Bundle", theRequest, getContext());
notifyInterceptors(RestOperationTypeEnum.TRANSACTION, requestDetails);
String actionName = "Transaction";

View File

@ -201,7 +201,7 @@ public class FhirSystemDaoDstu21 extends BaseHapiFhirSystemDao<Bundle, Meta> {
@Override
public Meta metaGetOperation() {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext());
notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)";
@ -260,7 +260,7 @@ public class FhirSystemDaoDstu21 extends BaseHapiFhirSystemDao<Bundle, Meta> {
@Transactional(propagation = Propagation.REQUIRED)
@Override
public Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest) {
ActionRequestDetails requestDetails = new ActionRequestDetails(null, "Bundle", theRequest);
ActionRequestDetails requestDetails = new ActionRequestDetails(null, "Bundle", theRequest, getContext());
notifyInterceptors(RestOperationTypeEnum.TRANSACTION, requestDetails);
String actionName = "Transaction";

View File

@ -74,6 +74,7 @@ import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.dstu2.valueset.IssueTypeEnum;
import ca.uhn.fhir.model.dstu2.valueset.QuantityComparatorEnum;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DateDt;
@ -297,9 +298,10 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
@Test
public void testCreateOperationOutcomeError() {
FhirResourceDaoDstu2<Bundle> dao = new FhirResourceDaoDstu2<Bundle>();
OperationOutcome oo = (OperationOutcome) dao.createErrorOperationOutcome("my message");
OperationOutcome oo = (OperationOutcome) dao.createErrorOperationOutcome("my message", "incomplete");
assertEquals(IssueSeverityEnum.ERROR.getCode(), oo.getIssue().get(0).getSeverity());
assertEquals("my message", oo.getIssue().get(0).getDiagnostics());
assertEquals(IssueTypeEnum.INCOMPLETE_RESULTS, oo.getIssue().get(0).getCodeElement().getValueAsEnum());
}
@Test

View File

@ -83,7 +83,7 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
//@formatter:off
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= {TestDstu21Config.class /*, ca.uhn.fhir.jpa.config.WebsocketDstu21Config.class*/ })
@ContextConfiguration(classes= {TestDstu21Config.class})
//@formatter:on
public abstract class BaseJpaDstu21Test extends BaseJpaTest {

View File

@ -52,6 +52,7 @@ import org.hl7.fhir.dstu21.model.Meta;
import org.hl7.fhir.dstu21.model.Observation;
import org.hl7.fhir.dstu21.model.OperationOutcome;
import org.hl7.fhir.dstu21.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu21.model.OperationOutcome.IssueType;
import org.hl7.fhir.dstu21.model.Organization;
import org.hl7.fhir.dstu21.model.Patient;
import org.hl7.fhir.dstu21.model.Period;
@ -293,9 +294,10 @@ public class FhirResourceDaoDstu21Test extends BaseJpaDstu21Test {
@Test
public void testCreateOperationOutcomeError() {
FhirResourceDaoDstu21<Bundle> dao = new FhirResourceDaoDstu21<Bundle>();
OperationOutcome oo = (OperationOutcome) dao.createErrorOperationOutcome("my message");
OperationOutcome oo = (OperationOutcome) dao.createErrorOperationOutcome("my message", "incomplete");
assertEquals(IssueSeverity.ERROR.toCode(), oo.getIssue().get(0).getSeverity().toCode());
assertEquals("my message", oo.getIssue().get(0).getDiagnostics());
assertEquals(IssueType.INCOMPLETE, oo.getIssue().get(0).getCode());
}
@Test
@ -304,6 +306,7 @@ public class FhirResourceDaoDstu21Test extends BaseJpaDstu21Test {
OperationOutcome oo = (OperationOutcome) dao.createInfoOperationOutcome("my message");
assertEquals(IssueSeverity.INFORMATION.toCode(), oo.getIssue().get(0).getSeverity().toCode());
assertEquals("my message", oo.getIssue().get(0).getDiagnostics());
assertEquals(IssueType.INFORMATIONAL, oo.getIssue().get(0).getCode());
}
@Test

View File

@ -12,6 +12,7 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.config.WebsocketDstu21Config;
@ -32,7 +33,10 @@ import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor;
import ca.uhn.fhir.validation.IValidatorModule;
import ca.uhn.fhirtest.config.TestDstu21Config;
import ca.uhn.fhirtest.config.TestDstu2Config;
@ -42,7 +46,7 @@ public class TestRestfulServer extends RestfulServer {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestRestfulServer.class);
private AnnotationConfigApplicationContext myAppCtx;
private AnnotationConfigWebApplicationContext myAppCtx;
@SuppressWarnings("unchecked")
@Override
@ -72,7 +76,11 @@ public class TestRestfulServer extends RestfulServer {
String baseUrlProperty;
switch (fhirVersionParam.trim().toUpperCase()) {
case "DSTU1": {
myAppCtx = new AnnotationConfigApplicationContext(ca.uhn.fhirtest.config.TestDstu1Config.class);
myAppCtx = new AnnotationConfigWebApplicationContext();
myAppCtx.setServletConfig(getServletConfig());
myAppCtx.setParent(parentAppCtx);
myAppCtx.register(ca.uhn.fhirtest.config.TestDstu1Config.class);
myAppCtx.refresh();
setFhirContext(FhirContext.forDstu1());
beans = myAppCtx.getBean("myResourceProvidersDstu1", List.class);
systemProviderDstu1 = myAppCtx.getBean("mySystemProviderDstu1", JpaSystemProviderDstu1.class);
@ -85,7 +93,8 @@ public class TestRestfulServer extends RestfulServer {
break;
}
case "DSTU2": {
myAppCtx = new AnnotationConfigApplicationContext();
myAppCtx = new AnnotationConfigWebApplicationContext();
myAppCtx.setServletConfig(getServletConfig());
myAppCtx.setParent(parentAppCtx);
myAppCtx.register(TestDstu2Config.class, WebsocketDstu2Config.class);
myAppCtx.refresh();
@ -101,7 +110,8 @@ public class TestRestfulServer extends RestfulServer {
break;
}
case "DSTU21": {
myAppCtx = new AnnotationConfigApplicationContext();
myAppCtx = new AnnotationConfigWebApplicationContext();
myAppCtx.setServletConfig(getServletConfig());
myAppCtx.setParent(parentAppCtx);
myAppCtx.register(TestDstu21Config.class, WebsocketDstu21Config.class);
myAppCtx.refresh();

View File

@ -9,6 +9,8 @@ import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.time.DateUtils;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -23,6 +25,9 @@ import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu21;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu21;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor;
import ca.uhn.fhir.validation.IValidatorModule;
@Configuration
@Import(CommonConfig.class)
@ -35,13 +40,13 @@ public class TestDstu21Config extends BaseJavaConfigDstu21 {
@Value("${fhir.lucene.location.dstu21}")
private String myFhirLuceneLocation;
/**
* This lets the "@Value" fields reference properties from the properties file
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Autowired
@Qualifier("myInstanceValidatorDstu21")
private IValidatorModule myInstanceValidatorDstu21;
@Autowired
@Qualifier("myQuestionnaireResponseValidatorDstu21")
private IValidatorModule myQuestionnaireResponseValidatorDstu21;
@Bean()
public DaoConfig daoConfig() {
@ -65,13 +70,6 @@ public class TestDstu21Config extends BaseJavaConfigDstu21 {
return retVal;
}
@Bean()
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager retVal = new JpaTransactionManager();
retVal.setEntityManagerFactory(entityManagerFactory);
return retVal;
}
@Bean()
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean();
@ -94,16 +92,54 @@ public class TestDstu21Config extends BaseJavaConfigDstu21 {
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
extraProperties.put("hibernate.cache.use_structured_entries", "false");
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
extraProperties.put("hibernate.search.default.directory_provider" ,"filesystem");
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
extraProperties.put("hibernate.search.default.indexBase", myFhirLuceneLocation);
extraProperties.put("hibernate.search.lucene_version","LUCENE_CURRENT");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
return extraProperties;
}
@Bean(autowire=Autowire.BY_TYPE)
/**
* Bean which validates incoming requests
*/
@Bean
public IServerInterceptor requestValidatingInterceptor() {
RequestValidatingInterceptor requestValidator = new RequestValidatingInterceptor();
requestValidator.addValidatorModule(myInstanceValidatorDstu21);
requestValidator.addValidatorModule(myQuestionnaireResponseValidatorDstu21);
requestValidator.setResponseHeaderValueNoIssues("Validation did not detect any issues");
return requestValidator;
}
/**
* Bean which validates outgoing responses
*/
@Bean
public IServerInterceptor responseValidatingInterceptor() {
ResponseValidatingInterceptor responseValidator = new ResponseValidatingInterceptor();
responseValidator.addValidatorModule(myInstanceValidatorDstu21);
responseValidator.addValidatorModule(myQuestionnaireResponseValidatorDstu21);
responseValidator.setResponseHeaderValueNoIssues("Validation did not detect any issues");
responseValidator.setFailOnSeverity(null);
return responseValidator;
}
@Bean(autowire = Autowire.BY_TYPE)
public IServerInterceptor subscriptionSecurityInterceptor() {
return new SubscriptionsRequireManualActivationInterceptorDstu21();
}
@Bean()
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager retVal = new JpaTransactionManager();
retVal.setEntityManagerFactory(entityManagerFactory);
return retVal;
}
/**
* This lets the "@Value" fields reference properties from the properties file
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}

View File

@ -13,8 +13,9 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.validation.IValidationContext;
import ca.uhn.fhir.validation.IValidatorModule;
public class FhirQuestionnaireResponseValidator extends BaseValidatorBridge {
public class FhirQuestionnaireResponseValidator extends BaseValidatorBridge implements IValidatorModule {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirQuestionnaireResponseValidator.class);
private IValidationSupport myValidationSupport;

View File

@ -1,55 +0,0 @@
package org.hl7.fhir.dstu21.model.annotations;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Class annotation used to indicate a class which is a "block"/"component" type. A block
* is a nested group of fields within a resource definition and can contain other blocks as
* well as data types.
* <p>
* An example of a block would be Patient.contact
* </p>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value= {ElementType.TYPE})
public @interface Block {
/**
* @deprecated Do not use, will be removed
*/
@Deprecated
String name() default "";
}

View File

@ -1,165 +0,0 @@
package org.hl7.fhir.dstu21.model.annotations;
/*
* #%L
* HAPI FHIR Structures - HL7.org DSTU2
* %%
* Copyright (C) 2014 - 2015 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%
*/
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.hl7.fhir.dstu21.model.Enumeration;
import org.hl7.fhir.dstu21.model.api.IBase;
import org.hl7.fhir.dstu21.model.api.IBaseEnumFactory;
/**
* Field annotation for fields within resource and datatype definitions, indicating
* a child of that type.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value= {ElementType.FIELD})
public @interface Child {
/**
* Constant value to supply for {@link #order()} when the order is defined
* elsewhere
*/
int ORDER_UNKNOWN = -1;
/**
* Constant value to supply for {@link #max()} to indicate '*' (no maximum)
*/
int MAX_UNLIMITED = -1;
/**
* Constant value to supply for {@link #order()} to indicate that this child should replace the
* entry in the superclass with the same name (and take its {@link Child#order() order} value
* in the process). This is useful if you wish to redefine an existing field in a resource/type
* definition in order to constrain/extend it.
*/
int REPLACE_PARENT = -2;
/**
* The name of this field, as it will appear in serialized versions of the message
*/
String name();
/**
* The order in which this field comes within its parent. The first field should have a
* value of 0, the second a value of 1, etc.
*/
int order() default ORDER_UNKNOWN;
/**
* The minimum number of repetitions allowed for this child
*/
int min() default 0;
/**
* The maximum number of repetitions allowed for this child. Should be
* set to {@link #MAX_UNLIMITED} if there is no limit to the number of
* repetitions.
*/
int max() default 1;
/**
* Lists the allowable types for this field, if the field supports multiple
* types (otherwise does not need to be populated).
* <p>
* For example, if this field supports either DateTimeDt or BooleanDt types,
* those two classes should be supplied here.
* </p>
*/
Class<? extends IBase>[] type() default {};
/**
* For children which accept an {@link Enumeration} as the type, this
* field indicates the type to use for the enum factory
*/
Class<? extends IBaseEnumFactory<?>> enumFactory() default NoEnumFactory.class;
/**
* Is this element a modifier?
*/
boolean modifier() default false;
/**
* Should this element be included in the summary view
*/
boolean summary() default false;
// Not implemented
// /**
// * This value is used when extending a built-in model class and defining a
// * field to replace a field within the built-in class. For example, the {@link Patient}
// * resource has a {@link Patient#getName() name} field, but if you wanted to extend Patient and
// * provide your own implementation of {@link HumanNameDt} (most likely your own subclass of
// * HumanNameDt which adds extensions of your choosing) you could do that using a replacement field.
// */
// String replaces() default "";
public static class NoEnumFactory implements IBaseEnumFactory<Enum<?>> {
private NoEnumFactory() {
// non instantiable
}
@Override
public Enum<?> fromCode(String theCodeString) throws IllegalArgumentException {
return null;
}
@Override
public String toCode(Enum<?> theCode) {
return null;
}
}
}

View File

@ -1,88 +0,0 @@
package org.hl7.fhir.dstu21.model.annotations;
/*
* #%L
* HAPI FHIR Structures - HL7.org DSTU2
* %%
* Copyright (C) 2014 - 2015 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%
*/
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.hl7.fhir.dstu21.model.api.IBaseDatatype;
/**
* Class annotation to note a class which defines a datatype
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value= {ElementType.TYPE})
public @interface DatatypeDef {
/**
* The defined name of this datatype
*/
String name();
/**
* Set this to true (default is false) for any types that are
* really only a specialization of another type. For example,
* {@link BoundCodeDt} is really just a specific type of
* {@link CodeDt} and not a separate datatype, so it should
* have this set to true.
*/
boolean isSpecialization() default false;
/**
* Indicates that this datatype is a profile of the given datatype, which
* implies certain parsing/encoding rules (e.g. a choice element named
* foo[x] which allows a Markdown value will still be encoded as
* fooString because Markdown is a profile of string.
*/
Class<? extends IBaseDatatype> profileOf() default IBaseDatatype.class;
}

View File

@ -1,55 +0,0 @@
package org.hl7.fhir.dstu21.model.annotations;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation which may be placed on a resource/datatype definition, or a field, or
* a search parameter definition in order to provide documentation for that item.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value= {ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER, ElementType.METHOD})
public @interface Description {
/**
* Optional short name for this child
*/
String shortDefinition() default "";
/**
* Optional formal definition for this child
*/
String formalDefinition() default "";
}

View File

@ -1,74 +0,0 @@
package org.hl7.fhir.dstu21.model.annotations;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Field modifier to be placed on a child field (a field also annotated with the {@link Child} annotation) which
* indicates that this field is an extension.
*/
@Target(value = { ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Extension {
/**
* This parameter affects how the extension is treated when the element definition containing this resource is
* exported to a profile.
*
* <p>
* If set to <b><code>true</code></b>, the resource is taken to be a local resource and its definition is exported
* along with the reference. Use this option for extension defintions that you have added locally (i.e. within your
* own organization)
* </p>
*
* <p>
* If set to <b><code>false</code></b>, the resource is taken to be a remote resource and its definition is
* <b>not</b> exported to the profile. Use this option for extensions that are defined by other organizations (i.e.
* by regional authorities or jurisdictional governments)
* </p>
*/
boolean definedLocally();
/**
* Returns <code>true</code> if this extension is a <a
* href="http://www.hl7.org/implement/standards/fhir/extensibility.html#modifierExtension">modifier extension</a>
*/
boolean isModifier();
/**
* The URL associated with this extension
*/
String url();
}

View File

@ -1,59 +0,0 @@
package org.hl7.fhir.dstu21.model.annotations;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Class annotation which indicates a resource definition class
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value= {ElementType.TYPE})
public @interface ResourceDef {
/**
* The name of the resource (e.g. "Patient" or "DiagnosticReport")
*/
String name();
/**
* Not currently used
*/
String id() default "";
/**
* The URL indicating the profile for this resource definition, if known
*/
String profile() default "";
}

View File

@ -1,79 +0,0 @@
package org.hl7.fhir.dstu21.model.annotations;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.hl7.fhir.dstu21.model.Resource;
@Target(value=ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SearchParamDefinition {
/**
* The name for this parameter
*/
String name();
/**
* The path for this parameter
*/
String path();
/**
* A description of this parameter
*/
String description() default "";
/**
* The type for this parameter, e.g. "string", or "token"
*/
String type() default "string";
/**
* If the parameter is of type "composite", this parameter lists the names of the parameters
* which this parameter is a composite of. E.g. "name-value-token" is a composite of "name" and "value-token".
* <p>
* If the parameter is not a composite, this parameter must be empty
* </p>
*/
String[] compositeOf() default {};
/**
* For search params of type "reference", this can optionally be used to
* specify the resource type(s) that this parameter applies to.
*/
Class<? extends Resource>[] target() default {};
}

View File

@ -1,34 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IAnyResource extends IBaseResource {
String getId();
IAnyResource setId(String theId);
IIdType getIdElement();
IBaseMetaType getMeta();
}

View File

@ -1,26 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IBackboneElement extends IBase {
}

View File

@ -1,32 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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%
*/
/**
* This interface is a simple marker for anything which is an HL7
* structure of some kind. It is provided mostly to simplify convergence
* between the HL7.org structures and the HAPI ones.
*/
public interface IBase {
boolean isEmpty();
}

View File

@ -1,26 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IBaseBackboneElement extends IBase, IBaseHasExtensions, IBaseHasModifierExtensions {
}

View File

@ -1,37 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IBaseBinary extends IBaseResource {
byte[] getContent();
String getContentAsBase64();
String getContentType();
IBaseBinary setContent(byte[] theContent);
IBaseBinary setContentAsBase64(String theContent);
IBaseBinary setContentType(String theContentType);
}

View File

@ -1,26 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IBaseBooleanDatatype extends IPrimitiveType<Boolean> {
}

View File

@ -1,48 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IBaseBundle extends IBaseResource {
/**
* Constant for links provided in the bundle. This constant is used in the
* link.type field to indicate that the given link is for
* the next page of results.
*/
public static final String LINK_NEXT = "next";
/**
* Constant for links provided in the bundle. This constant is used in the
* link.type field to indicate that the given link is for
* the previous page of results.
*/
public static final String LINK_PREV = "prev";
/**
* Constant for links provided in the bundle. This constant is used in the
* link.type field to indicate that the given link is for
* this bundle.
*/
public static final String LINK_SELF = "self";
}

View File

@ -1,31 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IBaseCoding {
IBaseCoding setSystem(String theScheme);
IBaseCoding setCode(String theTerm);
IBaseCoding setDisplay(String theLabel);
}

View File

@ -1,6 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
public interface IBaseConformance extends IBaseResource {
}

View File

@ -1,26 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IBaseDatatype extends IBase {
}

View File

@ -1,26 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IBaseDatatypeElement extends IBase {
}

View File

@ -1,27 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.math.BigDecimal;
public interface IBaseDecimalDatatype extends IPrimitiveType<BigDecimal> {
}

View File

@ -1,27 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IBaseElement {
IBaseElement setId(String theValue);
}

View File

@ -1,42 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IBaseEnumFactory<T extends Enum<?>> {
/**
* Read an enumeration value from the string that represents it on the XML or JSON
*
* @param codeString the value found in the XML or JSON
* @return the enumeration value
* @throws IllegalArgumentException is the value is not known
*/
public T fromCode(String codeString) throws IllegalArgumentException;
/**
* Get the XML/JSON representation for an enumerated value
*
* @param code - the enumeration value
* @return the XML/JSON representation
*/
public String toCode(T code);
}

View File

@ -1,28 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IBaseEnumeration<T extends Enum<?>> extends IPrimitiveType<T> {
// Marker interface
}

View File

@ -1,37 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.List;
public interface IBaseExtension<T, D> extends ICompositeType {
List<T> getExtension();
String getUrl();
IBaseDatatype getValue();
T setUrl(String theUrl);
T setValue(IBaseDatatype theValue);
}

View File

@ -1,66 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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%
*/
/*
Copyright (c) 2011+, HL7, Inc
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Interface to be implemented by all built-in FHIR enumerations (i.e. the
* actual FHIR-defined Java Enum will implement this interface)
*/
public interface IBaseFhirEnum {
/**
* Get the XML/JSON representation for an enumerated value
* @return the XML/JSON representation
*/
public String toCode();
}

View File

@ -1,31 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.List;
public interface IBaseHasExtensions {
public List<? extends IBaseExtension<?, ?>> getExtension();
public IBaseExtension<?, ?> addExtension();
}

View File

@ -1,31 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.List;
public interface IBaseHasModifierExtensions {
public List<? extends IBaseExtension<?, ?>> getModifierExtension();
public IBaseExtension<?, ?> addModifierExtension();
}

View File

@ -1,26 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IBaseIntegerDatatype extends IPrimitiveType<Integer> {
}

View File

@ -1,35 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.Date;
public interface IBaseMetaType extends ICompositeType {
IBaseCoding addTag();
IBaseMetaType setLastUpdated(Date theHeaderDateValue);
Date getLastUpdated();
String getVersionId();
}

View File

@ -1,5 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
public interface IBaseOperationOutcome extends IBaseResource {
}

View File

@ -1,26 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IBaseParameters extends IBaseResource {
}

View File

@ -1,37 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IBaseReference extends ICompositeType {
IBaseResource getResource();
void setResource(IBaseResource theResource);
IIdType getReferenceElement();
IBaseReference setReference(String theReference);
IBase setDisplay(String theValue);
IPrimitiveType<String> getDisplayElement();
}

View File

@ -1,40 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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%
*/
/**
* For now, this is a simple marker interface indicating that a class is a resource type.
* There are two concrete types of implementations of this interrface. The first are
* HL7.org's Resource structures (e.g.
* <code>org.hl7.fhir.instance.model.Patient</code>) and
* the second are HAPI's Resource structures, e.g.
* <code>ca.uhn.fhir.model.dstu.resource.Patient</code>)
*/
public interface IBaseResource extends IBase {
IIdType getIdElement();
IBaseResource setId(String theId);
IBaseResource setId(IIdType theId);
}

View File

@ -1,6 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
public interface IBaseXhtml extends IPrimitiveType<String> {
}

View File

@ -1,26 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface ICompositeType extends IBaseDatatype {
}

View File

@ -1,31 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.List;
public interface IDomainResource extends IAnyResource {
List<? extends IAnyResource> getContained();
INarrative getText();
}

View File

@ -1,138 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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%
*/
/**
* Base interface for ID datatype.
*
* <p>
* <b>Concrete Implementations:</b> This interface is often returned and/or accepted by methods in HAPI's API
* where either {@link ca.uhn.fhir.model.primitive.IdDt} (the HAPI structure ID type) or
* <code>org.hl7.fhir.instance.model.IdType</code> (the RI structure ID type) will be used, depending on
* which version of the strctures your application is using.
* </p>
*/
public interface IIdType {
void applyTo(IBaseResource theResource);
/**
* Returns the server base URL if this ID contains one. For example, the base URL is
* the 'http://example.com/fhir' in the following ID: <code>http://example.com/fhir/Patient/123/_history/55</code>
*/
String getBaseUrl();
/**
* Returns only the logical ID part of this ID. For example, given the ID
* "http://example,.com/fhir/Patient/123/_history/456", this method would
* return "123".
*/
String getIdPart();
/**
* Returns the ID part of this ID (e.g. in the ID http://example.com/Patient/123/_history/456 this would be the
* part "123") parsed as a {@link Long}.
*
* @throws NumberFormatException If the value can't be parsed as a long
*/
Long getIdPartAsLong();
String getResourceType();
/**
* Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a simple ID. Use {@link #getIdPart()} to get just the ID portion.
*
* @see #getIdPart()
*/
String getValue();
String getVersionIdPart();
/**
* Returns the version ID part of this ID (e.g. in the ID http://example.com/Patient/123/_history/456 this would be the
* part "456") parsed as a {@link Long}.
*
* @throws NumberFormatException If the value can't be parsed as a long
*/
Long getVersionIdPartAsLong();
boolean hasBaseUrl();
/**
* Returns <code>true</code> if this ID contains an actual ID part. For example, the ID part is
* the '123' in the following ID: <code>http://example.com/fhir/Patient/123/_history/55</code>
*/
boolean hasIdPart();
boolean hasResourceType();
boolean hasVersionIdPart();
/**
* Returns <code>true</code> if this ID contains an absolute URL (in other words, a URL starting with "http://" or "https://"
*/
boolean isAbsolute();
boolean isEmpty();
/**
* Returns <code>true</code> if the {@link #getIdPart() ID part of this object} is valid according to the FHIR rules for valid IDs.
* <p>
* The FHIR specification states:
* <code>Any combination of upper or lower case ASCII letters ('A'..'Z', and 'a'..'z', numerals ('0'..'9'), '-' and '.', with a length limit of 64 characters. (This might be an integer, an un-prefixed OID, UUID or any other identifier pattern that meets these constraints.) regex: [A-Za-z0-9\-\.]{1,64}</code>
* </p>
*/
boolean isIdPartValid();
/**
* Returns <code>true</code> if the {@link #getIdPart() ID part of this object} contains
* only numbers
*/
boolean isIdPartValidLong();
/**
* Returns <code>true</code> if the ID is a local reference (in other words, it begins with the '#' character)
*/
boolean isLocal();
/**
* Returns <code>true</code> if the {@link #getVersionIdPart() version ID part of this object} contains
* only numbers
*/
boolean isVersionIdPartValidLong();
IIdType setValue(String theString);
IIdType toUnqualified();
IIdType toUnqualifiedVersionless();
IIdType toVersionless();
IIdType withResourceType(String theResName);
IIdType withServerBase(String theServerBase, String theResourceName);
IIdType withVersion(String theVersion);
}

View File

@ -1,34 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface INarrative extends ICompositeType {
boolean isEmpty();
// TODO: use less broad exception type here
public void setDivAsString(String theString) throws Exception;
// TODO: use less broad exception type here
public String getDivAsString() throws Exception;
}

View File

@ -1,35 +0,0 @@
package org.hl7.fhir.dstu21.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IPrimitiveType<T> extends IBaseDatatype {
void setValueAsString(String theValue) throws IllegalArgumentException;
String getValueAsString();
T getValue();
IPrimitiveType<T> setValue(T theValue) throws IllegalArgumentException;
}

View File

@ -0,0 +1,311 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu21.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu21.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.dstu21.model.IdType;
import org.hl7.fhir.dstu21.model.Patient;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.validation.IValidatorModule;
public class RequestValidatingInterceptorTest {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static boolean ourLastRequestWasSearch;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RequestValidatingInterceptorTest.class);
private static int ourPort;
private static Server ourServer;
private static RestfulServer ourServlet;
private RequestValidatingInterceptor myInterceptor;
@Before
public void before() {
ourLastRequestWasSearch = false;
while (ourServlet.getInterceptors().size() > 0) {
ourServlet.unregisterInterceptor(ourServlet.getInterceptors().get(0));
}
myInterceptor = new RequestValidatingInterceptor();
// myInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
// myInterceptor.setAddResponseHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
// myInterceptor.setResponseHeaderName("X-RESP");
// myInterceptor.setResponseHeaderValue(RequestValidatingInterceptor.DEFAULT_RESPONSE_HEADER_VALUE);
ourServlet.registerInterceptor(myInterceptor);
}
@Test
public void testCreateJsonInvalidNoValidatorsSpecified() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
patient.setGender(AdministrativeGender.MALE);
patient.addContact().addRelationship().setText("FOO");
String encoded = ourCtx.newJsonParser().encodeResourceToString(patient);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity(encoded, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.info("Response was:\n{}", responseContent);
assertEquals(422, status.getStatusLine().getStatusCode());
assertThat(status.toString(), containsString("X-HAPI-Request-Validation"));
assertThat(responseContent, containsString("<severity value=\"error\"/>"));
}
@Test
public void testCreateJsonInvalidNoFailure() throws Exception {
myInterceptor.setFailOnSeverity(null);
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
patient.setGender(AdministrativeGender.MALE);
patient.addContact().addRelationship().setText("FOO");
String encoded = ourCtx.newJsonParser().encodeResourceToString(patient);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity(encoded, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.info("Response was:\n{}", responseContent);
assertEquals(201, status.getStatusLine().getStatusCode());
assertThat(status.toString(), containsString("X-HAPI-Request-Validation"));
assertThat(responseContent, not(containsString("<severity value=\"error\"/>")));
}
@Test
public void testCreateJsonValidNoValidatorsSpecified() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
patient.setGender(AdministrativeGender.MALE);
String encoded = ourCtx.newJsonParser().encodeResourceToString(patient);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity(encoded, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.trace("Response was:\n{}", responseContent);
assertEquals(201, status.getStatusLine().getStatusCode());
assertThat(status.toString(), not(containsString("X-HAPI-Request-Validation")));
}
@Test
public void testCreateJsonValidNoValidatorsSpecifiedDefaultMessage() throws Exception {
myInterceptor.setResponseHeaderValueNoIssues("NO ISSUES");
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
patient.setGender(AdministrativeGender.MALE);
String encoded = ourCtx.newJsonParser().encodeResourceToString(patient);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity(encoded, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.trace("Response was:\n{}", responseContent);
assertEquals(201, status.getStatusLine().getStatusCode());
assertThat(status.toString(), (containsString("X-HAPI-Request-Validation: NO ISSUES")));
}
@Test
public void testCreateXmlInvalidNoValidatorsSpecified() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
patient.setGender(AdministrativeGender.MALE);
patient.addContact().addRelationship().setText("FOO");
String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity(encoded, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.info("Response was:\n{}", responseContent);
assertEquals(422, status.getStatusLine().getStatusCode());
assertThat(status.toString(), containsString("X-HAPI-Request-Validation"));
}
@Test
public void testCreateXmlInvalidInstanceValidator() throws Exception {
IValidatorModule module = new FhirInstanceValidator();
myInterceptor.addValidatorModule(module);
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
patient.setGender(AdministrativeGender.MALE);
patient.addContact().addRelationship().setText("FOO");
String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity(encoded, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.info("Response was:\n{}", responseContent);
assertEquals(422, status.getStatusLine().getStatusCode());
assertThat(status.toString(), containsString("X-HAPI-Request-Validation"));
}
@Test
public void testSearch() throws Exception {
HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient?foo=bar");
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(status.toString(), not(containsString("X-HAPI-Request-Validation")));
assertEquals(true, ourLastRequestWasSearch);
}
@Test
public void testCreateXmlValidNoValidatorsSpecified() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
patient.setGender(AdministrativeGender.MALE);
String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity(encoded, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.trace("Response was:\n{}", responseContent);
assertEquals(201, status.getStatusLine().getStatusCode());
assertThat(status.toString(), not(containsString("X-HAPI-Request-Validation")));
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
PatientProvider patientProvider = new PatientProvider();
ServletHandler proxyHandler = new ServletHandler();
ourServlet = new RestfulServer(ourCtx);
ourServlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(ourServlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class PatientProvider implements IResourceProvider {
@Create()
public MethodOutcome createPatient(@ResourceParam Patient thePatient, @IdParam IdType theIdParam) {
return new MethodOutcome(new IdDt("Patient/001/_history/002"));
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Search
public List<IResource> search(@OptionalParam(name="foo") StringParam theString) {
ourLastRequestWasSearch = true;
return new ArrayList<IResource>();
}
}
}

View File

@ -0,0 +1,253 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu21.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu21.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.dstu21.model.Patient;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.validation.IValidatorModule;
public class ResponseValidatingInterceptorTest {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResponseValidatingInterceptorTest.class);
private static int ourPort;
private static Server ourServer;
private static RestfulServer ourServlet;
private ResponseValidatingInterceptor myInterceptor;
public static IBaseResource myReturnResource;
@Before
public void before() {
myReturnResource = null;
while (ourServlet.getInterceptors().size() > 0) {
ourServlet.unregisterInterceptor(ourServlet.getInterceptors().get(0));
}
myInterceptor = new ResponseValidatingInterceptor();
// myInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
// myInterceptor.setAddResponseHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
// myInterceptor.setResponseHeaderName("X-RESP");
// myInterceptor.setResponseHeaderValue(RequestValidatingInterceptor.DEFAULT_RESPONSE_HEADER_VALUE);
ourServlet.registerInterceptor(myInterceptor);
}
/**
* Ignored until #264 is fixed
*/
@Test
@Ignore
public void testSearchJsonInvalidNoValidatorsSpecified() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
patient.setGender(AdministrativeGender.MALE);
patient.addContact().addRelationship().setText("FOO");
myReturnResource = patient;
HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient?foo=bar");
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.info("Response was:\n{}", responseContent);
assertEquals(422, status.getStatusLine().getStatusCode());
assertThat(status.toString(), containsString("X-HAPI-Response-Validation"));
assertThat(responseContent, containsString("<severity value=\"error\"/>"));
}
@Test
public void testSearchJsonValidNoValidatorsSpecified() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
patient.setGender(AdministrativeGender.MALE);
myReturnResource = patient;
HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient?foo=bar");
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.trace("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(status.toString(), not(containsString("X-HAPI-Response-Validation")));
}
@Test
public void testSearchJsonValidNoValidatorsSpecifiedDefaultMessage() throws Exception {
myInterceptor.setResponseHeaderValueNoIssues("NO ISSUES");
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
patient.setGender(AdministrativeGender.MALE);
myReturnResource = patient;
HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient?foo=bar");
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.trace("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(status.toString(), (containsString("X-HAPI-Response-Validation: NO ISSUES")));
}
/**
* Ignored until #264 is fixed
*/
@Test
@Ignore
public void testSearchXmlInvalidNoValidatorsSpecified() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
patient.setGender(AdministrativeGender.MALE);
patient.addContact().addRelationship().setText("FOO");
myReturnResource = patient;
HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient?foo=bar");
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.info("Response was:\n{}", responseContent);
assertEquals(422, status.getStatusLine().getStatusCode());
assertThat(status.toString(), containsString("X-HAPI-Response-Validation"));
}
@Test
public void testSearchXmlInvalidInstanceValidator() throws Exception {
IValidatorModule module = new FhirInstanceValidator();
myInterceptor.addValidatorModule(module);
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
patient.setGender(AdministrativeGender.MALE);
patient.addContact().addRelationship().setText("FOO");
myReturnResource = patient;
HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient?foo=bar");
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.info("Response was:\n{}", responseContent);
assertEquals(422, status.getStatusLine().getStatusCode());
assertThat(status.toString(), containsString("X-HAPI-Response-Validation"));
}
@Test
public void testSearchXmlValidNoValidatorsSpecified() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
patient.setGender(AdministrativeGender.MALE);
myReturnResource = patient;
HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient?foo=bar");
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.trace("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(status.toString(), not(containsString("X-HAPI-Response-Validation")));
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
PatientProvider patientProvider = new PatientProvider();
ServletHandler proxyHandler = new ServletHandler();
ourServlet = new RestfulServer(ourCtx);
ourServlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(ourServlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class PatientProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Search
public ArrayList<IBaseResource> search(@OptionalParam(name="foo") StringParam theString) {
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
myReturnResource.setId("1");
retVal.add(myReturnResource);
return retVal;
}
}
}

View File

@ -23,8 +23,9 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.ResourceReferenceInfo;
import ca.uhn.fhir.validation.IResourceLoader;
import ca.uhn.fhir.validation.IValidationContext;
import ca.uhn.fhir.validation.IValidatorModule;
public class FhirQuestionnaireResponseValidator extends BaseValidatorBridge {
public class FhirQuestionnaireResponseValidator extends BaseValidatorBridge implements IValidatorModule {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory
.getLogger(FhirQuestionnaireResponseValidator.class);

View File

@ -597,7 +597,7 @@
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.9.0.RELEASE</version>
<version>1.9.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>

View File

@ -26,6 +26,24 @@
codebase. This dependency was accidentally introduced in
1.3, and animal-sniffer-plugin failed to detect it (sigh).
</action>
<action type="add">
Add two new server interceptors:
<![CDATA[
<a href="./apidocs/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.html">RequestValidatingInterceptor</a>
and
<a href="./apidocs/ca/uhn/fhir/rest/server/interceptor/ResponseValidatingInterceptor.html">ResponseValidatingInterceptor</a>
]]>
which can be used to validate incoming requests or outgoing responses using the standard FHIR validation
tools. See the
<![CDATA[
<a href="./doc_rest_server_interceptor.html#RequestResponse_Validation">Server Validation Page</a>
]]>
for examples of how to use these interceptors. These intereptors have both
been enabled on the
<![CDATA[
<a href="http://fhirtest.uhn.ca">public test page</a>.
]]>
</action>
<action type="fix" issue="259">
Make IBoundCodeableConcept and IValueSetEnumBinder serializable,
fixing an issue when trying to serialize model classes containing
@ -78,7 +96,7 @@
Support target parameter type in _include / _revinclude values, e.g.
_include=Patient:careProvider:Organization. Thanks to Joe Portner
for reporting!
<action>
</action>
<action type="add">
Use ResponseHighlighterInterceptor in the hapi-fhir-jpaserver-example
project to provide nice syntax highlighting. Thanks to Rob Hausam for

View File

@ -251,6 +251,43 @@
</subsection>
<subsection name="Request/Response Validation">
<p>
The
<a href="./apidocs/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.html">RequestValidatingInterceptor</a>
and
<a href="./apidocs/ca/uhn/fhir/rest/server/interceptor/ResponseValidatingInterceptor.html">ResponseValidatingInterceptor</a>
can be used to perform validation of resources on their way into and out of the server respectively.
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
See the <a href="./doc_validation.html">Validation Page</a> for information on
available IValidationModule validation modules. Any of the <b>Resource Validators</b>
listed on that page can be enabled in these interceptors (note that the <b>Parser Validators</b>
can not).
</p>
<p>
The following example shows how to register this interceptor within
a FHIR RESTful server.
</p>
<macro name="snippet">
<param name="id" value="validatingInterceptor" />
<param name="file" value="examples/src/main/java/example/ServletExamples.java" />
</macro>
</subsection>
</section>
<section name="Creating Interceptors">

View File

@ -20,12 +20,14 @@
of a resource. It can be used to catch input data that is impossible to
fit into the HAPI data model. For
example, it can be used to throw exceptions
or display error messages if a resource being parsed contains tags for which
there are no appropriate fields in a HAPI data structure.
or display error messages if a resource being parsed contains elements for which
there are no appropriate fields in a HAPI data structure. This is useful in order to ensure
that no data is being lost during parsing, but is less comprehensive than resource validation
against raw text data.
</li>
<li>
<b>Resource Validation</b>
is validation of the parsed (or constructed) resource against
is validation of the raw or parsed resource against
the official FHIR validation rules (e.g. Schema/Schematron/Profile/StructureDefinition/ValueSet).
</li>
</ul>