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

zipkin2.collector.activemq.ActiveMQSpanConsumer Maven / Gradle / Ivy

/*
 * Copyright The OpenZipkin Authors
 * SPDX-License-Identifier: Apache-2.0
 */
package zipkin2.collector.activemq;

import java.io.Closeable;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.QueueReceiver;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.transport.TransportListener;
import zipkin2.Callback;
import zipkin2.CheckResult;
import zipkin2.collector.Collector;
import zipkin2.collector.CollectorMetrics;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * Consumes spans from messages on a ActiveMQ queue. Malformed messages will be discarded. Errors in
 * the storage component will similarly be ignored, with no retry of the message.
 */
final class ActiveMQSpanConsumer implements TransportListener, MessageListener, Closeable {
  static final Callback NOOP = new Callback() {
    @Override public void onSuccess(Void value) {
    }

    @Override public void onError(Throwable t) {
    }
  };

  static final CheckResult
    CLOSED = CheckResult.failed(new IllegalStateException("Collector intentionally closed")),
    INTERRUPTION = CheckResult.failed(new IOException("Recoverable error on ActiveMQ connection"));

  final Collector collector;
  final CollectorMetrics metrics;

  final ActiveMQConnection connection;
  final Map sessionToReceiver = new LinkedHashMap<>();

  volatile CheckResult checkResult = CheckResult.OK;

  ActiveMQSpanConsumer(Collector collector, CollectorMetrics metrics, ActiveMQConnection conn) {
    this.collector = collector;
    this.metrics = metrics;
    this.connection = conn;
    connection.addTransportListener(this);
  }

  /** JMS contract is one session per thread: we need a new session up to our concurrency level. */
  void registerInNewSession(ActiveMQConnection connection, String queue) throws JMSException {
    // Pass redundant info as we can't use default method in activeMQ
    QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
    // No need to do anything on ActiveMQ side as physical queues are created on demand
    Queue destination = session.createQueue(queue);
    QueueReceiver receiver = session.createReceiver(destination);
    receiver.setMessageListener(this);
    sessionToReceiver.put(session, receiver);
  }

  @Override public void onCommand(Object o) {
  }

  @Override public void onException(IOException error) {
    checkResult = CheckResult.failed(error);
  }

  @Override public void transportInterupted() {
    checkResult = INTERRUPTION;
  }

  @Override public void transportResumed() {
    checkResult = CheckResult.OK;
  }

  @Override public void onMessage(Message message) {
    metrics.incrementMessages();
    byte[] serialized; // TODO: consider how to reuse buffers here
    try {
      if (message instanceof BytesMessage bytesMessage) {
        serialized = new byte[(int) bytesMessage.getBodyLength()];
        bytesMessage.readBytes(serialized);
      } else if (message instanceof TextMessage textMessage) {
        String text = textMessage.getText();
        serialized = text.getBytes(UTF_8);
      } else {
        metrics.incrementMessagesDropped();
        return;
      }
    } catch (Exception e) {
      metrics.incrementMessagesDropped();
      return;
    }

    metrics.incrementBytes(serialized.length);
    if (serialized.length == 0) return; // lenient on empty messages
    collector.acceptSpans(serialized, NOOP);
  }

  @Override public void close() {
    if (checkResult == CLOSED) return;
    checkResult = CLOSED;
    connection.removeTransportListener(this);
    try {
      for (Map.Entry sessionReceiver : sessionToReceiver.entrySet()) {
        sessionReceiver.getValue().setMessageListener(null); // deregister this
        sessionReceiver.getKey().close();
      }
      connection.close();
    } catch (JMSException ignored) {
      // EmptyCatch ignored
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy