Add "is trusted" property to server exceptions

This commit is contained in:
James Agnew 2019-03-01 13:58:13 -05:00
parent 7896887f67
commit 3972e17e62
2 changed files with 98 additions and 89 deletions

View File

@ -1,11 +1,11 @@
package ca.uhn.fhir.rest.server.exceptions; package ca.uhn.fhir.rest.server.exceptions;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
/* /*
* #%L * #%L
@ -16,9 +16,9 @@ import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -32,7 +32,7 @@ import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
* subclasses of this exception type. * subclasses of this exception type.
* <p> * <p>
* HAPI provides a number of subclasses of BaseServerResponseException, and each one corresponds to a specific * HAPI provides a number of subclasses of BaseServerResponseException, and each one corresponds to a specific
* HTTP status code. For example, if a IResourceProvider method throws * HTTP status code. For example, if a IResourceProvider method throws
* {@link ResourceNotFoundException}, this is a signal to the server that an <code>HTTP 404</code> should * {@link ResourceNotFoundException}, this is a signal to the server that an <code>HTTP 404</code> should
* be returned to the client. * be returned to the client.
* </p> * </p>
@ -69,28 +69,25 @@ public abstract class BaseServerResponseException extends RuntimeException {
private Map<String, List<String>> myResponseHeaders; private Map<String, List<String>> myResponseHeaders;
private String myResponseMimeType; private String myResponseMimeType;
private int myStatusCode; private int myStatusCode;
private boolean myErrorMessageTrusted;
/** /**
* Constructor * Constructor
* *
* @param theStatusCode * @param theStatusCode The HTTP status code corresponding to this problem
* The HTTP status code corresponding to this problem * @param theMessage The message
* @param theMessage
* The message
*/ */
public BaseServerResponseException(int theStatusCode, String theMessage) { public BaseServerResponseException(int theStatusCode, String theMessage) {
super(theMessage); super(theMessage);
myStatusCode = theStatusCode; myStatusCode = theStatusCode;
myBaseOperationOutcome = null; myBaseOperationOutcome = null;
} }
/** /**
* Constructor * Constructor
* *
* @param theStatusCode * @param theStatusCode The HTTP status code corresponding to this problem
* The HTTP status code corresponding to this problem * @param theMessages The messages
* @param theMessages
* The messages
*/ */
public BaseServerResponseException(int theStatusCode, String... theMessages) { public BaseServerResponseException(int theStatusCode, String... theMessages) {
super(theMessages != null && theMessages.length > 0 ? theMessages[0] : null); super(theMessages != null && theMessages.length > 0 ? theMessages[0] : null);
@ -100,16 +97,13 @@ public abstract class BaseServerResponseException extends RuntimeException {
myAdditionalMessages = Arrays.asList(Arrays.copyOfRange(theMessages, 1, theMessages.length, String[].class)); myAdditionalMessages = Arrays.asList(Arrays.copyOfRange(theMessages, 1, theMessages.length, String[].class));
} }
} }
/** /**
* Constructor * Constructor
* *
* @param theStatusCode * @param theStatusCode The HTTP status code corresponding to this problem
* The HTTP status code corresponding to this problem * @param theMessage The message
* @param theMessage * @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)
* The message
* @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, IBaseOperationOutcome theBaseOperationOutcome) { public BaseServerResponseException(int theStatusCode, String theMessage, IBaseOperationOutcome theBaseOperationOutcome) {
super(theMessage); super(theMessage);
@ -119,13 +113,10 @@ public abstract class BaseServerResponseException extends RuntimeException {
/** /**
* Constructor * Constructor
* *
* @param theStatusCode * @param theStatusCode The HTTP status code corresponding to this problem
* The HTTP status code corresponding to this problem * @param theMessage The message
* @param theMessage * @param theCause The cause
* The message
* @param theCause
* The cause
*/ */
public BaseServerResponseException(int theStatusCode, String theMessage, Throwable theCause) { public BaseServerResponseException(int theStatusCode, String theMessage, Throwable theCause) {
super(theMessage, theCause); super(theMessage, theCause);
@ -135,15 +126,11 @@ public abstract class BaseServerResponseException extends RuntimeException {
/** /**
* Constructor * Constructor
* *
* @param theStatusCode * @param theStatusCode The HTTP status code corresponding to this problem
* The HTTP status code corresponding to this problem * @param theMessage The message
* @param theMessage * @param theCause The underlying cause exception
* The message * @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)
* @param theCause
* The underlying cause exception
* @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, IBaseOperationOutcome theBaseOperationOutcome) { public BaseServerResponseException(int theStatusCode, String theMessage, Throwable theCause, IBaseOperationOutcome theBaseOperationOutcome) {
super(theMessage, theCause); super(theMessage, theCause);
@ -153,11 +140,9 @@ public abstract class BaseServerResponseException extends RuntimeException {
/** /**
* Constructor * Constructor
* *
* @param theStatusCode * @param theStatusCode The HTTP status code corresponding to this problem
* The HTTP status code corresponding to this problem * @param theCause The underlying cause exception
* @param theCause
* The underlying cause exception
*/ */
public BaseServerResponseException(int theStatusCode, Throwable theCause) { public BaseServerResponseException(int theStatusCode, Throwable theCause) {
super(theCause.getMessage(), theCause); super(theCause.getMessage(), theCause);
@ -167,13 +152,10 @@ public abstract class BaseServerResponseException extends RuntimeException {
/** /**
* Constructor * Constructor
* *
* @param theStatusCode * @param theStatusCode The HTTP status code corresponding to this problem
* The HTTP status code corresponding to this problem * @param theCause The underlying cause exception
* @param theCause * @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)
* The underlying cause exception
* @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, IBaseOperationOutcome theBaseOperationOutcome) { public BaseServerResponseException(int theStatusCode, Throwable theCause, IBaseOperationOutcome theBaseOperationOutcome) {
super(theCause.toString(), theCause); super(theCause.toString(), theCause);
@ -181,10 +163,28 @@ public abstract class BaseServerResponseException extends RuntimeException {
myBaseOperationOutcome = theBaseOperationOutcome; myBaseOperationOutcome = theBaseOperationOutcome;
} }
/**
* This flag can be used to signal to server infrastructure that the message supplied
* to this exception (ie to the constructor) is considered trusted and is safe to
* return to the calling client.
*/
public boolean isErrorMessageTrusted() {
return myErrorMessageTrusted;
}
/**
* This flag can be used to signal to server infrastructure that the message supplied
* to this exception (ie to the constructor) is considered trusted and is safe to
* return to the calling client.
*/
public void setErrorMessageTrusted(boolean theErrorMessageTrusted) {
myErrorMessageTrusted = theErrorMessageTrusted;
}
/** /**
* Add a header which will be added to any responses * Add a header which will be added to any responses
* *
* @param theName The header name * @param theName The header name
* @param theValue The header value * @param theValue The header value
* @return Returns a reference to <code>this</code> for easy method chaining * @return Returns a reference to <code>this</code> for easy method chaining
* @since 2.0 * @since 2.0
@ -193,7 +193,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
Validate.notBlank(theName, "theName must not be null or empty"); Validate.notBlank(theName, "theName must not be null or empty");
Validate.notBlank(theValue, "theValue must not be null or empty"); Validate.notBlank(theValue, "theValue must not be null or empty");
if (getResponseHeaders().containsKey(theName) == false) { if (getResponseHeaders().containsKey(theName) == false) {
getResponseHeaders().put(theName, new ArrayList<String>()); getResponseHeaders().put(theName, new ArrayList<>());
} }
getResponseHeaders().get(theName).add(theValue); getResponseHeaders().get(theName).add(theValue);
return this; return this;
@ -210,6 +210,17 @@ public abstract class BaseServerResponseException extends RuntimeException {
return myBaseOperationOutcome; return myBaseOperationOutcome;
} }
/**
* 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.
*
* @param theBaseOperationOutcome 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(IBaseOperationOutcome theBaseOperationOutcome) {
myBaseOperationOutcome = theBaseOperationOutcome;
}
/** /**
* In a RESTful client, this method will be populated with the body of the HTTP respone if one was provided by the server, or <code>null</code> otherwise. * In a RESTful client, this method will be populated with the body of the HTTP respone if one was provided by the server, or <code>null</code> otherwise.
* <p> * <p>
@ -220,17 +231,24 @@ public abstract class BaseServerResponseException extends RuntimeException {
return myResponseBody; return myResponseBody;
} }
/**
* This method is currently only called internally by HAPI, it should not be called by user code.
*/
public void setResponseBody(String theResponseBody) {
myResponseBody = theResponseBody;
}
/** /**
* Returns a map containing any headers which should be added to the outgoing * Returns a map containing any headers which should be added to the outgoing
* response. This methos creates the map if none exists, so it will never * response. This methos creates the map if none exists, so it will never
* return <code>null</code> * return <code>null</code>
* *
* @since 2.0 (note that this method existed in previous versions of HAPI but the method * @since 2.0 (note that this method existed in previous versions of HAPI but the method
* signature has been changed from <code>Map&lt;String, String[]&gt;</code> to <code>Map&lt;String, List&lt;String&gt;&gt;</code> * signature has been changed from <code>Map&lt;String, String[]&gt;</code> to <code>Map&lt;String, List&lt;String&gt;&gt;</code>
*/ */
public Map<String, List<String>> getResponseHeaders() { public Map<String, List<String>> getResponseHeaders() {
if (myResponseHeaders == null) { if (myResponseHeaders == null) {
myResponseHeaders = new HashMap<String, List<String>>(); myResponseHeaders = new HashMap<>();
} }
return myResponseHeaders; return myResponseHeaders;
} }
@ -245,6 +263,13 @@ public abstract class BaseServerResponseException extends RuntimeException {
return myResponseMimeType; return myResponseMimeType;
} }
/**
* This method is currently only called internally by HAPI, it should not be called by user code.
*/
public void setResponseMimeType(String theResponseMimeType) {
myResponseMimeType = theResponseMimeType;
}
/** /**
* Returns the HTTP status code corresponding to this problem * Returns the HTTP status code corresponding to this problem
*/ */
@ -254,7 +279,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
/** /**
* Does the exception have any headers which should be added to the outgoing response? * Does the exception have any headers which should be added to the outgoing response?
* *
* @see #getResponseHeaders() * @see #getResponseHeaders()
* @since 2.0 * @since 2.0
*/ */
@ -262,32 +287,6 @@ public abstract class BaseServerResponseException extends RuntimeException {
return myResponseHeaders != null && myResponseHeaders.isEmpty() == false; return myResponseHeaders != null && myResponseHeaders.isEmpty() == false;
} }
/**
* 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.
*
* @param theBaseOperationOutcome
* 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(IBaseOperationOutcome theBaseOperationOutcome) {
myBaseOperationOutcome = theBaseOperationOutcome;
}
/**
* This method is currently only called internally by HAPI, it should not be called by user code.
*/
public void setResponseBody(String theResponseBody) {
myResponseBody = theResponseBody;
}
/**
* This method is currently only called internally by HAPI, it should not be called by user code.
*/
public void setResponseMimeType(String theResponseMimeType) {
myResponseMimeType = theResponseMimeType;
}
/** /**
* For unit tests only * For unit tests only
*/ */
@ -298,7 +297,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
public static BaseServerResponseException newInstance(int theStatusCode, String theMessage) { public static BaseServerResponseException newInstance(int theStatusCode, String theMessage) {
if (ourStatusCodeToExceptionType.containsKey(theStatusCode)) { if (ourStatusCodeToExceptionType.containsKey(theStatusCode)) {
try { try {
return ourStatusCodeToExceptionType.get(theStatusCode).getConstructor(new Class[] { String.class }).newInstance(theMessage); return ourStatusCodeToExceptionType.get(theStatusCode).getConstructor(new Class[]{String.class}).newInstance(theMessage);
} catch (InstantiationException e) { } catch (InstantiationException e) {
throw new InternalErrorException(e); throw new InternalErrorException(e);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.server;
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -1204,6 +1204,11 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* <p> * <p>
* The default is <code>false</code> * The default is <code>false</code>
* </p> * </p>
* <p>
* Note that this setting is ignored by {@link ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor}
* when streaming HTML, although even when that interceptor it used this setting will
* still be honoured when streaming raw FHIR.
* </p>
* *
* @return Returns the default pretty print setting * @return Returns the default pretty print setting
*/ */
@ -1219,6 +1224,11 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* <p> * <p>
* The default is <code>false</code> * The default is <code>false</code>
* </p> * </p>
* <p>
* Note that this setting is ignored by {@link ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor}
* when streaming HTML, although even when that interceptor it used this setting will
* still be honoured when streaming raw FHIR.
* </p>
* *
* @param theDefaultPrettyPrint The default pretty print setting * @param theDefaultPrettyPrint The default pretty print setting
*/ */