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

org.honton.chas.dogstatd.model.Sender Maven / Gradle / Ivy

package org.honton.chas.dogstatd.model;

import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ProtocolFamily;
import java.net.StandardProtocolFamily;
import java.net.StandardSocketOptions;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.UnsupportedAddressTypeException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

/**
 * Send formatted UDP messages to a DogStatD. 
 * Messages are added to a queue and sent in a background thread.
 * If message cannot be sent to local DogStatD agent, drop any additional messages within the
 * throttleInterval.
 */
@Slf4j
public class Sender {

  private static final int MTU = 1440;

  private static final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1);

  private final DatagramChannel clientChannel;
  private volatile long throttleEnd;
  private long throttleInterval;

  /**
   * Create a sender which sends to a DogStatD on port 8125 at the local loopback
   * address.  ThrottleInterval is one minute.
   */
  public Sender() {
    this(InetAddress.getLoopbackAddress());
  }

  /**
   * Create a sender which sends to a DogStatD on port 8125 at the specified 
   * address.  ThrottleInterval is one minute.
   *
   * @param address The address to send to.
   */
  public Sender(String address) {
    this(getAddress(address));
  }

  @SneakyThrows
  private static InetAddress getAddress(String address) {
    return InetAddress.getByName(address);
  }

  /**
   * Create a sender which sends to a DogStatD on port 8125 at the specified 
   * address.  ThrottleInterval is one minute.
   *
   * @param address The address to send to.
   */
  public Sender(InetAddress address) {
    this(new InetSocketAddress(address, 8125));
  }

  /**
   * Create a sender which sends to a DogStatD on the specified port and address.
   * ThrottleInterval is one minute.
   * @param socket The address and port to send to.
   */
  public Sender(InetSocketAddress socket) {
    this(socket, ONE_MINUTE);
  }

  /**
   * Create a sender which sends to a DogStatD on the specified port and address.
   * ThrottleInterval is specified in milliseconds.  If a message fails to be sent to the
   * local agent, any messages during the throttleInterval will be dropped.  Additional
   * messages will be attempted once the throttleInterval has expired.
   *
   * @param socket The address and port to send to.
   * @param throttleInterval The number of milliseconds to ignore messages.
   */
  @SneakyThrows
  public Sender(InetSocketAddress socket, long throttleInterval) {
    clientChannel = DatagramChannel.open(getProtocol(socket.getAddress()));
    clientChannel.configureBlocking(false);
    clientChannel.setOption(StandardSocketOptions.SO_SNDBUF, MTU);
    clientChannel.connect(socket);
    this.throttleInterval = throttleInterval;
  }

  private ProtocolFamily getProtocol(InetAddress host) {
    if (host instanceof Inet4Address) {
      return StandardProtocolFamily.INET;
    }
    if (host instanceof Inet6Address) {
      return StandardProtocolFamily.INET6;
    }
    throw new UnsupportedAddressTypeException();
  }

  /**
   * Send message to collector daemon.  Messages are checked for validity and will be dropped if too
   * large or contain invalid values.  Message will also be dropped if this method is invoked within
   * the throttleInterval.
   *
   * @param message The message to send.
   * @return true, if message is valid and successfully queued for delivery; false, if message not
   * sent due to improper message formatting, delivery failure, or the throttleInterval is still active.
   */
  public boolean send(Message message) {
    if (throttleEnd != 0) {
      if (System.currentTimeMillis() < throttleEnd) {
        return false;
      }
      throttleEnd = 0;
    }

    if (!message.validate()) {
      return false;
    }

    CharBuffer chars = CharBuffer.allocate(1400);
    chars.clear();
    try {
      message.format(chars);
    }
    catch(BufferOverflowException boe) {
      log.info("too large of message {}", message);
      return false;
    }

    chars.flip();
    ByteBuffer bytes = StandardCharsets.UTF_8.encode(chars);

    try {
      clientChannel.write(bytes);
      return true;
    } catch (IOException pue) {
      throttleEnd = System.currentTimeMillis() + throttleInterval;
    }
    log.warn("failed to send message {}", message);
    return false;
  }

  void shutdown() throws IOException {
    clientChannel.close();
  }

  boolean isThrottled() {
    return throttleEnd != 0;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy