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

com.rabbitmq.client.amqp.impl.AmqpQueueSpecification Maven / Gradle / Ivy

Go to download

The RabbitMQ AMQP 1.0 Java client library defines an API to access RabbitMQ with the AMQP 1.0 protocol.

The newest version!
// Copyright (c) 2024 Broadcom. All Rights Reserved.
// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// If you have any questions regarding licensing, please contact us at
// [email protected].
package com.rabbitmq.client.amqp.impl;

import static com.rabbitmq.client.amqp.Management.QueueLeaderLocator.BALANCED;
import static java.lang.String.format;

import com.rabbitmq.client.amqp.ByteCapacity;
import com.rabbitmq.client.amqp.Management;
import com.rabbitmq.client.amqp.Management.QueueType;
import java.time.Duration;
import java.util.*;
import java.util.function.BiConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class AmqpQueueSpecification implements Management.QueueSpecification {

  private static final Logger LOGGER = LoggerFactory.getLogger(AmqpQueueSpecification.class);
  private static final Duration TEN_YEARS = Duration.ofDays(365 * 10);
  private static final boolean DURABLE = true;

  private final AmqpManagement management;

  // flag to detect no "shortcut" has been called and that no validation should occur
  // this would allow to declare a queue with the plain arguments, and without
  // validation on the client side, in case the validation blocked a given combination
  // of parameters (that would be considered a bug, but this would at least unlock
  // the user until the fix arrives)
  private boolean shortcutArguments = false;
  private String name;
  private boolean exclusive = false;
  private boolean autoDelete = false;
  private final Map arguments = new LinkedHashMap<>();

  AmqpQueueSpecification(AmqpManagement management) {
    this.management = management;
  }

  @Override
  public Management.QueueSpecification name(String name) {
    this.name = name;
    return this;
  }

  @Override
  public Management.QueueSpecification exclusive(boolean exclusive) {
    this.exclusive = exclusive;
    return this;
  }

  @Override
  public Management.QueueSpecification autoDelete(boolean autoDelete) {
    this.autoDelete = autoDelete;
    return this;
  }

  @Override
  public Management.QueueSpecification type(QueueType type) {
    if (type == QueueType.QUORUM || type == QueueType.STREAM) {
      this.exclusive(false).autoDelete(false);
    }
    this.arg("x-queue-type", type.name().toLowerCase(Locale.ENGLISH));
    return this;
  }

  @Override
  public Management.QueueSpecification deadLetterExchange(String dlx) {
    this.arg("x-dead-letter-exchange", dlx);
    return this;
  }

  @Override
  public Management.QueueSpecification deadLetterRoutingKey(String dlrk) {
    this.arg("x-dead-letter-routing-key", dlrk);
    return this;
  }

  @Override
  public Management.QueueSpecification overflowStrategy(String overflow) {
    this.arg("x-overflow", overflow);
    return this;
  }

  @Override
  public Management.QueueSpecification overflowStrategy(Management.OverFlowStrategy overflow) {
    this.arg("x-overflow", overflow.strategy());
    return this;
  }

  @Override
  public Management.QueueSpecification expires(Duration expiration) {
    validatePositive("x-expires", expiration.toMillis(), TEN_YEARS.toMillis());
    this.arg("x-expires", expiration.toMillis());
    return this;
  }

  @Override
  public Management.QueueSpecification maxLength(long maxLength) {
    validatePositive("x-max-length", maxLength);
    this.arg("x-max-length", maxLength);
    return this;
  }

  @Override
  public Management.QueueSpecification maxLengthBytes(ByteCapacity maxLengthBytes) {
    validatePositive("x-max-length-bytes", maxLengthBytes.toBytes());
    this.arg("x-max-length-bytes", maxLengthBytes.toBytes());
    return this;
  }

  @Override
  public Management.QueueSpecification singleActiveConsumer(boolean singleActiveConsumer) {
    this.arg("x-single-active-consumer", singleActiveConsumer);
    return this;
  }

  @Override
  public Management.QueueSpecification messageTtl(Duration ttl) {
    validateNonNegative("x-message-ttl", ttl.toMillis(), TEN_YEARS.toMillis());
    this.arg("x-message-ttl", ttl.toMillis());
    return this;
  }

  @Override
  public Management.QueueSpecification leaderLocator(Management.QueueLeaderLocator locator) {
    if (locator == null) {
      arg("x-queue-master-locator", null);
      arg("x-queue-leader-locator", null);
    } else {
      QueueType type = type();
      if (type == QueueType.CLASSIC) {
        arg("x-queue-master-locator", locator == BALANCED ? "min-masters" : locator.locator());
      } else if (type == QueueType.QUORUM || type == QueueType.STREAM) {
        arg("x-queue-leader-locator", locator.locator());
      } else {
        LOGGER.warn("Set queue type before setting leader locator");
      }
    }
    return this;
  }

  @Override
  public Management.QuorumQueueSpecification quorum() {
    this.type(QueueType.QUORUM);
    return new AmqpQuorumQueueSpecification(this);
  }

  @Override
  public Management.ClassicQueueSpecification classic() {
    this.type(QueueType.CLASSIC);
    return new AmqpClassicQueueSpecification(this);
  }

  @Override
  public Management.StreamSpecification stream() {
    this.type(QueueType.STREAM);
    return new AmqpStreamSpecification(this);
  }

  @Override
  public Management.QueueSpecification argument(String key, Object value) {
    if (value == null) {
      this.arguments.remove(key);
    } else {
      this.arguments.put(key, value);
    }
    return this;
  }

  @Override
  public Management.QueueInfo declare() {
    Map body = new LinkedHashMap<>();
    body.put("durable", DURABLE);
    body.put("exclusive", this.exclusive);
    body.put("auto_delete", this.autoDelete);
    body.put("arguments", this.arguments);
    Management.QueueInfo info = this.management.declareQueue(this.name, body);
    AmqpQueueSpecification copy = this.duplicate();
    copy.name(info.name());
    this.management.recovery().queueDeclared(copy);
    return info;
  }

  private Map arg(String key, Object value) {
    if (value == null) {
      this.arguments.remove(key);
    } else {
      this.arguments.put(key, value);
    }
    this.shortcutArguments = true;
    return this.arguments;
  }

  private static void validatePositive(String label, long value) {
    validatePositive(label, value, 0);
  }

  private static void validatePositive(String label, long value, long max) {
    if (value <= 0) {
      throw new IllegalArgumentException(format("'%s' must be positive", label));
    }
    if (max > 0) {
      if (value > max) {
        throw new IllegalArgumentException(format("'%s' must be lesser than %d", label, max));
      }
    }
  }

  private static void validateNonNegative(String label, long value, long max) {
    if (value < 0) {
      throw new IllegalArgumentException(format("'%s' must be greater than or equal to 0", label));
    }
    if (max > 0) {
      if (value > max) {
        throw new IllegalArgumentException(format("'%s' must be lesser than %d", label, max));
      }
    }
  }

  QueueType type() {
    String type = (String) this.arguments.get("x-queue-type");
    for (QueueType value : QueueType.values()) {
      if (value.toString().toLowerCase(Locale.ENGLISH).equals(type)) {
        return value;
      }
    }
    return null;
  }

  private static class AmqpQuorumQueueSpecification implements Management.QuorumQueueSpecification {

    private final AmqpQueueSpecification parent;

    private AmqpQuorumQueueSpecification(AmqpQueueSpecification parent) {
      this.parent = parent;
    }

    @Override
    public Management.QuorumQueueSpecification deadLetterStrategy(String strategy) {
      this.parent.arg("x-dead-letter-strategy", strategy);
      return this;
    }

    @Override
    public Management.QuorumQueueSpecification deadLetterStrategy(
        Management.QuorumQueueDeadLetterStrategy strategy) {
      this.parent.arg("x-dead-letter-strategy", strategy.strategy());
      return this;
    }

    @Override
    public Management.QuorumQueueSpecification deliveryLimit(int limit) {
      validatePositive("x-max-delivery-limit", limit);
      this.parent.arg("x-max-delivery-limit", limit);
      return this;
    }

    @Override
    public Management.QuorumQueueSpecification quorumInitialGroupSize(int size) {
      validatePositive("x-quorum-initial-group-size", size);
      this.parent.arg("x-quorum-initial-group-size", size);
      return this;
    }

    @Override
    public Management.QueueSpecification queue() {
      return parent;
    }
  }

  private static class AmqpClassicQueueSpecification
      implements Management.ClassicQueueSpecification {

    private final AmqpQueueSpecification parent;

    private AmqpClassicQueueSpecification(AmqpQueueSpecification parent) {
      this.parent = parent;
    }

    @Override
    public Management.ClassicQueueSpecification maxPriority(int maxPriority) {
      validateNonNegative("x-max-priority", maxPriority, 256);
      this.parent.arg("x-max-priority", maxPriority);
      return this;
    }

    @Override
    public Management.ClassicQueueSpecification version(Management.ClassicQueueVersion version) {
      if (version == null) {
        this.parent.arg("x-queue-version", null);
      } else {
        this.parent.arg("x-queue-version", version.version());
      }
      return this;
    }

    @Override
    public Management.QueueSpecification queue() {
      return this.parent;
    }
  }

  private static class AmqpStreamSpecification implements Management.StreamSpecification {

    private final AmqpQueueSpecification parent;

    private AmqpStreamSpecification(AmqpQueueSpecification parent) {
      this.parent = parent;
    }

    @Override
    public Management.StreamSpecification maxAge(Duration maxAge) {
      validatePositive("x-max-age", maxAge.getSeconds());
      this.parent.arg("x-max-age", maxAge.getSeconds() + "s");
      return this;
    }

    @Override
    public Management.StreamSpecification maxSegmentSizeBytes(ByteCapacity maxSegmentSize) {
      validatePositive("x-stream-max-segment-size-bytes", maxSegmentSize.toBytes());
      this.parent.arg("x-stream-max-segment-size-bytes", maxSegmentSize.toBytes());
      return this;
    }

    @Override
    public Management.StreamSpecification initialClusterSize(int initialClusterSize) {
      validatePositive("x-initial-cluster-size", initialClusterSize);
      this.parent.arg("x-initial-cluster-size", initialClusterSize);
      return this;
    }

    @Override
    public Management.QueueSpecification queue() {
      return this.parent;
    }
  }

  String name() {
    return this.name;
  }

  boolean exclusive() {
    return this.exclusive;
  }

  boolean autoDelete() {
    return this.autoDelete;
  }

  void arguments(BiConsumer consumer) {
    this.arguments.forEach(consumer);
  }

  AmqpQueueSpecification duplicate() {
    AmqpQueueSpecification copy = new AmqpQueueSpecification(this.management);
    copy.name(this.name).exclusive(this.exclusive).autoDelete(this.autoDelete);
    copy.shortcutArguments = this.shortcutArguments;
    copy.arguments.putAll(this.arguments);
    return copy;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy