All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.camunda.connector.rabbitmq.inbound.RabbitMqExecutable Maven / Gradle / Ivy

The newest version!
/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
 * under one or more contributor license agreements. Licensed under a proprietary license.
 * See the License.txt file for more information. You may not use this file
 * except in compliance with the proprietary license.
 */
package io.camunda.connector.rabbitmq.inbound;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.Recoverable;
import com.rabbitmq.client.RecoveryListener;
import io.camunda.connector.api.annotation.InboundConnector;
import io.camunda.connector.api.inbound.Activity;
import io.camunda.connector.api.inbound.Health;
import io.camunda.connector.api.inbound.InboundConnectorContext;
import io.camunda.connector.api.inbound.InboundConnectorExecutable;
import io.camunda.connector.api.inbound.Severity;
import io.camunda.connector.generator.dsl.BpmnType;
import io.camunda.connector.generator.java.annotation.ElementTemplate;
import io.camunda.connector.rabbitmq.inbound.model.RabbitMqInboundProperties;
import io.camunda.connector.rabbitmq.supplier.ConnectionFactorySupplier;
import java.io.IOException;
import java.util.HashMap;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@InboundConnector(name = "RabbitMQ Consumer", type = "io.camunda:connector-rabbitmq-inbound:1")
@ElementTemplate(
    id = "io.camunda.connectors.inbound.RabbitMQ",
    name = "RabbitMQ Connector",
    icon = "icon.svg",
    version = 7,
    inputDataClass = RabbitMqInboundProperties.class,
    description = "Receive a message from RabbitMQ",
    documentationRef =
        "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/rabbitmq/?rabbitmq=inbound",
    propertyGroups = {
      @ElementTemplate.PropertyGroup(id = "authentication", label = "Authentication"),
      @ElementTemplate.PropertyGroup(id = "routing", label = "Routing"),
      @ElementTemplate.PropertyGroup(id = "subscription", label = "Subscription"),
    },
    elementTypes = {
      @ElementTemplate.ConnectorElementType(
          appliesTo = BpmnType.START_EVENT,
          elementType = BpmnType.MESSAGE_START_EVENT,
          templateIdOverride = "io.camunda.connectors.inbound.RabbitMQ.MessageStart.v1",
          templateNameOverride = "RabbitMQ Message Start Event Connector"),
      @ElementTemplate.ConnectorElementType(
          appliesTo = {BpmnType.INTERMEDIATE_THROW_EVENT, BpmnType.INTERMEDIATE_CATCH_EVENT},
          elementType = BpmnType.INTERMEDIATE_CATCH_EVENT,
          templateIdOverride = "io.camunda.connectors.inbound.RabbitMQ.Intermediate.v1",
          templateNameOverride = "RabbitMQ Intermediate Catch Event Connector"),
      @ElementTemplate.ConnectorElementType(
          appliesTo = BpmnType.BOUNDARY_EVENT,
          elementType = BpmnType.BOUNDARY_EVENT,
          templateIdOverride = "io.camunda.connectors.inbound.RabbitMQ.Boundary.v1",
          templateNameOverride = "RabbitMQ Boundary Event Connector")
    })
public class RabbitMqExecutable implements InboundConnectorExecutable {

  private static final Logger LOGGER = LoggerFactory.getLogger(RabbitMqExecutable.class);
  private static final int CLOSE_TIMEOUT_MILLIS = 3000;
  private final ConnectionFactorySupplier connectionFactorySupplier;
  private Connection connection;
  private Channel channel;
  private String consumerTag; // either provided in properties or generated by RabbitMQ server

  public RabbitMqExecutable() {
    this.connectionFactorySupplier = new ConnectionFactorySupplier();
  }

  public RabbitMqExecutable(final ConnectionFactorySupplier connectionFactorySupplier) {
    this.connectionFactorySupplier = connectionFactorySupplier;
  }

  @Override
  public void activate(InboundConnectorContext context) throws Exception {
    RabbitMqInboundProperties properties = context.bindProperties(RabbitMqInboundProperties.class);

    LOGGER.info("Subscription activation requested by the Connector runtime: {}", properties);
    context.log(
        Activity.level(Severity.INFO)
            .tag("Subscription activation")
            .message(
                "Subscription activation requested for queue name :" + properties.getQueueName()));

    initializeConsumer(context, properties);
  }

  @Override
  public void deactivate() throws Exception {
    LOGGER.info("Subscription deactivation requested by the Connector runtime");
    try {
      channel.basicCancel(consumerTag);
    } catch (Exception e) {
      LOGGER.warn("Failed to cancel consumer", e);
    } finally {
      if (connection != null) {
        connection.close(CLOSE_TIMEOUT_MILLIS);
      }
    }
  }

  void initializeConsumer(InboundConnectorContext context, RabbitMqInboundProperties properties)
      throws Exception {

    connection = openConnection(properties);

    if (connection instanceof Recoverable recoverable) {
      final var recoveryListener =
          new RecoveryListener() {
            @Override
            public void handleRecovery(Recoverable recoverable) {
              LOGGER.info("Connection recovered successfully: {}", recoverable);
              context.log(
                  Activity.level(Severity.INFO)
                      .tag("Connection recovery")
                      .message("Connection recovered successfully: " + recoverable));
              context.reportHealth(Health.up());
            }

            @Override
            public void handleRecoveryStarted(Recoverable recoverable) {
              LOGGER.info("Connection recovery started: {}", recoverable);
              context.log(
                  Activity.level(Severity.INFO)
                      .tag("Connection recovery")
                      .message("Connection recovery started: " + recoverable));
              context.reportHealth(Health.down());
            }
          };
      recoverable.addRecoveryListener(recoveryListener);
    }

    channel = connection.createChannel();
    Consumer consumer = new RabbitMqConsumer(channel, context);

    var data = new HashMap();
    data.put("connection-id", connection.getId());
    data.put("connection-name", connection.getClientProvidedName());
    data.put("connection-address", connection.getAddress());
    data.put("connection-port", connection.getPort());
    context.reportHealth(Health.up(data));

    consumerTag = startConsumer(properties, consumer);
    LOGGER.info("Started RabbitMQ consumer for queue {}", properties.getQueueName());
    context.log(
        Activity.level(Severity.INFO)
            .tag("Subscription activation")
            .message("Activated subscription for queue: " + properties.getQueueName()));
    context.reportHealth(Health.up());
  }

  Connection openConnection(RabbitMqInboundProperties properties) throws Exception {
    return connectionFactorySupplier
        .createFactory(properties.getAuthentication(), properties.getRouting())
        .newConnection();
  }

  private String startConsumer(RabbitMqInboundProperties properties, Consumer consumer)
      throws IOException {

    if (StringUtils.isBlank(properties.getConsumerTag())) {
      // generate a random consumer tag if it wasn't provided
      return channel.basicConsume(
          properties.getQueueName(),
          false,
          UUID.randomUUID().toString(),
          false,
          properties.isExclusive(),
          properties.getArguments(),
          consumer);
    }

    return channel.basicConsume(
        properties.getQueueName(),
        false,
        properties.getConsumerTag(),
        false,
        properties.isExclusive(),
        properties.getArguments(),
        consumer);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy