org.apache.james.backend.rabbitmq.DockerRabbitMQ.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.james.backend.rabbitmq.DockerRabbitMQ.java

Source

/****************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one   *
 * or more contributor license agreements.  See the NOTICE file *
 * distributed with this work for additional information        *
 * regarding copyright ownership.  The ASF licenses this file   *
 * to you 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.                                           *
 ****************************************************************/
package org.apache.james.backend.rabbitmq;

import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import java.util.Optional;
import java.util.UUID;

import org.apache.http.client.utils.URIBuilder;
import org.apache.james.util.docker.Images;
import org.apache.james.util.docker.RateLimiters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.containers.wait.strategy.WaitAllStrategy;
import org.testcontainers.containers.wait.strategy.WaitStrategy;

import com.github.fge.lambdas.consumers.ThrowingConsumer;
import com.google.common.collect.ImmutableMap;
import com.rabbitmq.client.Address;
import com.rabbitmq.client.ConnectionFactory;

public class DockerRabbitMQ {
    private static final Logger LOGGER = LoggerFactory.getLogger(DockerRabbitMQ.class);

    private static final String DEFAULT_RABBIT_HOST_NAME_PREFIX = "my-rabbit";
    private static final String DEFAULT_RABBIT_NODE_NAME_PREFIX = "rabbit";
    private static final int DEFAULT_RABBITMQ_PORT = 5672;
    private static final int DEFAULT_RABBITMQ_ADMIN_PORT = 15672;
    private static final String DEFAULT_RABBITMQ_USERNAME = "guest";
    private static final String DEFAULT_RABBITMQ_PASSWORD = "guest";
    private static final String RABBITMQ_ERLANG_COOKIE = "RABBITMQ_ERLANG_COOKIE";
    private static final String RABBITMQ_NODENAME = "RABBITMQ_NODENAME";
    private static final Duration TEN_MINUTES_TIMEOUT = Duration.ofMinutes(10);

    private final GenericContainer<?> container;
    private final String nodeName;
    private final String rabbitHostName;
    private final String hostNameSuffix;

    public static DockerRabbitMQ withCookieAndHostName(String hostNamePrefix, String clusterIdentity,
            String erlangCookie, Network network) {
        return new DockerRabbitMQ(Optional.ofNullable(hostNamePrefix), Optional.ofNullable(clusterIdentity),
                Optional.ofNullable(erlangCookie), Optional.of(network));
    }

    public static DockerRabbitMQ withoutCookie() {
        return new DockerRabbitMQ(Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty());
    }

    @SuppressWarnings("resource")
    private DockerRabbitMQ(Optional<String> hostNamePrefix, Optional<String> clusterIdentity,
            Optional<String> erlangCookie, Optional<Network> net) {
        this.hostNameSuffix = clusterIdentity.orElse(UUID.randomUUID().toString());
        this.rabbitHostName = hostName(hostNamePrefix);
        this.container = new GenericContainer<>(Images.RABBITMQ)
                .withCreateContainerCmdModifier(cmd -> cmd.withName(this.rabbitHostName))
                .withCreateContainerCmdModifier(cmd -> cmd.withHostName(this.rabbitHostName))
                .withExposedPorts(DEFAULT_RABBITMQ_PORT, DEFAULT_RABBITMQ_ADMIN_PORT).waitingFor(waitStrategy())
                .withLogConsumer(frame -> LOGGER.debug(frame.getUtf8String()))
                .withCreateContainerCmdModifier(cmd -> cmd.getHostConfig()
                        .withTmpFs(ImmutableMap.of("/var/lib/rabbitmq/mnesia", "rw,noexec,nosuid,size=100m")));
        net.ifPresent(this.container::withNetwork);
        erlangCookie.ifPresent(cookie -> this.container.withEnv(RABBITMQ_ERLANG_COOKIE, cookie));
        this.nodeName = DEFAULT_RABBIT_NODE_NAME_PREFIX + "@" + this.rabbitHostName;
        this.container.withEnv(RABBITMQ_NODENAME, this.nodeName);
    }

    private WaitStrategy waitStrategy() {
        return new WaitAllStrategy()
                .withStrategy(Wait.forHttp("").forPort(DEFAULT_RABBITMQ_ADMIN_PORT)
                        .withRateLimiter(RateLimiters.TWENTIES_PER_SECOND).withStartupTimeout(TEN_MINUTES_TIMEOUT))
                .withStrategy(new RabbitMQWaitStrategy(this, TEN_MINUTES_TIMEOUT))
                .withStartupTimeout(TEN_MINUTES_TIMEOUT);
    }

    private String hostName(Optional<String> hostNamePrefix) {
        return hostNamePrefix.orElse(DEFAULT_RABBIT_HOST_NAME_PREFIX) + "-" + hostNameSuffix;
    }

    private String getHostIp() {
        return container.getContainerIpAddress();
    }

    private Integer getPort() {
        return container.getMappedPort(DEFAULT_RABBITMQ_PORT);
    }

    private Integer getAdminPort() {
        return container.getMappedPort(DEFAULT_RABBITMQ_ADMIN_PORT);
    }

    public String getUsername() {
        return DEFAULT_RABBITMQ_USERNAME;
    }

    public String getPassword() {
        return DEFAULT_RABBITMQ_PASSWORD;
    }

    public ConnectionFactory connectionFactory() {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(getHostIp());
        connectionFactory.setPort(getPort());
        connectionFactory.setUsername(getUsername());
        connectionFactory.setPassword(getPassword());
        return connectionFactory;
    }

    public void start() {
        if (!container.isRunning()) {
            container.start();
        }
    }

    public void stop() {
        container.stop();
    }

    public void restart() {
        DockerClientFactory.instance().client().restartContainerCmd(container.getContainerId());
    }

    public GenericContainer<?> container() {
        return container;
    }

    public String getNodeName() {
        return nodeName;
    }

    public void join(DockerRabbitMQ rabbitMQ) throws Exception {
        stopApp();
        joinCluster(rabbitMQ);
        startApp();
        waitForReadyness();
    }

    public void stopApp() throws java.io.IOException, InterruptedException {
        String stdout = container().execInContainer("rabbitmqctl", "stop_app").getStdout();
        LOGGER.debug("stop_app: {}", stdout);
    }

    private void joinCluster(DockerRabbitMQ rabbitMQ) throws java.io.IOException, InterruptedException {
        String stdout = container().execInContainer("rabbitmqctl", "join_cluster", rabbitMQ.getNodeName())
                .getStdout();
        LOGGER.debug("join_cluster: {}", stdout);
    }

    public void startApp() throws Exception {
        String stdout = container().execInContainer("rabbitmqctl", "start_app").getStdout();
        LOGGER.debug("start_app: {}", stdout);
    }

    public void reset() throws Exception {
        stopApp();

        String stdout = container().execInContainer("rabbitmqctl", "reset").getStdout();
        LOGGER.debug("reset: {}", stdout);

        startApp();
    }

    public void forgetNode(String removalClusterNodeName) throws Exception {
        String stdout = container()
                .execInContainer("rabbitmqctl", "-n", this.nodeName, "forget_cluster_node", removalClusterNodeName)
                .getStdout();
        LOGGER.debug("forget_cluster_node: {}", stdout);

        startApp();
    }

    public void waitForReadyness() {
        waitStrategy().waitUntilReady(container);
    }

    public Address address() {
        return new Address(getHostIp(), getPort());
    }

    public URI amqpUri() throws URISyntaxException {
        return new URIBuilder().setScheme("amqp").setHost(getHostIp()).setPort(getPort()).build();
    }

    public URI managementUri() throws URISyntaxException {
        return new URIBuilder().setScheme("http").setHost(getHostIp()).setPort(getAdminPort()).build();
    }

    public void performIfRunning(ThrowingConsumer<DockerRabbitMQ> actionPerform) {
        if (container.isRunning()) {
            actionPerform.accept(this);
        }
    }
}