Clean up exception handling in server, and add QuestionnaireAnswers validation for JPA server

This commit is contained in:
jamesagnew 2015-07-16 20:16:51 -04:00
parent 3bba0c0425
commit 2fc0d4c7a2
41 changed files with 537 additions and 191 deletions

View File

@ -244,10 +244,10 @@ class ModelScanner {
* annotations if the HL7.org ones are found instead.
*/
@SuppressWarnings("unchecked")
private <T extends Annotation> T pullAnnotation(AnnotatedElement theTarget, Class<T> theAnnotationType) {
private <T extends Annotation> T pullAnnotation(Class<?> theContainer, AnnotatedElement theTarget, Class<T> theAnnotationType) {
T retVal = theTarget.getAnnotation(theAnnotationType);
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG) == false) {
if (myContext.getVersion().getVersion() != FhirVersionEnum.DSTU2_HL7ORG) {
return retVal;
}
@ -307,7 +307,7 @@ class ModelScanner {
return;
}
ResourceDef resourceDefinition = pullAnnotation(theClass, ResourceDef.class);
ResourceDef resourceDefinition = pullAnnotation(theClass, theClass, ResourceDef.class);
if (resourceDefinition != null) {
if (!IBaseResource.class.isAssignableFrom(theClass)) {
throw new ConfigurationException("Resource type contains a @" + ResourceDef.class.getSimpleName() + " annotation but does not implement " + IResource.class.getCanonicalName() + ": " + theClass.getCanonicalName());
@ -317,7 +317,7 @@ class ModelScanner {
scanResource(resClass, resourceDefinition);
}
DatatypeDef datatypeDefinition = pullAnnotation(theClass, DatatypeDef.class);
DatatypeDef datatypeDefinition = pullAnnotation(theClass, theClass, DatatypeDef.class);
if (datatypeDefinition != null) {
if (ICompositeType.class.isAssignableFrom(theClass)) {
@SuppressWarnings("unchecked")
@ -332,7 +332,7 @@ class ModelScanner {
}
}
Block blockDefinition = pullAnnotation(theClass, Block.class);
Block blockDefinition = pullAnnotation(theClass, theClass, Block.class);
if (blockDefinition != null) {
if (IResourceBlock.class.isAssignableFrom(theClass) || IBaseBackboneElement.class.isAssignableFrom(theClass) || IBaseDatatypeElement.class.isAssignableFrom(theClass)) {
@ -440,16 +440,16 @@ class ModelScanner {
continue;
}
Child childAnnotation = pullAnnotation(next, Child.class);
Child childAnnotation = pullAnnotation(theClass, next, Child.class);
if (childAnnotation == null) {
ourLog.trace("Ignoring non @Child field {} on target type {}", next.getName(), theClass);
continue;
}
Description descriptionAnnotation = pullAnnotation(next, Description.class);
Description descriptionAnnotation = pullAnnotation(theClass, next, Description.class);
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderMap = theOrderToElementDef;
Extension extensionAttr = pullAnnotation(next, Extension.class);
Extension extensionAttr = pullAnnotation(theClass, next, Extension.class);
if (extensionAttr != null) {
orderMap = theOrderToExtensionDef;
}
@ -649,7 +649,7 @@ class ModelScanner {
}
}
CodeableConceptElement concept = pullAnnotation(next, CodeableConceptElement.class);
CodeableConceptElement concept = pullAnnotation(theClass, next, CodeableConceptElement.class);
if (concept != null) {
if (!ICodedDatatype.class.isAssignableFrom(nextDatatype)) {
throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is marked as @" + CodeableConceptElement.class.getCanonicalName() + " but type is not a subtype of " + ICodedDatatype.class.getName());
@ -709,7 +709,7 @@ class ModelScanner {
Class<?> parent = theClass.getSuperclass();
primaryNameProvider = false;
while (parent.equals(Object.class) == false && isBlank(resourceName)) {
ResourceDef nextDef = pullAnnotation(parent, ResourceDef.class);
ResourceDef nextDef = pullAnnotation(theClass, parent, ResourceDef.class);
if (nextDef != null) {
resourceName = nextDef.name();
}
@ -749,7 +749,7 @@ class ModelScanner {
Map<Field, SearchParamDefinition> compositeFields = new LinkedHashMap<Field, SearchParamDefinition>();
for (Field nextField : theClass.getFields()) {
SearchParamDefinition searchParam = pullAnnotation(nextField, SearchParamDefinition.class);
SearchParamDefinition searchParam = pullAnnotation(theClass, nextField, SearchParamDefinition.class);
if (searchParam != null) {
RestSearchParameterTypeEnum paramType = RestSearchParameterTypeEnum.valueOf(searchParam.type().toUpperCase());
if (paramType == null) {

View File

@ -7,7 +7,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
/*
* #%L
@ -54,7 +54,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
}
private List<String> myAdditionalMessages = null;
private BaseOperationOutcome myBaseOperationOutcome;
private IBaseOperationOutcome myBaseOperationOutcome;
private String myResponseBody;
private String myResponseMimeType;
private int myStatusCode;
@ -100,7 +100,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
* @param theBaseOperationOutcome
* An BaseOperationOutcome resource to return to the calling client (in a server) or the BaseOperationOutcome that was returned from the server (in a client)
*/
public BaseServerResponseException(int theStatusCode, String theMessage, BaseOperationOutcome theBaseOperationOutcome) {
public BaseServerResponseException(int theStatusCode, String theMessage, IBaseOperationOutcome theBaseOperationOutcome) {
super(theMessage);
myStatusCode = theStatusCode;
myBaseOperationOutcome = theBaseOperationOutcome;
@ -134,7 +134,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
* @param theBaseOperationOutcome
* An BaseOperationOutcome resource to return to the calling client (in a server) or the BaseOperationOutcome that was returned from the server (in a client)
*/
public BaseServerResponseException(int theStatusCode, String theMessage, Throwable theCause, BaseOperationOutcome theBaseOperationOutcome) {
public BaseServerResponseException(int theStatusCode, String theMessage, Throwable theCause, IBaseOperationOutcome theBaseOperationOutcome) {
super(theMessage, theCause);
myStatusCode = theStatusCode;
myBaseOperationOutcome = theBaseOperationOutcome;
@ -164,7 +164,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
* @param theBaseOperationOutcome
* An BaseOperationOutcome resource to return to the calling client (in a server) or the BaseOperationOutcome that was returned from the server (in a client)
*/
public BaseServerResponseException(int theStatusCode, Throwable theCause, BaseOperationOutcome theBaseOperationOutcome) {
public BaseServerResponseException(int theStatusCode, Throwable theCause, IBaseOperationOutcome theBaseOperationOutcome) {
super(theCause.toString(), theCause);
myStatusCode = theStatusCode;
myBaseOperationOutcome = theBaseOperationOutcome;
@ -182,9 +182,9 @@ public abstract class BaseServerResponseException extends RuntimeException {
}
/**
* Returns the {@link BaseOperationOutcome} resource if any which was supplied in the response, or <code>null</code>
* Returns the {@link IBaseOperationOutcome} resource if any which was supplied in the response, or <code>null</code>
*/
public BaseOperationOutcome getOperationOutcome() {
public IBaseOperationOutcome getOperationOutcome() {
return myBaseOperationOutcome;
}
@ -223,7 +223,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
* The BaseOperationOutcome resource Sets the BaseOperationOutcome resource associated with this exception. In server implementations, this is the OperartionOutcome resource to include
* with the HTTP response. In client implementations you should not call this method.
*/
public void setOperationOutcome(BaseOperationOutcome theBaseOperationOutcome) {
public void setOperationOutcome(IBaseOperationOutcome theBaseOperationOutcome) {
myBaseOperationOutcome = theBaseOperationOutcome;
}

View File

@ -1,7 +1,9 @@
package ca.uhn.fhir.rest.server.exceptions;
import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants;
/*
@ -54,7 +56,7 @@ public class ForbiddenOperationException extends BaseServerResponseException {
* @param theOperationOutcome
* The OperationOutcome resource to return to the client
*/
public ForbiddenOperationException(String theMessage, BaseOperationOutcome theOperationOutcome) {
public ForbiddenOperationException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}

View File

@ -1,7 +1,9 @@
package ca.uhn.fhir.rest.server.exceptions;
import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants;
/*
@ -54,7 +56,7 @@ public class InternalErrorException extends BaseServerResponseException {
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public InternalErrorException(String theMessage, BaseOperationOutcome theOperationOutcome) {
public InternalErrorException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}

View File

@ -1,7 +1,9 @@
package ca.uhn.fhir.rest.server.exceptions;
import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants;
/*
@ -54,7 +56,7 @@ public class InvalidRequestException extends BaseServerResponseException {
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public InvalidRequestException(String theMessage, BaseOperationOutcome theOperationOutcome) {
public InvalidRequestException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}

View File

@ -5,7 +5,8 @@ import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.server.Constants;
@ -53,7 +54,7 @@ public class MethodNotAllowedException extends BaseServerResponseException {
* @param theAllowedMethods
* A list of allowed methods (see {@link #setAllowedMethods(RequestTypeEnum...)} )
*/
public MethodNotAllowedException(String theMessage, BaseOperationOutcome theOperationOutcome, RequestTypeEnum... theAllowedMethods) {
public MethodNotAllowedException(String theMessage, IBaseOperationOutcome theOperationOutcome, RequestTypeEnum... theAllowedMethods) {
super(STATUS_CODE, theMessage, theOperationOutcome);
setAllowedMethods(theAllowedMethods);
}
@ -79,7 +80,7 @@ public class MethodNotAllowedException extends BaseServerResponseException {
* @param theOperationOutcome
* The OperationOutcome resource to return to the client
*/
public MethodNotAllowedException(String theMessage, BaseOperationOutcome theOperationOutcome) {
public MethodNotAllowedException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}

View File

@ -1,7 +1,9 @@
package ca.uhn.fhir.rest.server.exceptions;
import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants;
/*
@ -51,7 +53,7 @@ public class NotImplementedOperationException extends BaseServerResponseExceptio
* @param theOperationOutcome
* The OperationOutcome resource to return to the client
*/
public NotImplementedOperationException(String theMessage, BaseOperationOutcome theOperationOutcome) {
public NotImplementedOperationException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}

View File

@ -1,7 +1,9 @@
package ca.uhn.fhir.rest.server.exceptions;
import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants;
/*
@ -52,7 +54,7 @@ public class NotModifiedException extends BaseServerResponseException {
* @param theOperationOutcome
* The OperationOutcome resource to return to the client
*/
public NotModifiedException(String theMessage, BaseOperationOutcome theOperationOutcome) {
public NotModifiedException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}

View File

@ -21,7 +21,9 @@ package ca.uhn.fhir.rest.server.exceptions;
*/
import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.server.Constants;
@ -48,7 +50,7 @@ public class PreconditionFailedException extends ResourceVersionNotSpecifiedExce
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public PreconditionFailedException(String theMessage, BaseOperationOutcome theOperationOutcome) {
public PreconditionFailedException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}

View File

@ -21,9 +21,11 @@ package ca.uhn.fhir.rest.server.exceptions;
*/
import net.sourceforge.cobertura.CoverageIgnore;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.Constants;
@ -55,7 +57,7 @@ public class ResourceGoneException extends BaseServerResponseException {
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public ResourceGoneException(String theMessage, BaseOperationOutcome theOperationOutcome) {
public ResourceGoneException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}

View File

@ -22,11 +22,11 @@ package ca.uhn.fhir.rest.server.exceptions;
import net.sourceforge.cobertura.CoverageIgnore;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.Constants;
@ -44,7 +44,7 @@ public class ResourceNotFoundException extends BaseServerResponseException {
super(STATUS_CODE, createErrorMessage(theClass, theId));
}
public ResourceNotFoundException(Class<? extends IResource> theClass, IdDt theId, BaseOperationOutcome theOperationOutcome) {
public ResourceNotFoundException(Class<? extends IResource> theClass, IdDt theId, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, createErrorMessage(theClass, theId), theOperationOutcome);
}
@ -52,7 +52,7 @@ public class ResourceNotFoundException extends BaseServerResponseException {
super(STATUS_CODE, createErrorMessage(theClass, theId));
}
public ResourceNotFoundException(Class<? extends IResource> theClass, IIdType theId, BaseOperationOutcome theOperationOutcome) {
public ResourceNotFoundException(Class<? extends IResource> theClass, IIdType theId, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, createErrorMessage(theClass, theId), theOperationOutcome);
}
@ -63,7 +63,7 @@ public class ResourceNotFoundException extends BaseServerResponseException {
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public ResourceNotFoundException(String theMessage, BaseOperationOutcome theOperationOutcome) {
public ResourceNotFoundException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}
@ -83,7 +83,7 @@ public class ResourceNotFoundException extends BaseServerResponseException {
super(STATUS_CODE, createErrorMessage(theId));
}
public ResourceNotFoundException(IdDt theId, BaseOperationOutcome theOperationOutcome) {
public ResourceNotFoundException(IdDt theId, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, createErrorMessage(theId), theOperationOutcome);
}

View File

@ -21,7 +21,9 @@ package ca.uhn.fhir.rest.server.exceptions;
*/
import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.server.Constants;
@ -47,7 +49,7 @@ public class ResourceVersionConflictException extends BaseServerResponseExceptio
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public ResourceVersionConflictException(String theMessage, BaseOperationOutcome theOperationOutcome) {
public ResourceVersionConflictException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}

View File

@ -21,7 +21,9 @@ package ca.uhn.fhir.rest.server.exceptions;
*/
import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants;
/**
@ -45,7 +47,7 @@ public class ResourceVersionNotSpecifiedException extends BaseServerResponseExce
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public ResourceVersionNotSpecifiedException(String theMessage, BaseOperationOutcome theOperationOutcome) {
public ResourceVersionNotSpecifiedException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}
@ -60,7 +62,7 @@ public class ResourceVersionNotSpecifiedException extends BaseServerResponseExce
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public ResourceVersionNotSpecifiedException(int theStatusCode, String theMessage, BaseOperationOutcome theOperationOutcome) {
public ResourceVersionNotSpecifiedException(int theStatusCode, String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(theStatusCode, theMessage, theOperationOutcome);
}

View File

@ -1,7 +1,8 @@
package ca.uhn.fhir.rest.server.exceptions;
import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
/*
* #%L
@ -51,7 +52,7 @@ public class UnclassifiedServerFailureException extends BaseServerResponseExcept
* The message to add to the status line
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public UnclassifiedServerFailureException(int theStatusCode, String theMessage, BaseOperationOutcome theOperationOutcome) {
public UnclassifiedServerFailureException(int theStatusCode, String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(theStatusCode, theMessage, theOperationOutcome);
}

View File

@ -21,14 +21,16 @@ package ca.uhn.fhir.rest.server.exceptions;
*/
import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants;
/**
* Represents an <b>HTTP 422 Unprocessable Entity</b> response, which means that a resource was rejected by the server because it "violated applicable FHIR profiles or server business rules".
*
* <p>
* This exception will generally contain an {@link BaseOperationOutcome} instance which details the failure.
* This exception will generally contain an {@link IBaseOperationOutcome} instance which details the failure.
* </p>
*
* @see InvalidRequestException Which corresponds to an <b>HTTP 400 Bad Request</b> failure
@ -45,29 +47,29 @@ public class UnprocessableEntityException extends BaseServerResponseException {
*
* @param theMessage
* The message to add to the status line
* @param theOperationOutcome The BaseOperationOutcome resource to return to the client
* @param theOperationOutcome The {@link IBaseOperationOutcome} resource to return to the client
*/
public UnprocessableEntityException(String theMessage, BaseOperationOutcome theOperationOutcome) {
public UnprocessableEntityException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}
/**
* Constructor which accepts an {@link BaseOperationOutcome} resource which will be supplied in the response
* Constructor which accepts an {@link IBaseOperationOutcome} resource which will be supplied in the response
*/
public UnprocessableEntityException(BaseOperationOutcome theOperationOutcome) {
public UnprocessableEntityException(IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, DEFAULT_MESSAGE, theOperationOutcome);
}
/**
* Constructor which accepts a String describing the issue. This string will be translated into an {@link BaseOperationOutcome} resource which will be supplied in the response.
* Constructor which accepts a String describing the issue. This string will be translated into an {@link IBaseOperationOutcome} resource which will be supplied in the response.
*/
public UnprocessableEntityException(String theMessage) {
super(STATUS_CODE, theMessage);
}
/**
* Constructor which accepts an array of Strings describing the issue. This strings will be translated into an {@link BaseOperationOutcome} resource which will be supplied in the response.
* Constructor which accepts an array of Strings describing the issue. This strings will be translated into an {@link IBaseOperationOutcome} resource which will be supplied in the response.
*/
public UnprocessableEntityException(String... theMessage) {
super(STATUS_CODE, theMessage);

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.server.interceptor;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -33,11 +34,18 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
/**
* This interceptor detects when a request is coming from a browser, and automatically
* returns a response with syntax highlighted (coloured) HTML for the response instead
* of just returning raw XML/JSON.
*
* @since 1.0
*/
public class ResponseHighlighterInterceptor extends InterceptorAdapter {
private String format(String theResultBody, EncodingEnum theEncodingEnum) {
@ -178,12 +186,15 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
return super.outgoingResponse(theRequestDetails, theResponseObject, theServletRequest, theServletResponse);
}
streamResponse(theRequestDetails, theServletRequest, theServletResponse, theResponseObject);
return false;
}
private void streamResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse, IBaseResource resource) {
// Pretty print
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theRequestDetails.getServer(), theRequestDetails);
// Narrative mode
NarrativeModeEnum narrativeMode = RestfulServerUtils.determineNarrativeMode(theRequestDetails);
// Determine response encoding
EncodingEnum responseEncoding = null;
if (theRequestDetails.getParameters().containsKey(Constants.PARAM_FORMAT)) {
@ -198,7 +209,7 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
IParser p = responseEncoding.newParser(theRequestDetails.getServer().getFhirContext());
p.setPrettyPrint(prettyPrint);
String encoded = p.encodeResourceToString(theResponseObject);
String encoded = p.encodeResourceToString(resource);
theServletResponse.setContentType(Constants.CT_HTML_WITH_UTF8);
@ -239,6 +250,43 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
} catch (IOException e) {
throw new InternalErrorException(e);
}
}
@Override
public boolean handleException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws ServletException, IOException {
/*
* It's not a browser...
*/
String accept = theServletRequest.getHeader(Constants.HEADER_ACCEPT);
if (accept == null || !accept.toLowerCase().contains("html")) {
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
}
/*
* It's an AJAX request, so no HTML
*/
String requestedWith = theServletRequest.getHeader("X-Requested-With");
if (requestedWith != null) {
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
}
/*
* Not a GET
*/
if (theRequestDetails.getRequestType() != RequestTypeEnum.GET) {
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
}
if (!(theException instanceof BaseServerResponseException)) {
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
}
BaseServerResponseException bsre = (BaseServerResponseException)theException;
if (bsre.getOperationOutcome() == null) {
}
streamResponse(theRequestDetails, theServletRequest, theServletResponse, bsre.getOperationOutcome());
return false;
}

View File

@ -54,7 +54,7 @@ public class FhirValidator {
private static volatile Boolean ourPhlocPresentOnClasspath;
private final FhirContext myContext;
private volatile List<IValidator> myValidators = new ArrayList<IValidator>();
private volatile List<IValidatorModule> myValidators = new ArrayList<IValidatorModule>();
/**
* Constructor (this should not be called directly, but rather {@link FhirContext#newValidator()} should be called to obtain an instance of {@link FhirValidator})
@ -78,25 +78,25 @@ public class FhirValidator {
}
private void addOrRemoveValidator(boolean theValidateAgainstStandardSchema, Class<? extends IValidator> type, IValidator theInstance) {
private void addOrRemoveValidator(boolean theValidateAgainstStandardSchema, Class<? extends IValidatorModule> type, IValidatorModule theInstance) {
if (theValidateAgainstStandardSchema) {
boolean found = haveValidatorOfType(type);
if (!found) {
registerValidator(theInstance);
registerValidatorModule(theInstance);
}
} else {
for (Iterator<IValidator> iter = myValidators.iterator(); iter.hasNext();) {
IValidator next = iter.next();
for (Iterator<IValidatorModule> iter = myValidators.iterator(); iter.hasNext();) {
IValidatorModule next = iter.next();
if (next.getClass().equals(type)) {
unregisterValidator(next);
unregisterValidatorModule(next);
}
}
}
}
private boolean haveValidatorOfType(Class<? extends IValidator> type) {
private boolean haveValidatorOfType(Class<? extends IValidatorModule> type) {
boolean found = false;
for (IValidator next : myValidators) {
for (IValidatorModule next : myValidators) {
if (next.getClass().equals(type)) {
found = true;
}
@ -124,9 +124,9 @@ public class FhirValidator {
* @param theValidator
* The validator module. Must not be null.
*/
public synchronized void registerValidator(IValidator theValidator) {
public synchronized void registerValidatorModule(IValidatorModule theValidator) {
Validate.notNull(theValidator, "theValidator must not be null");
ArrayList<IValidator> newValidators = new ArrayList<IValidator>(myValidators.size() + 1);
ArrayList<IValidatorModule> newValidators = new ArrayList<IValidatorModule>(myValidators.size() + 1);
newValidators.addAll(myValidators);
newValidators.add(theValidator);
@ -162,9 +162,9 @@ public class FhirValidator {
* @param theValidator
* The validator module. Must not be null.
*/
public synchronized void unregisterValidator(IValidator theValidator) {
public synchronized void unregisterValidatorModule(IValidatorModule theValidator) {
Validate.notNull(theValidator, "theValidator must not be null");
ArrayList<IValidator> newValidators = new ArrayList<IValidator>(myValidators.size() + 1);
ArrayList<IValidatorModule> newValidators = new ArrayList<IValidatorModule>(myValidators.size() + 1);
newValidators.addAll(myValidators);
newValidators.remove(theValidator);
@ -186,7 +186,7 @@ public class FhirValidator {
IValidationContext<Bundle> ctx = ValidationContext.forBundle(myContext, theBundle);
for (IValidator next : myValidators) {
for (IValidatorModule next : myValidators) {
next.validateBundle(ctx);
}
@ -227,7 +227,7 @@ public class FhirValidator {
IValidationContext<Bundle> ctx = ValidationContext.forBundle(myContext, theBundle);
for (IValidator next : myValidators) {
for (IValidatorModule next : myValidators) {
next.validateBundle(ctx);
}
@ -247,7 +247,7 @@ public class FhirValidator {
IValidationContext<IBaseResource> ctx = ValidationContext.forResource(myContext, theResource);
for (IValidator next : myValidators) {
for (IValidatorModule next : myValidators) {
next.validateResource(ctx);
}
@ -267,7 +267,7 @@ public class FhirValidator {
IValidationContext<IBaseResource> ctx = ValidationContext.forText(myContext, theResource);
for (IValidator next : myValidators) {
for (IValidatorModule next : myValidators) {
next.validateResource(ctx);
}

View File

@ -28,7 +28,7 @@ import ca.uhn.fhir.model.api.Bundle;
/**
* Registers
*/
public interface IValidator {
public interface IValidatorModule {
void validateResource(IValidationContext<IBaseResource> theCtx);

View File

@ -52,7 +52,7 @@ import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
class SchemaBaseValidator implements IValidator {
class SchemaBaseValidator implements IValidatorModule {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SchemaBaseValidator.class);
private static final Set<String> SCHEMA_NAMES;

View File

@ -43,7 +43,7 @@ import com.phloc.schematron.ISchematronResource;
import com.phloc.schematron.SchematronHelper;
import com.phloc.schematron.xslt.SchematronResourceSCH;
public class SchematronBaseValidator implements IValidator {
public class SchematronBaseValidator implements IValidatorModule {
private Map<Class<? extends IBaseResource>, ISchematronResource> myClassToSchematron = new HashMap<Class<? extends IBaseResource>, ISchematronResource>();
private FhirContext myCtx;

View File

@ -80,7 +80,9 @@ public class ValidationResult {
}
/**
* @deprecated Use {@link #toOperationOutcome()} instead since this method returns a view
* @deprecated Use {@link #toOperationOutcome()} instead since this method returns a view.
* {@link #toOperationOutcome()} is identical to this method, but has a more suitable name so this method
* will be removed at some point.
*/
@Deprecated
public IBaseOperationOutcome getOperationOutcome() {

View File

@ -325,7 +325,8 @@ public abstract class BaseHapiFhirDao implements IDao {
}
}
protected IFhirResourceDao<? extends IResource> getDao(Class<? extends IBaseResource> theType) {
@SuppressWarnings("unchecked")
protected <T extends IBaseResource> IFhirResourceDao<T> getDao(Class<T> theType) {
if (myResourceTypeToDao == null) {
myResourceTypeToDao = new HashMap<Class<? extends IBaseResource>, IFhirResourceDao<?>>();
for (IFhirResourceDao<?> next : myResourceDaos) {
@ -339,7 +340,7 @@ public abstract class BaseHapiFhirDao implements IDao {
}
return myResourceTypeToDao.get(theType);
return (IFhirResourceDao<T>) myResourceTypeToDao.get(theType);
}
protected TagDefinition getTag(TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
@ -625,12 +626,12 @@ public abstract class BaseHapiFhirDao implements IDao {
}
protected Set<Long> processMatchUrl(String theMatchUrl, Class<? extends IBaseResource> theResourceType) {
protected <T extends IResource> Set<Long> processMatchUrl(String theMatchUrl, Class<T> theResourceType) {
RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType);
SearchParameterMap paramMap = translateMatchUrl(theMatchUrl, resourceDef);
IFhirResourceDao<? extends IResource> dao = getDao(theResourceType);
IFhirResourceDao<T> dao = getDao(theResourceType);
Set<Long> ids = dao.searchForIdsWithAndOr(paramMap);
return ids;
@ -1019,8 +1020,10 @@ public abstract class BaseHapiFhirDao implements IDao {
return updateEntity(theResource, entity, theUpdateHistory, theDeletedTimestampOrNull, true, true);
}
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion) {
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion) {
validateResourceForStorage(theResource);
if (entity.getPublished() == null) {
entity.setPublished(new Date());
}
@ -1028,8 +1031,7 @@ public abstract class BaseHapiFhirDao implements IDao {
if (theResource != null) {
String resourceType = myContext.getResourceDefinition(theResource).getName();
if (isNotBlank(entity.getResourceType()) && !entity.getResourceType().equals(resourceType)) {
throw new UnprocessableEntityException("Existing resource ID[" + entity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + entity.getResourceType() + "] - Cannot update with ["
+ resourceType + "]");
throw new UnprocessableEntityException("Existing resource ID[" + entity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + entity.getResourceType() + "] - Cannot update with [" + resourceType + "]");
}
}
@ -1195,6 +1197,16 @@ public abstract class BaseHapiFhirDao implements IDao {
return entity;
}
/**
* Subclasses may override to provide specific behaviour
*
* @param theResource
* The resource that is about to be persisted
*/
protected void validateResourceForStorage(IResource theResource) {
// nothing
}
protected static String normalizeString(String theString) {
char[] out = new char[theString.length()];
theString = Normalizer.normalize(theString, Normalizer.Form.NFD);

View File

@ -56,8 +56,10 @@ import javax.persistence.criteria.Subquery;
import org.apache.commons.lang3.NotImplementedException;
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;
import org.omg.PortableInterceptor.InterceptorOperations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.transaction.PlatformTransactionManager;
@ -857,16 +859,24 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
if (isNotBlank(theResource.getId().getIdPart())) {
if (getContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
if (theResource.getId().isIdPartValidLong()) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getId().getIdPart()));
String message = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getId().getIdPart());
throw new InvalidRequestException(message, createErrorOperationOutcome(message));
}
} else {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedId", theResource.getId().getIdPart()));
String message = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedId", theResource.getId().getIdPart());
throw new InvalidRequestException(message, createErrorOperationOutcome(message));
}
}
return doCreate(theResource, theIfNoneExist, thePerformIndexing);
}
private IBaseOperationOutcome createErrorOperationOutcome(String theMessage) {
return createOperationOutcome("error", theMessage);
}
protected abstract IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage);
private Predicate createCompositeParamPart(CriteriaBuilder builder, Root<ResourceTable> from, RuntimeSearchParam left, IQueryParameterType leftValue) {
Predicate retVal = null;
switch (left.getParamType()) {

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
public class FhirBundleResourceDaoDstu2 extends FhirResourceDaoDstu2<Bundle> {
public class FhirResourceDaoBundleDstu2 extends FhirResourceDaoDstu2<Bundle> {
@Override
protected void preProcessResourceForStorage(Bundle theResource) {

View File

@ -24,12 +24,18 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.util.FhirTerser;
public class FhirResourceDaoDstu1<T extends IResource> extends BaseHapiFhirResourceDao<T> {
@ -47,5 +53,18 @@ public class FhirResourceDaoDstu1<T extends IResource> extends BaseHapiFhirResou
return values;
}
@Override
public MethodOutcome validate(T theResource, IdDt theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile) {
throw new UnsupportedOperationException();
}
@Override
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage) {
OperationOutcome oo = new OperationOutcome();
oo.getIssueFirstRep().getSeverityElement().setValue(theSeverity);
oo.getIssueFirstRep().getDetailsElement().setValue(theMessage);
return oo;
}
}

View File

@ -20,10 +20,11 @@ package ca.uhn.fhir.jpa.dao;
* #L%
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
@ -31,7 +32,20 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome.BaseIssue;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome.Issue;
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.dstu2.valueset.IssueTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.IParserErrorHandler;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResourceDao<T> {
@ -52,5 +66,48 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
return values;
}
@Override
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage) {
OperationOutcome oo = new OperationOutcome();
oo.getIssueFirstRep().getSeverityElement().setValue(theSeverity);
oo.getIssueFirstRep().getDetailsElement().setValue(theMessage);
return oo;
}
@Override
public MethodOutcome validate(T theResource, IdDt theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile) {
final OperationOutcome oo = new OperationOutcome();
IParser parser = theEncoding.newParser(getContext());
parser.setParserErrorHandler(new IParserErrorHandler() {
@Override
public void unknownAttribute(IParseLocation theLocation, String theAttributeName) {
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Unknown attribute found: " + theAttributeName);
}
@Override
public void unknownElement(IParseLocation theLocation, String theElementName) {
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Unknown element found: " + theElementName);
}
});
FhirValidator validator = getContext().newValidator();
validator.setValidateAgainstStandardSchema(true);
validator.setValidateAgainstStandardSchematron(true);
ValidationResult result = validator.validateWithResult(theResource);
OperationOutcome operationOutcome = (OperationOutcome) result.toOperationOutcome();
for (BaseIssue next : operationOutcome.getIssue()) {
oo.getIssue().add((Issue) next);
}
// This method returns a MethodOutcome object
MethodOutcome retVal = new MethodOutcome();
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Validation succeeded");
retVal.setOperationOutcome(oo);
return retVal;
}
}

View File

@ -0,0 +1,74 @@
package ca.uhn.fhir.jpa.dao;
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 ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.resource.QuestionnaireAnswers;
import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.validation.FhirQuestionnaireAnswersValidator;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.IResourceLoader;
import ca.uhn.fhir.validation.ValidationResult;
public class FhirResourceDaoQuestionnaireAnswersDstu2 extends FhirResourceDaoDstu2<QuestionnaireAnswers> {
private FhirContext myRefImplCtx = FhirContext.forDstu2Hl7Org();
@Override
protected void validateResourceForStorage(IResource theResource) {
super.validateResourceForStorage(theResource);
QuestionnaireAnswers qa = (QuestionnaireAnswers) theResource;
if (qa.getQuestionnaire().getReference().isEmpty()) {
return;
}
FhirValidator val = myRefImplCtx.newValidator();
val.setValidateAgainstStandardSchema(false);
val.setValidateAgainstStandardSchematron(false);
FhirQuestionnaireAnswersValidator module = new FhirQuestionnaireAnswersValidator();
module.setResourceLoader(new JpaResourceLoader());
val.registerValidatorModule(module);
ValidationResult result = val.validateWithResult(myRefImplCtx.newJsonParser().parseResource(getContext().newJsonParser().encodeResourceToString(qa)));
if (!result.isSuccessful()) {
IBaseOperationOutcome oo = getContext().newJsonParser().parseResource(OperationOutcome.class, myRefImplCtx.newJsonParser().encodeResourceToString(result.toOperationOutcome()));
throw new UnprocessableEntityException(oo);
}
}
public class JpaResourceLoader implements IResourceLoader {
@Override
public <T extends IBaseResource> T load(Class<T> theType, IIdType theId) throws ResourceNotFoundException {
/*
* The QuestionnaireAnswers validator uses RI structures, so for now we need
* to convert between that and HAPI structures. This is a bit hackish, but
* hopefully it will go away at some point.
*/
if ("ValueSet".equals(theType.getSimpleName())) {
IFhirResourceDao<ValueSet> dao = getDao(ValueSet.class);
ValueSet vs = dao.read(theId);
return myRefImplCtx.newJsonParser().parseResource(theType, getContext().newJsonParser().encodeResourceToString(vs));
} else if ("Questionnaire".equals(theType.getSimpleName())) {
IFhirResourceDao<Questionnaire> dao = getDao(Questionnaire.class);
Questionnaire vs = dao.read(theId);
return myRefImplCtx.newJsonParser().parseResource(theType, getContext().newJsonParser().encodeResourceToString(vs));
} else {
// Should not happen, validator will only ask for these two
throw new IllegalStateException("Unexpected request to load resource of type " + theType);
}
}
}
}

View File

@ -132,7 +132,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theAction, theUrl);
throw new InvalidRequestException(msg);
}
IFhirResourceDao<? extends IResource> dao = null;
IFhirResourceDao<? extends IBaseResource> dao = null;
if (resType != null) {
dao = getDao(resType.getImplementingClass());
}
@ -268,9 +268,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
if (isNotBlank(ifNoneMatch)) {
throw new InvalidRequestException("Unable to perform vread on '" + url + "' with ifNoneMatch also set. Do not include a version in the URL to perform a conditional read.");
}
found = resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId(), parts.getVersionId()));
found = (IResource) resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId(), parts.getVersionId()));
} else {
found = resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId()));
found = (IResource) resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId()));
if (isNotBlank(ifNoneMatch) && ifNoneMatch.equals(found.getId().getVersionIdPart())) {
notChanged = true;
}
@ -392,13 +392,13 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
}
private static class UrlParts {
private IFhirResourceDao<? extends IResource> myDao;
private IFhirResourceDao<? extends IBaseResource> myDao;
private String myParams;
private String myResourceId;
private String myResourceType;
private String myVersionId;
public IFhirResourceDao<? extends IResource> getDao() {
public IFhirResourceDao<? extends IBaseResource> getDao() {
return myDao;
}
@ -418,7 +418,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
return myVersionId;
}
public void setDao(IFhirResourceDao<? extends IResource> theDao) {
public void setDao(IFhirResourceDao<? extends IBaseResource> theDao) {
myDao = theDao;
}

View File

@ -24,18 +24,22 @@ import java.util.Date;
import java.util.Map;
import java.util.Set;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
public interface IFhirResourceDao<T extends IResource> extends IDao {
public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel);
@ -44,7 +48,8 @@ public interface IFhirResourceDao<T extends IResource> extends IDao {
DaoMethodOutcome create(T theResource, String theIfNoneExist);
/**
* @param thePerformIndexing Use with caution! If you set this to false, you need to manually perform indexing or your resources
* @param thePerformIndexing
* Use with caution! If you set this to false, you need to manually perform indexing or your resources
* won't be indexed and searches won't work.
*/
DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing);
@ -65,6 +70,26 @@ public interface IFhirResourceDao<T extends IResource> extends IDao {
IBundleProvider history(Long theId, Date theSince);
/**
* Not supported in DSTU1!
*/
MetaDt metaAddOperation(IIdType theId1, MetaDt theMetaAdd);
/**
* Not supported in DSTU1!
*/
MetaDt metaDeleteOperation(IIdType theId1, MetaDt theMetaDel);
/**
* Not supported in DSTU1!
*/
MetaDt metaGetOperation();
/**
* Not supported in DSTU1!
*/
MetaDt metaGetOperation(IIdType theId);
/**
*
* @param theId
@ -77,7 +102,8 @@ public interface IFhirResourceDao<T extends IResource> extends IDao {
BaseHasResource readEntity(IIdType theId);
/**
* @param theCheckForForcedId If true, this method should fail if the requested ID contains a numeric PID which exists, but is
* @param theCheckForForcedId
* If true, this method should fail if the requested ID contains a numeric PID which exists, but is
* obscured by a "forced ID" so should not exist as far as the outside world is concerned.
*/
BaseHasResource readEntity(IIdType theId, boolean theCheckForForcedId);
@ -110,20 +136,6 @@ public interface IFhirResourceDao<T extends IResource> extends IDao {
/**
* Not supported in DSTU1!
*/
MetaDt metaGetOperation();
MethodOutcome validate(T theResource, IdDt theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile);
/**
* Not supported in DSTU1!
*/
MetaDt metaGetOperation(IIdType theId);
/**
* Not supported in DSTU1!
*/
MetaDt metaDeleteOperation(IIdType theId1, MetaDt theMetaDel);
/**
* Not supported in DSTU1!
*/
MetaDt metaAddOperation(IIdType theId1, MetaDt theMetaAdd);
}

View File

@ -165,38 +165,7 @@ public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResour
@Validate
public MethodOutcome validate(@ResourceParam T theResource, @IdParam IdDt theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
@Validate.Profile String theProfile) {
final OperationOutcome oo = new OperationOutcome();
IParser parser = theEncoding.newParser(getContext());
parser.setParserErrorHandler(new IParserErrorHandler() {
@Override
public void unknownAttribute(IParseLocation theLocation, String theAttributeName) {
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Unknown attribute found: " + theAttributeName);
}
@Override
public void unknownElement(IParseLocation theLocation, String theElementName) {
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Unknown element found: " + theElementName);
}
});
FhirValidator validator = getContext().newValidator();
validator.setValidateAgainstStandardSchema(true);
validator.setValidateAgainstStandardSchematron(true);
ValidationResult result = validator.validateWithResult(theResource);
OperationOutcome operationOutcome = (OperationOutcome) result.getOperationOutcome();
for (BaseIssue next : operationOutcome.getIssue()) {
oo.getIssue().add((Issue) next);
}
// This method returns a MethodOutcome object
MethodOutcome retVal = new MethodOutcome();
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Validation succeeded");
retVal.setOperationOutcome(oo);
return retVal;
return getDao().validate(theResource, theId, theRawResource, theEncoding, theMode, theProfile);
}
}

View File

@ -50,6 +50,7 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.dstu.resource.Device;
import ca.uhn.fhir.model.dstu.resource.Practitioner;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
@ -65,7 +66,10 @@ import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Organization;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.resource.QuestionnaireAnswers;
import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.model.dstu2.valueset.AnswerFormatEnum;
import ca.uhn.fhir.model.dstu2.valueset.EncounterClassEnum;
import ca.uhn.fhir.model.dstu2.valueset.EncounterStateEnum;
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
@ -208,7 +212,6 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
}
@Test
public void testCreateResourceConditional() throws IOException {
String methodName = "testCreateResourceConditional";
@ -245,6 +248,40 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
}
@Test
public void testCreateQuestionnaireAnswersWithValidation() throws IOException {
String methodName = "testCreateQuestionnaireAnswersWithValidation";
ValueSet options = new ValueSet();
options.getDefine().setSystem("urn:system").addConcept().setCode("code0");
IIdType optId = ourClient.create().resource(options).execute().getId();
Questionnaire q = new Questionnaire();
q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormatEnum.CHOICE).setOptions(new ResourceReferenceDt(optId));
IIdType qId = ourClient.create().resource(q).execute().getId();
QuestionnaireAnswers qa;
// Good code
qa = new QuestionnaireAnswers();
qa.getQuestionnaire().setReference(qId.toUnqualifiedVersionless().getValue());
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new CodingDt().setSystem("urn:system").setCode("code0"));
ourClient.create().resource(qa).execute();
// Bad code
qa = new QuestionnaireAnswers();
qa.getQuestionnaire().setReference(qId.toUnqualifiedVersionless().getValue());
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new CodingDt().setSystem("urn:system").setCode("code1"));
try {
ourClient.create().resource(qa).execute();
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Question with linkId[link0]"));
}
}
@Test
public void testCreateResourceWithNumericId() throws IOException {
String resource = "<Patient xmlns=\"http://hl7.org/fhir\"></Patient>";
@ -255,7 +292,10 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
assertEquals(400, response.getStatusLine().getStatusCode());
String respString = IOUtils.toString(response.getEntity().getContent());
ourLog.info(respString);
} finally {
response.getEntity().getContent().close();
response.close();
}
}
@ -365,7 +405,8 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
}
/*
* Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL but we want to make sure that works too..
* Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL but we want to
* make sure that works too..
*/
Socket sock = new Socket();
sock.setSoTimeout(3000);
@ -739,8 +780,7 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01");
IdDt p1Id = (IdDt) ourClient.create().resource(p1).execute().getId();
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint()
.execute();
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint().execute();
assertEquals(1, actual.size());
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart());
@ -1166,8 +1206,7 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
assertThat(p1Id.getValue(), containsString("Patient/testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2/_history"));
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2"))
.encodedJson().prettyPrint().execute();
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2")).encodedJson().prettyPrint().execute();
assertEquals(1, actual.size());
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart());

View File

@ -21,6 +21,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
@ -122,7 +123,6 @@ public class ExceptionHandlingTest {
assertThat(e.getResponseBody(), StringContains.containsString("value=\"foo\""));
}
}
@Test
@ -146,7 +146,7 @@ public class ExceptionHandlingTest {
assertThat(e.getMessage(), StringContains.containsString("HTTP 500 Internal Error"));
assertThat(e.getMessage(), StringContains.containsString("Help I'm a bug"));
assertNotNull(e.getOperationOutcome());
assertEquals("Help I'm a bug",e.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue());
assertEquals("Help I'm a bug", ((BaseOperationOutcome) e.getOperationOutcome()).getIssueFirstRep().getDetailsElement().getValue());
}
}
@ -172,13 +172,12 @@ public class ExceptionHandlingTest {
assertThat(e.getMessage(), StringContains.containsString("HTTP 500 Internal Error"));
assertThat(e.getMessage(), StringContains.containsString("Help I'm a bug"));
assertNotNull(e.getOperationOutcome());
assertEquals("Help I'm a bug",e.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue());
assertEquals("Help I'm a bug", ((BaseOperationOutcome) e.getOperationOutcome()).getIssueFirstRep().getDetailsElement().getValue());
}
}
public interface IMyClient extends IRestfulClient
{
public interface IMyClient extends IRestfulClient {
@Read
Patient read(@IdParam IdDt theId);
}

View File

@ -1,13 +1,10 @@
package ca.uhn.fhir.rest.server.exceptions;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assert.*;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.junit.Test;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ClassInfo;
@ -48,9 +45,9 @@ public class ExceptionPropertiesTest {
}
try {
next.getConstructor(String.class, BaseOperationOutcome.class);
next.getConstructor(String.class, IBaseOperationOutcome.class);
} catch (NoSuchMethodException e) {
fail(classInfo.getName() + " has no constructor with params: (String, OperationOutcome)");
fail(classInfo.getName() + " has no constructor with params: (String, IBaseOperationOutcome)");
}
}

View File

@ -90,7 +90,7 @@ public class FhirDstu2 implements IFhirVersion {
str = FhirDstu2.class.getResourceAsStream("ca/uhn/fhir/model/dstu2/fhirversion.properties");
}
if (str == null) {
throw new ConfigurationException("Can not find model property file on classpath: " + "/ca/uhn/fhir/model/dstu2/model.properties");
throw new ConfigurationException("Can not find model property file on classpath: " + "/ca/uhn/fhir/model/dstu2/fhirversion.properties");
}
return str;
}

View File

@ -1,12 +1,87 @@
package ca.uhn.fhir.rest.server.interceptor;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Test;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome.Issue;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
public class ResponseHighlightingInterceptorTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResponseHighlightingInterceptorTest.class);
@Test
public void testHighlight() {
public void testHighlightNormalResponse() throws Exception {
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
HttpServletRequest req = mock(HttpServletRequest.class);
when(req.getHeader(Constants.HEADER_ACCEPT)).thenReturn("text/html");
HttpServletResponse resp = mock(HttpServletResponse.class);
StringWriter sw = new StringWriter();
when(resp.getWriter()).thenReturn(new PrintWriter(sw));
Patient resource = new Patient();
resource.addName().addFamily("FAMILY");
RequestDetails reqDetails = new RequestDetails();
reqDetails.setRequestType(RequestTypeEnum.GET);
reqDetails.setParameters(new HashMap<String, String[]>());
reqDetails.setServer(new RestfulServer());
reqDetails.setServletRequest(req);
assertFalse(ic.outgoingResponse(reqDetails, resource, req, resp));
String output = sw.getBuffer().toString();
ourLog.info(output);
assertThat(output, containsString("<span class='hlTagName'>Patient</span>"));
}
@Test
public void testHighlightException() throws Exception {
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
HttpServletRequest req = mock(HttpServletRequest.class);
when(req.getHeader(Constants.HEADER_ACCEPT)).thenReturn("text/html");
HttpServletResponse resp = mock(HttpServletResponse.class);
StringWriter sw = new StringWriter();
when(resp.getWriter()).thenReturn(new PrintWriter(sw));
Patient resource = new Patient();
resource.addName().addFamily("FAMILY");
RequestDetails reqDetails = new RequestDetails();
reqDetails.setRequestType(RequestTypeEnum.GET);
reqDetails.setParameters(new HashMap<String, String[]>());
reqDetails.setServer(new RestfulServer());
reqDetails.setServletRequest(req);
ResourceNotFoundException exception = new ResourceNotFoundException("Not found");
exception.setOperationOutcome(new OperationOutcome().addIssue(new Issue().setDetails("Hello")));
assertFalse(ic.handleException(reqDetails, exception, req, resp));
String output = sw.getBuffer().toString();
ourLog.info(output);
assertThat(output, containsString("<span class='hlTagName'>OperationOutcome</span>"));
}
}

View File

@ -10,7 +10,7 @@ import ca.uhn.fhir.model.api.Bundle;
/**
* Base class for a bridge between the RI validation tools and HAPI
*/
abstract class BaseValidatorBridge implements IValidator {
abstract class BaseValidatorBridge implements IValidatorModule {
public BaseValidatorBridge() {
super();

View File

@ -30,7 +30,7 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
public class FhirInstanceValidator extends BaseValidatorBridge implements IValidator {
public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule {
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidator.class);

View File

@ -19,7 +19,7 @@ public class FhirInstanceValidatorTest {
val = ourCtx.newValidator();
val.setValidateAgainstStandardSchema(false);
val.setValidateAgainstStandardSchematron(false);
val.registerValidator(new FhirInstanceValidator());
val.registerValidatorModule(new FhirInstanceValidator());
}
@Test

View File

@ -47,7 +47,7 @@ public class QuestionnaireAnswersValidatorIntegrationTest {
myVal = ourCtx.newValidator();
myVal.setValidateAgainstStandardSchema(false);
myVal.setValidateAgainstStandardSchematron(false);
myVal.registerValidator(qaVal);
myVal.registerValidatorModule(qaVal);
}
@Test
@ -97,6 +97,8 @@ public class QuestionnaireAnswersValidatorIntegrationTest {
ourLog.info(result.getMessages().toString());
assertThat(result.getMessages().toString(), containsString("myLocationString=QuestionnaireAnswers.group(0).question(0).answer(0)"));
assertThat(result.getMessages().toString(), containsString("myMessage=Question with linkId[link0] has answer with system[urn:system] and code[code1] but this is not a valid answer for ValueSet[http://somevalueset/ValueSet/123]"));
result.toOperationOutcome();
}

View File

@ -28,8 +28,8 @@
#foreach ( $res in $resources )
<bean id="my${res.name}Dao${versionCapitalized}"
#if ( ${res.name} == 'Bundle' )
class="ca.uhn.fhir.jpa.dao.Fhir${res.name}ResourceDao${versionCapitalized}">
#if ( ${versionCapitalized} == 'Dstu2' && ( ${res.name} == 'Bundle' || ${res.name} == 'QuestionnaireAnswers' ))
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${res.name}${versionCapitalized}">
#else
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${versionCapitalized}">
#end

View File

@ -6,6 +6,15 @@
<title>HAPI FHIR Changelog</title>
</properties>
<body>
<release version="1.2" date="2015-07-13">
<action type="add">
JPA server now validates QuestionnaireAnswers for conformance to their respective Questionnaire
if one is declared.
</action>
<action type="add">
SyntaxHighlightingInterceptor now also highlights OperationOutcome responses for errors/exceptions.
</action>
</release>
<release version="1.1" date="2015-07-13">
<action type="add">
Add support for reference implementation structures.