in rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java [81:196]
public Response toResponse(Throwable throwable1) {
String user = Strings.toString(Entitlements.getEntitlementContext());
if (user==null) user = "<not_logged_in>";
String path = null;
try {
// get path if possible for all requests
ContainerRequestContext requestContext = resourceContext.getResource(ContainerRequestContext.class);
path = requestContext.getUriInfo().getPath();
if (LOG.isTraceEnabled()) {
LOG.trace("ERROR CONTEXT DETAILS for "+throwable1);
LOG.trace("url: "+requestContext.getUriInfo());
LOG.trace("headers: "+requestContext.getHeaders());
}
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
if (firstEncounter(e)) {
// include debug trace for everything the first time we get one
LOG.debug("Unable to resolve path on error "+throwable1+" (logging once): "+e);
}
}
if (path==null) path = "<path_unavailable>";
// EofException is thrown when the connection is reset,
// for example when refreshing the browser window.
// Don't depend on jetty, could be running in other environments as well.
if (throwable1.getClass().getName().equals("org.eclipse.jetty.io.EofException")) {
if (LOG.isTraceEnabled()) {
LOG.trace("REST request {} running as user {} was disconnected, threw: {}",
path, user,
Exceptions.collapseText(throwable1));
}
return null;
}
String errorReference = Identifiers.makeRandomId(13);
Throwable throwable2 = Exceptions.getFirstInteresting(throwable1);
if (isSevere(throwable2)) {
LOG.warn("REST request {} running as {} threw severe: {} (ref {})",
path, user,
Exceptions.collapseText(throwable1), errorReference);
} else {
LOG.debug("REST request {} running as {} threw: {} (ref {})",
path, user,
Exceptions.collapseText(throwable1), errorReference);
}
logExceptionDetailsForDebugging(throwable1, errorReference);
// Some methods will throw this, which gets converted automatically
if (throwable2 instanceof WebApplicationException) {
if (throwable2 instanceof ApiError.WebApplicationExceptionWithApiError) {
ApiError apiError = ((ApiError.WebApplicationExceptionWithApiError)throwable2).getApiError();
if (!isTraceVisibleToUser()) {
if (apiError.getDetails() != null) {
LOG.debug("Details of suppressed API error ref " + errorReference + ": " + apiError.getDetails());
}
return ApiError.builder().message(apiError.getMessage()).details("Reference: " + errorReference).errorCode(apiError.getError()).build().asJsonResponse();
} else {
// include error reference
return ApiError.builder().copy(apiError).message(""+apiError.getMessage()+" (Reference: "+errorReference+")").build().asJsonResponse();
}
} else {
WebApplicationException wae = (WebApplicationException) throwable2;
// could suppress all messages if anything includes unwanted details (but believed not to be the case)
// note error reference not included, but we'll live with that for this error which isn't used (will still be logged above, just not reported to user)
return wae.getResponse();
}
}
// The nicest way for methods to provide errors, wrap as this, and the stack trace will be suppressed
if (throwable2 instanceof UserFacingException) {
return ApiError.of(throwable2.getMessage()).asBadRequestResponseJson();
}
// For everything else, a trace is supplied unless blocked
// Assume ClassCoercionExceptions are caused by TypeCoercions from input parameters gone wrong
// And IllegalArgumentException for malformed input parameters.
if (throwable2 instanceof ClassCoercionException || throwable2 instanceof IllegalArgumentException) {
if (!isTraceVisibleToUser()) {
return ApiError.of(throwable2.getMessage()).asBadRequestResponseJson();
}
return ApiError.of(throwable2).asBadRequestResponseJson();
}
// YAML exception
if (throwable2 instanceof YAMLException) {
return ApiError.builder().message(throwable2.getMessage()).details("Reference: "+errorReference).prefixMessage("Invalid YAML").build().asBadRequestResponseJson();
}
if (!isTooUninterestingToLogWarn(throwable2)) {
if (encounteredUnknownExceptions.add( throwable2.getClass() )) {
LOG.warn("REST call reference "+errorReference+" generated exception type "+throwable2.getClass()+" unrecognized in "+getClass()+" (subsequent occurrences will be logged debug only): " + throwable2, throwable2);
}
}
// Before saying server error, look for a user-facing exception anywhere in the hierarchy
UserFacingException userFacing = Exceptions.getFirstThrowableOfType(throwable1, UserFacingException.class);
if (userFacing instanceof UserFacingException) {
return ApiError.builder().message(userFacing.getMessage()).details("Reference: "+errorReference).build().asBadRequestResponseJson();
}
if (!isTraceVisibleToUser()) {
return ApiError.builder()
.message("Internal error. Contact server administrator citing reference " + errorReference +" to consult logs for more details.")
.details("Reference: "+errorReference)
.build().asResponse(Status.INTERNAL_SERVER_ERROR, MediaType.APPLICATION_JSON_TYPE);
} else {
Builder rb = ApiError.builderFromThrowable(Exceptions.collapse(throwable2));
if (Strings.isBlank(rb.getMessage())) {
rb.message("Internal error. Contact server administrator citing reference " + errorReference + " to consult logs for more details.");
}
rb.details("Reference: "+errorReference);
return rb.build().asResponse(Status.INTERNAL_SERVER_ERROR, MediaType.APPLICATION_JSON_TYPE);
}
}