Fix #111 - Don't return stack traces in server responses y default
This commit is contained in:
parent
16857404c5
commit
8434f96e97
|
@ -4,6 +4,8 @@ import javax.servlet.ServletException;
|
||||||
import javax.servlet.annotation.WebServlet;
|
import javax.servlet.annotation.WebServlet;
|
||||||
|
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
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.LoggingInterceptor;
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
|
@ -34,4 +36,27 @@ public class ServletExamples {
|
||||||
|
|
||||||
}
|
}
|
||||||
// END SNIPPET: loggingInterceptor
|
// END SNIPPET: loggingInterceptor
|
||||||
|
|
||||||
|
|
||||||
|
// START SNIPPET: exceptionInterceptor
|
||||||
|
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
|
||||||
|
public class RestfulServerWithExceptionHandling extends RestfulServer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initialize() throws ServletException {
|
||||||
|
|
||||||
|
// ... define your resource providers here ...
|
||||||
|
|
||||||
|
// Now register the logging interceptor
|
||||||
|
ExceptionHandlingInterceptor interceptor = new ExceptionHandlingInterceptor();
|
||||||
|
registerInterceptor(interceptor);
|
||||||
|
|
||||||
|
// Return the stack trace to the client for the following exception types
|
||||||
|
interceptor.setReturnStackTracesForExceptionTypes(InternalErrorException.class, NullPointerException.class);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// END SNIPPET: exceptionInterceptor
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.NotModifiedException;
|
import ca.uhn.fhir.rest.server.exceptions.NotModifiedException;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
import ca.uhn.fhir.util.ReflectionUtil;
|
import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
@ -715,60 +716,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseOperationOutcome oo = null;
|
new ExceptionHandlingInterceptor().handleException(requestDetails, e, theRequest, theResponse);
|
||||||
int statusCode = Constants.STATUS_HTTP_500_INTERNAL_ERROR;
|
|
||||||
|
|
||||||
if (e instanceof BaseServerResponseException) {
|
|
||||||
oo = ((BaseServerResponseException) e).getOperationOutcome();
|
|
||||||
statusCode = ((BaseServerResponseException) e).getStatusCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generate an OperationOutcome to return, unless the exception throw by the resource provider had one
|
|
||||||
*/
|
|
||||||
if (oo == null) {
|
|
||||||
try {
|
|
||||||
oo = (BaseOperationOutcome) myFhirContext.getResourceDefinition("OperationOutcome").getImplementingClass().newInstance();
|
|
||||||
} catch (Exception e1) {
|
|
||||||
ourLog.error("Failed to instantiate OperationOutcome resource instance", e1);
|
|
||||||
throw new ServletException("Failed to instantiate OperationOutcome resource instance", e1);
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseIssue issue = oo.addIssue();
|
|
||||||
issue.getSeverityElement().setValue("error");
|
|
||||||
if (e instanceof InternalErrorException) {
|
|
||||||
ourLog.error("Failure during REST processing", e);
|
|
||||||
issue.getDetailsElement().setValue(e.toString() + "\n\n" + ExceptionUtils.getStackTrace(e));
|
|
||||||
} else if (e instanceof BaseServerResponseException) {
|
|
||||||
ourLog.warn("Failure during REST processing: {}", e);
|
|
||||||
BaseServerResponseException baseServerResponseException = (BaseServerResponseException) e;
|
|
||||||
statusCode = baseServerResponseException.getStatusCode();
|
|
||||||
issue.getDetailsElement().setValue(e.getMessage());
|
|
||||||
if (baseServerResponseException.getAdditionalMessages() != null) {
|
|
||||||
for (String next : baseServerResponseException.getAdditionalMessages()) {
|
|
||||||
BaseIssue issue2 = oo.addIssue();
|
|
||||||
issue2.getSeverityElement().setValue("error");
|
|
||||||
issue2.setDetails(next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ourLog.error("Failure during REST processing: " + e.toString(), e);
|
|
||||||
issue.getDetailsElement().setValue(e.toString() + "\n\n" + ExceptionUtils.getStackTrace(e));
|
|
||||||
statusCode = Constants.STATUS_HTTP_500_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ourLog.error("Unknown error during processing", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
RestfulServerUtils.streamResponseAsResource(this, theResponse, oo, RestfulServerUtils.determineResponseEncodingNoDefault(theRequest), true, requestIsBrowser, NarrativeModeEnum.NORMAL,
|
|
||||||
statusCode, false, fhirServerBase);
|
|
||||||
|
|
||||||
theResponse.setStatus(statusCode);
|
|
||||||
addHeadersToResponse(theResponse);
|
|
||||||
theResponse.setContentType("text/plain");
|
|
||||||
theResponse.setCharacterEncoding("UTF-8");
|
|
||||||
theResponse.getWriter().append(e.getMessage());
|
|
||||||
theResponse.getWriter().close();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -897,7 +845,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
myInterceptors.add(theInterceptor);
|
myInterceptors.add(theInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean requestIsBrowser(HttpServletRequest theRequest) {
|
public static boolean requestIsBrowser(HttpServletRequest theRequest) {
|
||||||
String userAgent = theRequest.getHeader("User-Agent");
|
String userAgent = theRequest.getHeader("User-Agent");
|
||||||
return userAgent != null && userAgent.contains("Mozilla");
|
return userAgent != null && userAgent.contains("Mozilla");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||||
|
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome.BaseIssue;
|
||||||
|
import ca.uhn.fhir.rest.method.Request;
|
||||||
|
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.RestfulServer.NarrativeModeEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
|
||||||
|
public class ExceptionHandlingInterceptor extends InterceptorAdapter {
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionHandlingInterceptor.class);
|
||||||
|
private Class<?>[] myReturnStackTracesForExceptionTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If any server methods throw an exception which extends any of the given exception types, the exception
|
||||||
|
* stack trace will be returned to the user. This can be useful for helping to diagnose issues, but may
|
||||||
|
* not be desirable for production situations.
|
||||||
|
*
|
||||||
|
* @param theExceptionTypes The exception types for which to return the stack trace to the user.
|
||||||
|
* @return Returns an instance of this interceptor, to allow for easy method chaining.
|
||||||
|
*/
|
||||||
|
public ExceptionHandlingInterceptor setReturnStackTracesForExceptionTypes(Class<?>... theExceptionTypes) {
|
||||||
|
myReturnStackTracesForExceptionTypes = theExceptionTypes;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException {
|
||||||
|
|
||||||
|
BaseOperationOutcome oo = null;
|
||||||
|
int statusCode = Constants.STATUS_HTTP_500_INTERNAL_ERROR;
|
||||||
|
|
||||||
|
FhirContext ctx = theRequestDetails.getServer().getFhirContext();
|
||||||
|
|
||||||
|
if (theException instanceof BaseServerResponseException) {
|
||||||
|
oo = ((BaseServerResponseException) theException).getOperationOutcome();
|
||||||
|
statusCode = ((BaseServerResponseException) theException).getStatusCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate an OperationOutcome to return, unless the exception throw by the resource provider had one
|
||||||
|
*/
|
||||||
|
if (oo == null) {
|
||||||
|
try {
|
||||||
|
oo = (BaseOperationOutcome) ctx.getResourceDefinition("OperationOutcome").getImplementingClass().newInstance();
|
||||||
|
} catch (Exception e1) {
|
||||||
|
ourLog.error("Failed to instantiate OperationOutcome resource instance", e1);
|
||||||
|
throw new ServletException("Failed to instantiate OperationOutcome resource instance", e1);
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseIssue issue = oo.addIssue();
|
||||||
|
issue.getSeverityElement().setValue("error");
|
||||||
|
if (theException instanceof InternalErrorException) {
|
||||||
|
ourLog.error("Failure during REST processing", theException);
|
||||||
|
populateDetails(theException, issue);
|
||||||
|
} else if (theException instanceof BaseServerResponseException) {
|
||||||
|
ourLog.warn("Failure during REST processing: {}", theException);
|
||||||
|
BaseServerResponseException baseServerResponseException = (BaseServerResponseException) theException;
|
||||||
|
statusCode = baseServerResponseException.getStatusCode();
|
||||||
|
populateDetails(theException, issue);
|
||||||
|
if (baseServerResponseException.getAdditionalMessages() != null) {
|
||||||
|
for (String next : baseServerResponseException.getAdditionalMessages()) {
|
||||||
|
BaseIssue issue2 = oo.addIssue();
|
||||||
|
issue2.getSeverityElement().setValue("error");
|
||||||
|
issue2.setDetails(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ourLog.error("Failure during REST processing: " + theException.toString(), theException);
|
||||||
|
populateDetails(theException, issue);
|
||||||
|
statusCode = Constants.STATUS_HTTP_500_INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ourLog.error("Unknown error during processing", theException);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean requestIsBrowser = RestfulServer.requestIsBrowser(theRequest);
|
||||||
|
String fhirServerBase = ((Request) theRequestDetails).getFhirServerBase();
|
||||||
|
RestfulServerUtils.streamResponseAsResource(theRequestDetails.getServer(), theResponse, oo, RestfulServerUtils.determineResponseEncodingNoDefault(theRequest), true, requestIsBrowser,
|
||||||
|
NarrativeModeEnum.NORMAL, statusCode, false, fhirServerBase);
|
||||||
|
|
||||||
|
theResponse.setStatus(statusCode);
|
||||||
|
theRequestDetails.getServer().addHeadersToResponse(theResponse);
|
||||||
|
theResponse.setContentType("text/plain");
|
||||||
|
theResponse.setCharacterEncoding("UTF-8");
|
||||||
|
theResponse.getWriter().append(theException.getMessage());
|
||||||
|
theResponse.getWriter().close();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateDetails(Throwable theException, BaseIssue issue) {
|
||||||
|
if (myReturnStackTracesForExceptionTypes != null) {
|
||||||
|
for (Class<?> next : myReturnStackTracesForExceptionTypes) {
|
||||||
|
if (next.isAssignableFrom(theException.getClass())) {
|
||||||
|
issue.getDetailsElement().setValue(theException.getMessage() + "\n\n" + ExceptionUtils.getStackTrace(theException));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue.getDetailsElement().setValue(theException.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package ca.uhn.fhir.rest.server;
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -41,10 +42,14 @@ import ca.uhn.fhir.util.PortUtil;
|
||||||
*/
|
*/
|
||||||
public class ExceptionTest {
|
public class ExceptionTest {
|
||||||
|
|
||||||
|
private static final String OPERATION_OUTCOME_DETAILS = "OperationOutcomeDetails";
|
||||||
private static CloseableHttpClient ourClient;
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static Class<? extends Exception> ourExceptionType;
|
||||||
private static boolean ourGenerateOperationOutcome;
|
private static boolean ourGenerateOperationOutcome;
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionTest.class);
|
||||||
|
|
||||||
private static int ourPort;
|
private static int ourPort;
|
||||||
|
|
||||||
private static Server ourServer;
|
private static Server ourServer;
|
||||||
|
|
||||||
private static RestfulServer servlet;
|
private static RestfulServer servlet;
|
||||||
|
@ -55,21 +60,6 @@ public class ExceptionTest {
|
||||||
ourExceptionType=null;
|
ourExceptionType=null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testThrowUnprocessableEntityWithMultipleMessages() throws Exception {
|
|
||||||
{
|
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?throwUnprocessableEntityWithMultipleMessages=aaa");
|
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
|
||||||
ourLog.info(responseContent);
|
|
||||||
assertEquals(422, status.getStatusLine().getStatusCode());
|
|
||||||
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newXmlParser().parseResource(responseContent);
|
|
||||||
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("message1"));
|
|
||||||
assertEquals(3, oo.getIssue().size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInternalError() throws Exception {
|
public void testInternalError() throws Exception {
|
||||||
{
|
{
|
||||||
|
@ -80,10 +70,39 @@ public class ExceptionTest {
|
||||||
ourLog.info(responseContent);
|
ourLog.info(responseContent);
|
||||||
assertEquals(500, status.getStatusLine().getStatusCode());
|
assertEquals(500, status.getStatusLine().getStatusCode());
|
||||||
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newXmlParser().parseResource(responseContent);
|
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newXmlParser().parseResource(responseContent);
|
||||||
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("InternalErrorException: Exception Text"));
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("Exception Text"));
|
||||||
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), not(StringContains.containsString("InternalErrorException")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInternalErrorFormatted() throws Exception {
|
||||||
|
{
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?throwInternalError=aaa&_format=true");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info(responseContent);
|
||||||
|
assertEquals(500, status.getStatusLine().getStatusCode());
|
||||||
|
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newXmlParser().parseResource(responseContent);
|
||||||
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("Exception Text"));
|
||||||
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), not(StringContains.containsString("InternalErrorException")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInternalErrorJson() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?throwInternalError=aaa&_format=json");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info(responseContent);
|
||||||
|
assertEquals(500, status.getStatusLine().getStatusCode());
|
||||||
|
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newJsonParser().parseResource(responseContent);
|
||||||
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("Exception Text"));
|
||||||
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), not(StringContains.containsString("InternalErrorException")));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResourceReturning() throws Exception {
|
public void testResourceReturning() throws Exception {
|
||||||
// No OO
|
// No OO
|
||||||
|
@ -115,30 +134,35 @@ public class ExceptionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInternalErrorFormatted() throws Exception {
|
public void testThrowUnprocessableEntityWithMultipleMessages() throws Exception {
|
||||||
{
|
{
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?throwInternalError=aaa&_format=true");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?throwUnprocessableEntityWithMultipleMessages=aaa");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
ourLog.info(responseContent);
|
ourLog.info(responseContent);
|
||||||
assertEquals(500, status.getStatusLine().getStatusCode());
|
assertEquals(422, status.getStatusLine().getStatusCode());
|
||||||
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newXmlParser().parseResource(responseContent);
|
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newXmlParser().parseResource(responseContent);
|
||||||
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("InternalErrorException: Exception Text"));
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("message1"));
|
||||||
|
assertEquals(3, oo.getIssue().size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInternalErrorJson() throws Exception {
|
public void testUnprocessableEntityFormatted() throws Exception {
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?throwInternalError=aaa&_format=json");
|
{
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?throwUnprocessableEntity=aaa&_format=true");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
ourLog.info(responseContent);
|
ourLog.info(responseContent);
|
||||||
assertEquals(500, status.getStatusLine().getStatusCode());
|
assertEquals(UnprocessableEntityException.STATUS_CODE, status.getStatusLine().getStatusCode());
|
||||||
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newJsonParser().parseResource(responseContent);
|
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newXmlParser().parseResource(responseContent);
|
||||||
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("InternalErrorException: Exception Text"));
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("Exception Text"));
|
||||||
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), not(StringContains.containsString("UnprocessableEntityException")));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterClass() throws Exception {
|
public static void afterClass() throws Exception {
|
||||||
|
@ -166,27 +190,12 @@ public class ExceptionTest {
|
||||||
ourClient = builder.build();
|
ourClient = builder.build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static Class<? extends Exception> ourExceptionType;
|
|
||||||
|
|
||||||
private static final String OPERATION_OUTCOME_DETAILS = "OperationOutcomeDetails";
|
|
||||||
/**
|
/**
|
||||||
* Created by dsotnikov on 2/25/2014.
|
* Created by dsotnikov on 2/25/2014.
|
||||||
*/
|
*/
|
||||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
|
||||||
@Search
|
|
||||||
public List<Patient> throwInternalError(@RequiredParam(name = "throwInternalError") StringParam theParam) {
|
|
||||||
throw new InternalErrorException("Exception Text");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Search
|
|
||||||
public List<Patient> throwUnprocessableEntityWithMultipleMessages(@RequiredParam(name = "throwUnprocessableEntityWithMultipleMessages") StringParam theParam) {
|
|
||||||
throw new UnprocessableEntityException("message1", "message2", "message3");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<? extends IResource> getResourceType() {
|
public Class<? extends IResource> getResourceType() {
|
||||||
return Patient.class;
|
return Patient.class;
|
||||||
|
@ -208,6 +217,21 @@ public class ExceptionTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Search
|
||||||
|
public List<Patient> throwInternalError(@RequiredParam(name = "throwInternalError") StringParam theParam) {
|
||||||
|
throw new InternalErrorException("Exception Text");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search()
|
||||||
|
public List<Patient> throwUnprocessableEntity(@RequiredParam(name = "throwUnprocessableEntity") StringParam theParam) {
|
||||||
|
throw new UnprocessableEntityException("Exception Text");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search
|
||||||
|
public List<Patient> throwUnprocessableEntityWithMultipleMessages(@RequiredParam(name = "throwUnprocessableEntityWithMultipleMessages") StringParam theParam) {
|
||||||
|
throw new UnprocessableEntityException("message1", "message2", "message3");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
package ca.uhn.fhir.rest.server.interceptor;
|
package ca.uhn.fhir.rest.server.interceptor;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import junit.framework.AssertionFailedError;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
|
@ -26,96 +23,87 @@ import org.junit.AfterClass;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
|
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.UriDt;
|
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Read;
|
import ca.uhn.fhir.rest.annotation.Read;
|
||||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
import ca.uhn.fhir.rest.server.ExceptionTest;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.PortUtil;
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
|
||||||
public class ExceptionHandlingInterceptorTest {
|
public class ExceptionHandlingInterceptorTest {
|
||||||
|
|
||||||
|
private static final String OPERATION_OUTCOME_DETAILS = "OperationOutcomeDetails";
|
||||||
private static CloseableHttpClient ourClient;
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static Class<? extends Exception> ourExceptionType;
|
||||||
|
private static boolean ourGenerateOperationOutcome;
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionTest.class);
|
||||||
|
|
||||||
private static int ourPort;
|
private static int ourPort;
|
||||||
|
|
||||||
private static Server ourServer;
|
private static Server ourServer;
|
||||||
|
|
||||||
private static RestfulServer servlet;
|
private static RestfulServer servlet;
|
||||||
private IServerInterceptor myInterceptor;
|
private static ExceptionHandlingInterceptor myInterceptor;
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionHandlingInterceptorTest.class);
|
|
||||||
|
|
||||||
@Test
|
@Before
|
||||||
public void testThrowUnprocessableEntityException() throws Exception {
|
public void before() {
|
||||||
|
ourGenerateOperationOutcome = false;
|
||||||
|
ourExceptionType=null;
|
||||||
|
|
||||||
when(myInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
myInterceptor.setReturnStackTracesForExceptionTypes(Throwable.class);
|
||||||
when(myInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
|
||||||
when(myInterceptor.handleException(any(RequestDetails.class), any(Throwable.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
|
||||||
|
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=throwUnprocessableEntityException");
|
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
|
||||||
ourLog.info(IOUtils.toString(status.getEntity().getContent()));
|
|
||||||
assertEquals(422, status.getStatusLine().getStatusCode());
|
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
|
||||||
|
|
||||||
ArgumentCaptor<Throwable> captor = ArgumentCaptor.forClass(Throwable.class);
|
|
||||||
verify(myInterceptor, times(1)).handleException(any(RequestDetails.class), captor.capture(), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
|
||||||
|
|
||||||
assertEquals(UnprocessableEntityException.class, captor.getValue().getClass());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testThrowUnprocessableEntityExceptionAndOverrideResponse() throws Exception {
|
public void testInternalError() throws Exception {
|
||||||
|
myInterceptor.setReturnStackTracesForExceptionTypes(Throwable.class);
|
||||||
when(myInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
{
|
||||||
when(myInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?throwInternalError=aaa");
|
||||||
|
|
||||||
when(myInterceptor.handleException(any(RequestDetails.class), any(Throwable.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenAnswer(new Answer<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public Boolean answer(InvocationOnMock theInvocation) throws Throwable {
|
|
||||||
HttpServletResponse resp = (HttpServletResponse) theInvocation.getArguments()[3];
|
|
||||||
resp.setStatus(405);
|
|
||||||
resp.setContentType("text/plain");
|
|
||||||
resp.getWriter().write("HELP IM A BUG");
|
|
||||||
resp.getWriter().close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=throwUnprocessableEntityException");
|
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
ourLog.info(responseContent);
|
|
||||||
assertEquals(405, status.getStatusLine().getStatusCode());
|
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info(responseContent);
|
||||||
assertEquals("HELP IM A BUG", responseContent);
|
assertEquals(500, status.getStatusLine().getStatusCode());
|
||||||
|
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newXmlParser().parseResource(responseContent);
|
||||||
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("Exception Text"));
|
||||||
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), (StringContains.containsString("InternalErrorException: Exception Text")));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInternalErrorFormatted() throws Exception {
|
||||||
|
myInterceptor.setReturnStackTracesForExceptionTypes(Throwable.class);
|
||||||
|
{
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?throwInternalError=aaa&_format=true");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info(responseContent);
|
||||||
|
assertEquals(500, status.getStatusLine().getStatusCode());
|
||||||
|
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newXmlParser().parseResource(responseContent);
|
||||||
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("Exception Text"));
|
||||||
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), (StringContains.containsString("InternalErrorException: Exception Text")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterClass() throws Exception {
|
public static void afterClass() throws Exception {
|
||||||
ourServer.stop();
|
ourServer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
|
||||||
public void before() {
|
|
||||||
myInterceptor = mock(IServerInterceptor.class);
|
|
||||||
servlet.setInterceptors(Collections.singletonList(myInterceptor));
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void beforeClass() throws Exception {
|
public static void beforeClass() throws Exception {
|
||||||
ourPort = PortUtil.findFreePort();
|
ourPort = PortUtil.findFreePort();
|
||||||
|
@ -136,30 +124,51 @@ public class ExceptionHandlingInterceptorTest {
|
||||||
builder.setConnectionManager(connectionManager);
|
builder.setConnectionManager(connectionManager);
|
||||||
ourClient = builder.build();
|
ourClient = builder.build();
|
||||||
|
|
||||||
|
myInterceptor = new ExceptionHandlingInterceptor();
|
||||||
|
servlet.registerInterceptor(myInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by dsotnikov on 2/25/2014.
|
* Created by dsotnikov on 2/25/2014.
|
||||||
*/
|
*/
|
||||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the resource by its identifier
|
|
||||||
*
|
|
||||||
* @param theId
|
|
||||||
* The resource identity
|
|
||||||
* @return The resource
|
|
||||||
*/
|
|
||||||
@Search(queryName = "throwUnprocessableEntityException")
|
|
||||||
public List<Patient> throwUnprocessableEntityException() {
|
|
||||||
throw new UnprocessableEntityException("Unprocessable!");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<Patient> getResourceType() {
|
public Class<? extends IResource> getResourceType() {
|
||||||
return Patient.class;
|
return Patient.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Read
|
||||||
|
public Patient read(@IdParam IdDt theId) {
|
||||||
|
OperationOutcome oo = null;
|
||||||
|
if (ourGenerateOperationOutcome) {
|
||||||
|
oo = new OperationOutcome();
|
||||||
|
oo.addIssue().setDetails(OPERATION_OUTCOME_DETAILS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ourExceptionType == ResourceNotFoundException.class) {
|
||||||
|
throw new ResourceNotFoundException(theId, oo);
|
||||||
|
}else {
|
||||||
|
throw new AssertionFailedError("Unknown exception type: " + ourExceptionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search
|
||||||
|
public List<Patient> throwInternalError(@RequiredParam(name = "throwInternalError") StringParam theParam) {
|
||||||
|
throw new InternalErrorException("Exception Text");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search()
|
||||||
|
public List<Patient> throwUnprocessableEntity(@RequiredParam(name = "throwUnprocessableEntity") StringParam theParam) {
|
||||||
|
throw new UnprocessableEntityException("Exception Text");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search
|
||||||
|
public List<Patient> throwUnprocessableEntityWithMultipleMessages(@RequiredParam(name = "throwUnprocessableEntityWithMultipleMessages") StringParam theParam) {
|
||||||
|
throw new UnprocessableEntityException("message1", "message2", "message3");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
package ca.uhn.fhir.rest.server.interceptor;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
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.hamcrest.core.StringContains;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
|
||||||
|
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.UriDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Read;
|
||||||
|
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
|
||||||
|
public class ExceptionInterceptorMethodTest {
|
||||||
|
|
||||||
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static int ourPort;
|
||||||
|
private static Server ourServer;
|
||||||
|
private static RestfulServer servlet;
|
||||||
|
private IServerInterceptor myInterceptor;
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionInterceptorMethodTest.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testThrowUnprocessableEntityException() throws Exception {
|
||||||
|
|
||||||
|
when(myInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||||
|
when(myInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||||
|
when(myInterceptor.handleException(any(RequestDetails.class), any(Throwable.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=throwUnprocessableEntityException");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
ourLog.info(IOUtils.toString(status.getEntity().getContent()));
|
||||||
|
assertEquals(422, status.getStatusLine().getStatusCode());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
ArgumentCaptor<Throwable> captor = ArgumentCaptor.forClass(Throwable.class);
|
||||||
|
verify(myInterceptor, times(1)).handleException(any(RequestDetails.class), captor.capture(), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||||
|
|
||||||
|
assertEquals(UnprocessableEntityException.class, captor.getValue().getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testThrowUnprocessableEntityExceptionAndOverrideResponse() throws Exception {
|
||||||
|
|
||||||
|
when(myInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||||
|
when(myInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||||
|
|
||||||
|
when(myInterceptor.handleException(any(RequestDetails.class), any(Throwable.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenAnswer(new Answer<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public Boolean answer(InvocationOnMock theInvocation) throws Throwable {
|
||||||
|
HttpServletResponse resp = (HttpServletResponse) theInvocation.getArguments()[3];
|
||||||
|
resp.setStatus(405);
|
||||||
|
resp.setContentType("text/plain");
|
||||||
|
resp.getWriter().write("HELP IM A BUG");
|
||||||
|
resp.getWriter().close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=throwUnprocessableEntityException");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
ourLog.info(responseContent);
|
||||||
|
assertEquals(405, status.getStatusLine().getStatusCode());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("HELP IM A BUG", responseContent);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
myInterceptor = mock(IServerInterceptor.class);
|
||||||
|
servlet.setInterceptors(Collections.singletonList(myInterceptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = PortUtil.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
|
||||||
|
|
||||||
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
|
servlet = new RestfulServer();
|
||||||
|
servlet.setResourceProviders(patientProvider);
|
||||||
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
|
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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the resource by its identifier
|
||||||
|
*
|
||||||
|
* @param theId
|
||||||
|
* The resource identity
|
||||||
|
* @return The resource
|
||||||
|
*/
|
||||||
|
@Search(queryName = "throwUnprocessableEntityException")
|
||||||
|
public List<Patient> throwUnprocessableEntityException() {
|
||||||
|
throw new UnprocessableEntityException("Unprocessable!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Patient> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -158,6 +158,12 @@
|
||||||
<action type="add">
|
<action type="add">
|
||||||
Sorting is now supported in the Web Testing UI (previously a button existed for sorting, but it didn't do anything)
|
Sorting is now supported in the Web Testing UI (previously a button existed for sorting, but it didn't do anything)
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add" issue="111">
|
||||||
|
Server will no longer include stack traces in the OperationOutcome returned to the client
|
||||||
|
when an exception is thrown. A new interceptor called ExceptionHandlingInterceptor has been
|
||||||
|
created which adds this functionality back if it is needed (e.g. for DEV setups). See the
|
||||||
|
server interceptor documentation for more information. Thanks to Andy Huang for the suggestion!
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="0.8" date="2014-Dec-17">
|
<release version="0.8" date="2014-Dec-17">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
|
@ -150,6 +150,35 @@
|
||||||
|
|
||||||
</subsection>
|
</subsection>
|
||||||
|
|
||||||
|
<a name="ExceptionHandlingInterceptor"/>
|
||||||
|
<subsection name="Exception Handling">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The
|
||||||
|
<a href="./apidocs/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.html">ExceptionHandlingInterceptor</a>
|
||||||
|
(<a href="./xref/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.html">code</a>)
|
||||||
|
can be used to customize what is returned to the client and what is logged when the server throws an
|
||||||
|
exception for any reason (including routine things like UnprocessableEntityExceptions thrown as a matter of
|
||||||
|
normal processing in a create method, but also including unexpected NullPointerExceptions thrown by client code).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The following example shows how to register an exception handling interceptor within
|
||||||
|
a FHIR RESTful server.
|
||||||
|
</p>
|
||||||
|
<macro name="snippet">
|
||||||
|
<param name="id" value="exceptionInterceptor" />
|
||||||
|
<param name="file" value="examples/src/main/java/example/ServletExamples.java" />
|
||||||
|
</macro>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This interceptor will then produce output similar to the following:
|
||||||
|
</p>
|
||||||
|
<source><![CDATA[2014-09-04 02:37:30.030 Source[127.0.0.1] Operation[vread Patient/1667/_history/1] UA[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.94 Safari/537.36] Params[?_format=json]
|
||||||
|
2014-09-04 03:30:00.443 Source[127.0.0.1] Operation[search-type Organization] UA[Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)] Params[]]]></source>
|
||||||
|
|
||||||
|
</subsection>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section name="Creating Interceptors">
|
<section name="Creating Interceptors">
|
||||||
|
|
Loading…
Reference in New Issue