/** * Handles an unimplemented operation by failing the given handler * with a {@link ClientErrorException} having a <em>501 Not Implemented</em> status code. * * @param resultHandler The handler. */ protected void handleUnimplementedOperation(final Handler<AsyncResult<TenantResult<JsonObject>>> resultHandler) { resultHandler.handle(Future.failedFuture(new ServerErrorException(HttpURLConnection.HTTP_NOT_IMPLEMENTED))); } }
/** * Handles an unimplemented operation by failing the given handler * with a {@link ClientErrorException} having a <em>501 Not Implemented</em> status code. * * @param resultHandler The handler. */ protected void handleUnimplementedOperation(final Handler<AsyncResult<TenantResult<JsonObject>>> resultHandler) { resultHandler.handle(Future.failedFuture(new ServerErrorException(HttpURLConnection.HTTP_NOT_IMPLEMENTED))); } }
private void checkConnected(final Handler<AsyncResult<Void>> resultHandler) { if (isConnectedInternal()) { resultHandler.handle(Future.succeededFuture()); } else { resultHandler.handle(Future.failedFuture( new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "not connected"))); } }
/** * Fails a routing context with HTTP status code 500 (Internal Error) and an optional message. * * @param ctx The vert.x routing context to fail. * @param msg The message to write to the response's body (may be {@code null}). * @throws NullPointerException if routing context is {@code null}. */ public static void internalServerError(final RoutingContext ctx, final String msg) { failWithHeaders( ctx, new ServerErrorException(HttpURLConnection.HTTP_INTERNAL_ERROR, msg), null); }
/** * Fails a routing context with HTTP status code 500 (Internal Error) and an optional message. * * @param ctx The vert.x routing context to fail. * @param msg The message to write to the response's body (may be {@code null}). * @throws NullPointerException if routing context is {@code null}. */ public static void internalServerError(final RoutingContext ctx, final String msg) { failWithHeaders( ctx, new ServerErrorException(HttpURLConnection.HTTP_INTERNAL_ERROR, msg), null); }
/** * Fails a routing context with HTTP status code 503 (Service Unavailable) and sets the <em>Retry-After</em> HTTP header * to a given number of seconds. * * @param ctx The vert.x routing context to fail. * @param retryAfterSeconds The number of seconds to set in the header. * @param detail The message to write to the response's body (may be {@code null}). * @throws NullPointerException if routing context is {@code null}. */ public static void serviceUnavailable(final RoutingContext ctx, final int retryAfterSeconds, final String detail) { final Map<CharSequence, CharSequence> headers = new HashMap<>(1); headers.put(HttpHeaders.RETRY_AFTER, String.valueOf(retryAfterSeconds)); failWithHeaders( ctx, new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, detail), headers); }
/** * Fails a routing context with HTTP status code 503 (Service Unavailable) and sets the <em>Retry-After</em> HTTP header * to a given number of seconds. * * @param ctx The vert.x routing context to fail. * @param retryAfterSeconds The number of seconds to set in the header. * @param detail The message to write to the response's body (may be {@code null}). * @throws NullPointerException if routing context is {@code null}. */ public static void serviceUnavailable(final RoutingContext ctx, final int retryAfterSeconds, final String detail) { final Map<CharSequence, CharSequence> headers = new HashMap<>(1); headers.put(HttpHeaders.RETRY_AFTER, String.valueOf(retryAfterSeconds)); failWithHeaders( ctx, new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, detail), headers); }
/** * Executes some code on the vert.x Context that has been used to establish the * connection to the peer. * * @param <T> The type of the result that the code produces. * @param codeToRun The code to execute. The code is required to either complete or * fail the future that is passed into the handler. * @return The future passed into the handler for executing the code. The future * thus indicates the outcome of executing the code. The future will * be failed with a {@link ServerErrorException} if the <em>context</em> * property is {@code null}. */ protected final <T> Future<T> executeOrRunOnContext(final Handler<Future<T>> codeToRun) { if (context == null) { // this means that the connection to the peer is not established (yet) return Future.failedFuture(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "not connected")); } else { return HonoProtonHelper.executeOrRunOnContext(context, codeToRun); } }
new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "no connection to service")); }; creationRequests.add(connectionFailureHandler); result.tryFail(new ServerErrorException( HttpURLConnection.HTTP_UNAVAILABLE, "no connection to service"));
private void createConsumer( final String tenantId, final Supplier<Future<MessageConsumer>> newConsumerSupplier, final Future<MessageConsumer> result) { // register a handler to be notified if the underlying connection to the server fails // so that we can fail the returned future final Handler<Void> connectionFailureHandler = connectionLost -> { result.tryFail( new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "connection to server lost")); }; creationRequests.add(connectionFailureHandler); newConsumerSupplier.get().setHandler(attempt -> { creationRequests.remove(connectionFailureHandler); if (attempt.succeeded()) { result.tryComplete(attempt.result()); } else { result.tryFail(attempt.cause()); } }); }
/** * Verifies that the credentials provided by a device during the authentication * process match the credentials on record for that device. * * @param deviceCredentials The credentials provided by the device. * @param credentialsOnRecord The credentials on record. * @return A future that is succeeded with the authenticated device if the * credentials have been validated successfully. Otherwise, the * future is failed with a {@link ServiceInvocationException}. */ private Future<Device> validateCredentials( final T deviceCredentials, final CredentialsObject credentialsOnRecord) { if (!deviceCredentials.getAuthId().equals(credentialsOnRecord.getAuthId())) { return Future.failedFuture(new ServerErrorException(HttpURLConnection.HTTP_INTERNAL_ERROR)); } else if (!deviceCredentials.getType().equals(credentialsOnRecord.getType())) { return Future.failedFuture(new ServerErrorException(HttpURLConnection.HTTP_INTERNAL_ERROR)); } else if (!credentialsOnRecord.isEnabled()) { return Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_UNAUTHORIZED)); } else { return doValidateCredentials(deviceCredentials, credentialsOnRecord); } }
/** * 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); } }
@Override @Deprecated public final Future<ProtonDelivery> send(final Message rawMessage, final Handler<Void> capacityAvailableHandler) { Objects.requireNonNull(rawMessage); return executeOrRunOnContext(result -> { if (capacityAvailableHandler == null) { final Span currentSpan = startSpan(rawMessage); sendMessage(rawMessage, currentSpan).setHandler(result.completer()); } else if (this.drainHandler != null) { result.fail(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "cannot send message while waiting for replenishment with credit")); } else if (sender.isOpen()) { final Span currentSpan = startSpan(rawMessage); sendMessage(rawMessage, currentSpan).setHandler(result.completer()); if (sender.sendQueueFull()) { sendQueueDrainHandler(capacityAvailableHandler); } else { capacityAvailableHandler.handle(null); } } else { result.fail(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "send link to peer is closed")); } }); }
private static void onTimeOut( final ProtonLink<?> link, final ClientConfigProperties clientConfig, final Future<?> result) { if (link.isOpen() && !HonoProtonHelper.isLinkEstablished(link)) { LOG.debug("link establishment timed out after {}ms", clientConfig.getLinkEstablishmentTimeout()); link.close(); link.free(); result.tryFail(new ServerErrorException(HttpsURLConnection.HTTP_UNAVAILABLE)); } }
/** * Verifies that the auth provider propagates the exception reported by a failed invocation * of the credentials service. * * @param ctx The vert.x test context. */ @Test public void testAuthenticateFailsWithExceptionReportedByCredentialsClient(final TestContext ctx) { final ServerErrorException reportedException = new ServerErrorException(503); when(credentialsClient.get(anyString(), anyString(), any(JsonObject.class), any())).thenReturn(Future.failedFuture(reportedException)); provider.authenticate(new JsonObject(), ctx.asyncAssertFailure(t -> { ctx.assertEquals(t, reportedException); })); }
/** * Creates an exception for an AMQP error condition. * * @param error The error condition. * @return The exception. * @throws NullPointerException if error is {@code null}. */ public static final ServiceInvocationException from(final ErrorCondition error) { Objects.requireNonNull(error); if (AmqpError.RESOURCE_LIMIT_EXCEEDED.equals(error.getCondition())) { return new ClientErrorException(HttpURLConnection.HTTP_FORBIDDEN, error.getDescription()); } else if (AmqpError.UNAUTHORIZED_ACCESS.equals(error.getCondition())) { return new ClientErrorException(HttpURLConnection.HTTP_FORBIDDEN, error.getDescription()); } else if (AmqpError.INTERNAL_ERROR.equals(error.getCondition())) { return new ServerErrorException(HttpURLConnection.HTTP_INTERNAL_ERROR, error.getDescription()); } else { return new ClientErrorException(HttpURLConnection.HTTP_NOT_FOUND, error.getDescription()); } } }
/** * {@inheritDoc} */ @Override public Future<Void> sendOneWayCommand(final String command, final String contentType, final Buffer data, final Map<String, Object> properties) { Objects.requireNonNull(command); final Span currentSpan = newChildSpan(null, command); if (sender.isOpen()) { final Future<BufferResult> responseTracker = Future.future(); final Message request = ProtonHelper.message(); AbstractHonoClient.setApplicationProperties(request, properties); final String messageId = createMessageId(); request.setMessageId(messageId); request.setSubject(command); MessageHelper.setPayload(request, contentType, data); sendRequest(request, responseTracker.completer(), null, currentSpan); return responseTracker.map(ignore -> null); } else { TracingHelper.logError(currentSpan, "sender link is not open"); return Future.failedFuture(new ServerErrorException( HttpURLConnection.HTTP_UNAVAILABLE, "sender link is not open")); } }
@Override public final Future<ProtonDelivery> send(final Message rawMessage, final SpanContext parent) { Objects.requireNonNull(rawMessage); if (!isRegistrationAssertionRequired()) { MessageHelper.getAndRemoveRegistrationAssertion(rawMessage); } final Span span = startSpan(parent, rawMessage); Tags.MESSAGE_BUS_DESTINATION.set(span, targetAddress); span.setTag(MessageHelper.APP_PROPERTY_TENANT_ID, tenantId); span.setTag(MessageHelper.APP_PROPERTY_DEVICE_ID, MessageHelper.getDeviceId(rawMessage)); tracer.inject(span.context(), Format.Builtin.TEXT_MAP, new MessageAnnotationsInjectAdapter(rawMessage)); return executeOrRunOnContext(result -> { if (sender.sendQueueFull()) { final ServiceInvocationException e = new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "no credit available"); logError(span, e); span.finish(); result.fail(e); } else { sendMessage(rawMessage, span).setHandler(result.completer()); } }); }
/** * {@inheritDoc} */ @Override public Future<ProtonDelivery> sendAndWaitForOutcome(final Message rawMessage, final SpanContext parent) { Objects.requireNonNull(rawMessage); // we create a child span (instead of a following span) because we depend // on the outcome of the sending operation final Span span = startChildSpan(parent, rawMessage); Tags.MESSAGE_BUS_DESTINATION.set(span, targetAddress); span.setTag(MessageHelper.APP_PROPERTY_TENANT_ID, tenantId); span.setTag(MessageHelper.APP_PROPERTY_DEVICE_ID, MessageHelper.getDeviceId(rawMessage)); tracer.inject(span.context(), Format.Builtin.TEXT_MAP, new MessageAnnotationsInjectAdapter(rawMessage)); if (!isRegistrationAssertionRequired()) { MessageHelper.getAndRemoveRegistrationAssertion(rawMessage); } return executeOrRunOnContext(result -> { if (sender.sendQueueFull()) { final ServiceInvocationException e = new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "no credit available"); logError(span, e); span.finish(); result.fail(e); } else { sendMessageAndWaitForOutcome(rawMessage, span).setHandler(result.completer()); } }); }
private Future<MessageConsumer> openCommandSenderLink( final ProtonSender sender, final ResourceIdentifier address, final Device authenticatedDevice, final Span span) { return createCommandConsumer(sender, address).map(consumer -> { final String tenantId = address.getTenantId(); final String deviceId = address.getResourceId(); sender.setSource(sender.getRemoteSource()); sender.setTarget(sender.getRemoteTarget()); sender.setQoS(ProtonQoS.AT_LEAST_ONCE); final Handler<AsyncResult<ProtonSender>> detachHandler = link -> { final Span detachHandlerSpan = newSpan("detach Command receiver", authenticatedDevice); sendDisconnectedTtdEvent(tenantId, deviceId, authenticatedDevice, detachHandlerSpan.context()); consumer.close(null); onLinkDetach(sender); detachHandlerSpan.finish(); }; HonoProtonHelper.setCloseHandler(sender, detachHandler); HonoProtonHelper.setDetachHandler(sender, detachHandler); sender.open(); // At this point, the remote peer's receiver link is successfully opened and is ready to receive // commands. Send "device ready for command" notification downstream. LOG.debug("established link [address: {}] for sending commands to device", address); sendConnectedTtdEvent(tenantId, deviceId, authenticatedDevice, span.context()); return consumer; }).otherwise(t -> { throw new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "cannot create command consumer"); }); }