/** * Check, if reported error cause indicates a temporary error. * * @param cause reported error cause * @return {@code true}, if error is temporary and the client may retry its action, {@code false}, otherwise, when * the client should not repeat this action. */ public static boolean isTemporaryError(final Throwable cause) { if (ServiceInvocationException.class.isInstance(cause)) { return ((ServiceInvocationException) cause).getErrorCode() == HttpURLConnection.HTTP_UNAVAILABLE; } return false; }
/** * Ends a response with a given HTTP status code and detail message. * * @param ctx The vert.x routing context to fail. * @param status The status code to write to the response. * @param headers HTTP headers to set on the response (may be {@code null}). * @param detail The message to write to the response's body (may be {@code null}). * @throws NullPointerException if routing context is {@code null}. * @deprecated Use {@link #failWithHeaders(RoutingContext, ServiceInvocationException, Map)} instead. */ @Deprecated public static void failWithStatus( final RoutingContext ctx, final int status, final Map<CharSequence, CharSequence> headers, final String detail) { failWithHeaders(ctx, new ServiceInvocationException(status, detail), headers); }
/** * Creates an ErrorCondition using the given throwable to provide an error condition value and * description. All throwables that are not service invocation exceptions will be mapped to {@link AmqpError#PRECONDITION_FAILED}. * * @param t The throwable to map to an error condition. * @return The ErrorCondition. */ static ErrorCondition getErrorCondition(final Throwable t) { if (ServiceInvocationException.class.isInstance(t)) { final ServiceInvocationException error = (ServiceInvocationException) t; switch (error.getErrorCode()) { case HttpURLConnection.HTTP_BAD_REQUEST: return ProtonHelper.condition(Constants.AMQP_BAD_REQUEST, error.getMessage()); case HttpURLConnection.HTTP_FORBIDDEN: return ProtonHelper.condition(AmqpError.UNAUTHORIZED_ACCESS, error.getMessage()); default: return ProtonHelper.condition(AmqpError.PRECONDITION_FAILED, error.getMessage()); } } else { return ProtonHelper.condition(AmqpError.PRECONDITION_FAILED, t.getMessage()); } } }
private void processRequestMessage(final Message<JsonObject> msg) { if (log.isTraceEnabled()) { log.trace("received request message: {}", msg.body().encodePrettily()); } final EventBusMessage request = EventBusMessage.fromJson(msg.body()); processRequest(request).recover(t -> { log.debug("cannot process request [operation: {}]: {}", request.getOperation(), t.getMessage()); final int status = ServiceInvocationException.extractStatusCode(t); return Future.succeededFuture(request.getResponse(status)); }).map(response -> { if (response.getReplyToAddress() == null) { log.debug("sending response as direct reply to request [operation: {}]", request.getOperation()); msg.reply(response.toJson()); } else if (response.hasResponseProperties()) { log.debug("sending response [operation: {}, reply-to: {}]", request.getOperation(), request.getReplyToAddress()); vertx.eventBus().send(request.getReplyToAddress(), response.toJson()); } else { log.warn("discarding response lacking correlation ID or operation"); } return null; }); }
/** * Creates a new exception for an error code, a detail message and a root cause. * * @param errorCode The code representing the erroneous outcome. * @param msg The detail message. * @param cause The root cause. * @throws IllegalArgumentException if the code is not ≥ 400 and < 600. */ public ServiceInvocationException(final int errorCode, final String msg, final Throwable cause) { super(providedOrDefaultMessage(errorCode, msg), cause); if (errorCode < 400 || errorCode >= 600) { throw new IllegalArgumentException(String.format("illegal error code [%d], must be >= 400 and < 600", errorCode)); } else { this.errorCode = errorCode; } }
/** * Composes the given future so that the given <em>OpenTracing</em> span is finished when the future completes. * <p> * The result or exception of the given future will be used to set a {@link Tags#HTTP_STATUS} tag on the span * and to set a {@link Tags#ERROR} tag in case of an exception or a result with error status. * * @param span The span to finish. * @param resultFuture The future to be composed. * @return The composed future. */ protected Future<EventBusMessage> finishSpanOnFutureCompletion(final Span span, final Future<EventBusMessage> resultFuture) { return resultFuture.compose(eventBusMessage -> { Tags.HTTP_STATUS.set(span, eventBusMessage.getStatus()); if (eventBusMessage.hasErrorStatus()) { Tags.ERROR.set(span, true); } span.finish(); return Future.succeededFuture(eventBusMessage); }).recover(t -> { Tags.HTTP_STATUS.set(span, ServiceInvocationException.extractStatusCode(t)); TracingHelper.logError(span, t); span.finish(); return Future.failedFuture(t); }); } }
/** * Extract the HTTP status code from an exception. * * @param t The exception to extract the code from. * @return The HTTP status code, or 500 if the exception is not of type {@link ServiceInvocationException}. */ public static int extractStatusCode(final Throwable t) { return Optional.of(t).map(cause -> { if (cause instanceof ServiceInvocationException) { return ((ServiceInvocationException) cause).getErrorCode(); } else { return null; } }).orElse(HttpURLConnection.HTTP_INTERNAL_ERROR); } }
if (e.getCause() instanceof ServiceInvocationException) { final ServiceInvocationException sie = (ServiceInvocationException) e.getCause(); sampleResult.setResponseMessage(sie.getMessage()); sampleResult.setResponseCode(String.valueOf(sie.getErrorCode())); } else { String uncompletedFutureHint = "";
/** * Ends a response with a given HTTP status code and detail message. * * @param ctx The vert.x routing context to fail. * @param status The status code to write to the response. * @param headers HTTP headers to set on the response (may be {@code null}). * @param detail The message to write to the response's body (may be {@code null}). * @throws NullPointerException if routing context is {@code null}. * @deprecated Use {@link #failWithHeaders(RoutingContext, ServiceInvocationException, Map)} instead. */ public static void failWithStatus( final RoutingContext ctx, final int status, final Map<CharSequence, CharSequence> headers, final String detail) { failWithHeaders(ctx, new ServiceInvocationException(status, detail), headers); }
private void processRequestMessage(final Message<JsonObject> msg) { if (log.isTraceEnabled()) { log.trace("received request message: {}", msg.body().encodePrettily()); } final EventBusMessage request = EventBusMessage.fromJson(msg.body()); final SpanContext spanContext = tracer.extract(Format.Builtin.TEXT_MAP, new MultiMapExtractAdapter(msg.headers())); request.setSpanContext(spanContext); processRequest(request).recover(t -> { log.debug("cannot process request [operation: {}]: {}", request.getOperation(), t.getMessage()); final int status = ServiceInvocationException.extractStatusCode(t); return Future.succeededFuture(request.getResponse(status)); }).map(response -> { if (response.getReplyToAddress() == null) { log.debug("sending response as direct reply to request [operation: {}]", request.getOperation()); msg.reply(response.toJson()); } else if (response.hasResponseProperties()) { log.debug("sending response [operation: {}, reply-to: {}]", request.getOperation(), request.getReplyToAddress()); vertx.eventBus().send(request.getReplyToAddress(), response.toJson()); } else { log.warn("discarding response lacking correlation ID or operation"); } return null; }); }
private static MqttConnectReturnCode getConnectReturnCode(final Throwable e) { if (e instanceof MqttConnectionException) { return ((MqttConnectionException) e).code(); } else if (e instanceof ServiceInvocationException) { switch (((ServiceInvocationException) e).getErrorCode()) { case HttpURLConnection.HTTP_UNAUTHORIZED: case HttpURLConnection.HTTP_NOT_FOUND: return MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD; case HttpURLConnection.HTTP_UNAVAILABLE: return MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE; default: return MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; } } else { return MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; } } }
if (ctx.failure() instanceof ServiceInvocationException) { final ServiceInvocationException e = (ServiceInvocationException) ctx.failure(); sendError(ctx.response(), e.getErrorCode(), e.getMessage()); } else if (ctx.failure() instanceof HttpStatusException) { final HttpStatusException e = (HttpStatusException) ctx.failure();
/** * Creates an exception for a status code and detail message. * * @param statusCode The status code. * @param detailMessage The detail message. * @return The exception. * @throws NullPointerException if the response does not contain an error code. */ public static final ServiceInvocationException from(final int statusCode, final String detailMessage) { if (200 <= statusCode && statusCode < 300) { throw new IllegalArgumentException("status code " + statusCode + " does not represent an error"); } else if (400 <= statusCode && statusCode < 500) { switch(statusCode) { case HttpURLConnection.HTTP_CONFLICT: return new ResourceConflictException(detailMessage); default: return new ClientErrorException(statusCode, detailMessage); } } else if (500 <= statusCode && statusCode < 600) { return new ServerErrorException(statusCode, detailMessage); } else { return new ServiceInvocationException(statusCode, detailMessage); } }
filterResponse(Constants.getClientPrincipal(con), response) .recover(t -> { final int status = ServiceInvocationException.extractStatusCode(t); return Future.succeededFuture(response.getResponse(status)); })
private static MqttConnectReturnCode getConnectReturnCode(final Throwable e) { if (e instanceof MqttConnectionException) { return ((MqttConnectionException) e).code(); } else if (e instanceof ServiceInvocationException) { switch (((ServiceInvocationException) e).getErrorCode()) { case HttpURLConnection.HTTP_UNAUTHORIZED: case HttpURLConnection.HTTP_NOT_FOUND: return MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD; case HttpURLConnection.HTTP_UNAVAILABLE: return MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE; default: return MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; } } else { return MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; } } }
if (ctx.failure() instanceof ServiceInvocationException) { final ServiceInvocationException e = (ServiceInvocationException) ctx.failure(); sendError(ctx.response(), e.getErrorCode(), e.getMessage()); } else if (ctx.failure() instanceof HttpStatusException) { final HttpStatusException e = (HttpStatusException) ctx.failure();
result.completeExceptionally(new ServiceInvocationException(response.statusCode()));
filterResponse(Constants.getClientPrincipal(con), response) .recover(t -> { final int status = ServiceInvocationException.extractStatusCode(t); return Future.succeededFuture(response.getResponse(status)); })
/** * Marks an <em>OpenTracing</em> span as erroneous and logs an exception. * <p> * This method does <em>not</em> finish the span. * * @param span The span to mark. * @param error The exception that has occurred. If the exception is a * {@link ServiceInvocationException} then a {@link Tags#HTTP_STATUS} * tag is added containing the exception's error code property value. * @throws NullPointerException if error is {@code null}. */ protected final void logError(final Span span, final Throwable error) { if (span != null) { if (ServiceInvocationException.class.isInstance(error)) { final ServiceInvocationException e = (ServiceInvocationException) error; Tags.HTTP_STATUS.set(span, e.getErrorCode()); } TracingHelper.logError(span, error); } }
/** * Respond the coap exchange with the provide error cause. * * Convert http-code into coap-code, if available. Add cause message as payload for response. * * @param exchange coap exchange to be responded * @param cause error cause * @param defaultCode default response code, if a more specific response code is not available. */ public static void respond(final CoapExchange exchange, final Throwable cause, final ResponseCode defaultCode) { final String message = cause == null ? null : cause.getMessage(); final ResponseCode code; if (ServiceInvocationException.class.isInstance(cause)) { final int error = ((ServiceInvocationException) cause).getErrorCode(); code = toCoapCode(error, defaultCode); switch (error) { case HttpURLConnection.HTTP_UNAVAILABLE: // delay retry by 2 seconds, see http adapter, HttpUtils.serviceUnavailable(ctx, 2) exchange.setMaxAge(2); break; default: break; } } else { code = defaultCode; } respond(exchange, message, code); }