@Override public void close() { // close Kafka related stuff super.close(); this.receivers.forEach((name, receiver) -> { receiver.close(); }); this.receivers.clear(); }
@Override public void cancel() { LOG.trace("Cancel called"); if(!cancelled.getAndSet(true)) { LOG.trace("Cancellation scheduled"); connCtx.runOnContext(x -> { LOG.trace("Cancelling"); receiver.closeHandler(y -> { indicateCompletion(); receiver.close(); }); receiver.detachHandler(y -> { indicateCompletion(); receiver.detach(); }); if(durable) { receiver.detach(); } else { receiver.close(); } }); } else { LOG.trace("Cancel no-op, already called."); } }
private void onLinkDetach(final ProtonReceiver client, final ErrorCondition condition) { logger.debug("closing receiver for client [{}]", getLinkName(client)); client.close(); }
private void receiverHandler(ProtonReceiver receiver) { LOG.info("Attaching link request"); // the LWT service supports only the control address if (!receiver.getRemoteTarget().getAddress().equals(LWT_SERVICE_ENDPOINT)) { ErrorCondition errorCondition = new ErrorCondition(AmqpError.NOT_FOUND, "The provided address isn't supported"); receiver.setCondition(errorCondition) .close(); } else { receiver.setTarget(receiver.getRemoteTarget()) .setQoS(ProtonQoS.AT_LEAST_ONCE) .handler((delivery, message) -> { this.messageHandler(receiver, delivery, message); }) .closeHandler(ar -> { this.closeHandler(receiver, ar); }) .detachHandler(ar -> { this.closeHandler(receiver, ar); }) .setPrefetch(0) .open(); receiver.flow(AMQP_WILL_CREDITS); } }
@Override public void close(final ErrorCondition error) { if (error != null) { link.setCondition(error); } link.close(); }
@Override protected void handleReceiverOpen(final ProtonConnection con, final ProtonReceiver receiver) { receiver.setCondition(ProtonHelper.condition(AmqpError.NOT_ALLOWED, "cannot write to node")); receiver.close(); }
/** * Handle for detached link by the remote sender * @param receiver Proton receiver instance */ private void processCloseReceiver(ProtonReceiver receiver) { log.info("Remote AMQP sender detached"); // close and remove the receiver link receiver.close(); this.receivers.remove(receiver.getName()); // if the source endpoint has no receiver links, it can be closed if (this.receivers.isEmpty()) { this.close(); this.handleClose(); } } }
@Override public void onLinkAttach(final ProtonConnection con, final ProtonReceiver receiver, final ResourceIdentifier targetResource) { logger.info("Endpoint [{}] does not support data upload, closing link.", getName()); receiver.setCondition(ProtonHelper.condition(AmqpError.NOT_IMPLEMENTED, "resource cannot be written to")); receiver.close(); }
@Override public void onLinkAttach(final ProtonConnection con, final ProtonReceiver receiver, final ResourceIdentifier targetResource) { logger.info("Endpoint [{}] does not support data upload, closing link.", getName()); receiver.setCondition(ProtonHelper.condition(AmqpError.NOT_IMPLEMENTED, "resource cannot be written to")); receiver.close(); }
@Override public void onLinkAttach(final ProtonReceiver receiver, final ResourceIdentifier targetResource) { logger.info("Endpoint [{}] does not support data upload, closing link.", getName()); receiver.setCondition(ProtonHelper.condition(AmqpError.NOT_IMPLEMENTED, "resource cannot be written to")); receiver.close(); }
private void closeHandler(ProtonReceiver receiver, AsyncResult<ProtonReceiver> ar) { // link detached without error, so the "will" should be cleared and not sent if (ar.succeeded()) { LOG.info("Clean disconnection from {}", receiver.getName()); // TODO: for now nothing to do ? // link detached with error, so the "will" should be sent } else { LOG.info("Brute disconnection from {}", receiver.getName()); ErrorCondition errorCondition = new ErrorCondition(receiver.getRemoteCondition().getCondition(), String.format("client detached with: %s", receiver.getRemoteCondition().getDescription())); receiver.setCondition(errorCondition); } receiver.close(); if (this.disconnectionHandler != null) { this.disconnectionHandler.handle(new DisconnectionData(receiver.getName(), ar.failed())); } }
/** * Verifies that the endpoint opens a receiver under normal circumstances. */ @Test public void testOnLinkAttachOpensReceiver() { final RequestResponseEndpoint<ServiceConfigProperties> endpoint = getEndpoint(true); when(receiver.getRemoteQoS()).thenReturn(ProtonQoS.AT_LEAST_ONCE); endpoint.onLinkAttach(connection, receiver, resource); verify(receiver).handler(any(ProtonMessageHandler.class)); verify(receiver).open(); verify(receiver, never()).close(); }
/** * Verifies that the endpoint closes a receiver that wants to use <em>at-most-once</em> * delivery semantics. */ @Test public void testOnLinkAttachClosesReceiverUsingAtMostOnceQoS() { final RequestResponseEndpoint<ServiceConfigProperties> endpoint = getEndpoint(true); when(receiver.getRemoteQoS()).thenReturn(ProtonQoS.AT_MOST_ONCE); endpoint.onLinkAttach(connection, receiver, resource); verify(receiver).close(); }
private void messageHandler(ProtonReceiver receiver, ProtonDelivery delivery, Message message) { try { AmqpWillMessage amqpWillMessage = AmqpWillMessage.from(message); LOG.info("Received will on topic [{}] by client [{}]", amqpWillMessage.topic(), receiver.getName()); // TODO : having a callback to check if handling went well and send right disposition ? this.willHandler.handle(new WillData(receiver.getName(), amqpWillMessage)); delivery.disposition(Accepted.getInstance(), true); // NOTE : after receiving the AMQP_WILL, a new credit is issued because // with AMQP we want to change the "will" message during the client life receiver.flow(AMQP_WILL_CREDITS); } catch (IllegalArgumentException ex) { LOG.error("Error decoding will message", ex); ErrorCondition errorCondition = new ErrorCondition(AmqpError.DECODE_ERROR, "Received message is not a will message"); receiver.setCondition(errorCondition) .close(); } }
/** * Verifies that the endpoint rejects malformed request messages. */ @Test public void testHandleMessageRejectsMalformedMessage() { final Message msg = ProtonHelper.message(); final ProtonConnection con = mock(ProtonConnection.class); final ProtonDelivery delivery = mock(ProtonDelivery.class); final RequestResponseEndpoint<ServiceConfigProperties> endpoint = getEndpoint(false); // WHEN a malformed message is received endpoint.handleMessage(con, receiver, resource, delivery, msg); // THEN the link is closed and the message is rejected final ArgumentCaptor<DeliveryState> deliveryState = ArgumentCaptor.forClass(DeliveryState.class); verify(delivery).disposition(deliveryState.capture(), booleanThat(is(Boolean.TRUE))); assertThat(deliveryState.getValue(), instanceOf(Rejected.class)); verify(receiver, never()).close(); }
/** * Verifies that the service rejects sender links on resources that * the client is not authorized to write to. */ @Test public void testHandleReceiverOpenRejectsUnauthorizedClient() { // GIVEN a server with a endpoint final ResourceIdentifier restrictedTargetAddress = ResourceIdentifier.from(ENDPOINT, "RESTRICTED_TENANT", null); final AmqpEndpoint endpoint = mock(AmqpEndpoint.class); when(endpoint.getName()).thenReturn(ENDPOINT); final AuthorizationService authService = mock(AuthorizationService.class); when(authService.isAuthorized(Constants.PRINCIPAL_ANONYMOUS, restrictedTargetAddress, Activity.WRITE)) .thenReturn(Future.succeededFuture(Boolean.FALSE)); final AmqpServiceBase<ServiceConfigProperties> server = createServer(endpoint); server.setAuthorizationService(authService); // WHEN a client connects to the server using a address for a tenant it is not authorized to write to final Target target = getTarget(restrictedTargetAddress); final ProtonReceiver receiver = mock(ProtonReceiver.class); when(receiver.getRemoteTarget()).thenReturn(target); when(receiver.setCondition(any())).thenReturn(receiver); server.handleReceiverOpen(newConnection(Constants.PRINCIPAL_ANONYMOUS), receiver); // THEN the server closes the link with the client verify(receiver).close(); }
/** * Verifies that the endpoint processes request messages for operations the client * is authorized to invoke. */ @Test public void testHandleMessageProcessesAuthorizedRequests() { final Message msg = ProtonHelper.message(); msg.setSubject("get"); msg.setReplyTo(REPLY_RESOURCE.toString()); final ProtonConnection con = mock(ProtonConnection.class); final ProtonDelivery delivery = mock(ProtonDelivery.class); final AuthorizationService authService = mock(AuthorizationService.class); when(authService.isAuthorized(any(HonoUser.class), any(ResourceIdentifier.class), anyString())).thenReturn(Future.succeededFuture(Boolean.TRUE)); final Future<Void> processingTracker = Future.future(); final RequestResponseEndpoint<ServiceConfigProperties> endpoint = getEndpoint(true, processingTracker); endpoint.setAuthorizationService(authService); endpoint.onLinkAttach(con, sender, REPLY_RESOURCE); // WHEN a request for an operation is received that the client is authorized to invoke endpoint.handleMessage(con, receiver, resource, delivery, msg); // THEN then the message gets processed final ArgumentCaptor<DeliveryState> deliveryState = ArgumentCaptor.forClass(DeliveryState.class); verify(delivery).disposition(deliveryState.capture(), booleanThat(is(Boolean.TRUE))); assertThat(deliveryState.getValue(), instanceOf(Accepted.class)); verify(receiver, never()).close(); verify(authService).isAuthorized(Constants.PRINCIPAL_ANONYMOUS, resource, "get"); assertTrue(processingTracker.isComplete()); }
/** * Verifies that the endpoint rejects request messages for operations the client * is not authorized to invoke. */ @Test public void testHandleMessageRejectsUnauthorizedRequests() { final Message msg = ProtonHelper.message(); msg.setSubject("unauthorized"); msg.setReplyTo(REPLY_RESOURCE.toString()); final ProtonConnection con = mock(ProtonConnection.class); final ProtonDelivery delivery = mock(ProtonDelivery.class); final AuthorizationService authService = mock(AuthorizationService.class); when(authService.isAuthorized(any(HonoUser.class), any(ResourceIdentifier.class), anyString())).thenReturn(Future.succeededFuture(Boolean.FALSE)); final Future<Void> processingTracker = Future.future(); final RequestResponseEndpoint<ServiceConfigProperties> endpoint = getEndpoint(true, processingTracker); endpoint.setAuthorizationService(authService); endpoint.onLinkAttach(con, sender, REPLY_RESOURCE); // WHEN a request for an operation is received that the client is not authorized to invoke endpoint.handleMessage(con, receiver, resource, delivery, msg); // THEN the the message is rejected final ArgumentCaptor<DeliveryState> deliveryState = ArgumentCaptor.forClass(DeliveryState.class); verify(delivery).disposition(deliveryState.capture(), booleanThat(is(Boolean.TRUE))); assertThat(deliveryState.getValue(), instanceOf(Rejected.class)); verify(receiver, never()).close(); verify(authService).isAuthorized(Constants.PRINCIPAL_ANONYMOUS, resource, "unauthorized"); assertFalse(processingTracker.isComplete()); }