Search code examples
spring-amqp

Spring AmqpAdmin create exchange & queues in vHost


A similar question to Send message to arbitrary vhost / exchange with RabbitMQ / Spring AMQP but I'm trying to have the AmqpAdmin create an exchange under a specific vHost

I tried doing something like

SimpleResourceHolder.bind(((RabbitAdmin) amqpAdmin).getRabbitTemplate().getConnectionFactory(), vhost);
...
amqpAdmin.declareExchange(exchange);
...
amqpAdmin.declareQueue(queue);
amqpAdmin.declareBinding(BindingBuilder.bind(queue).to(exchange).with(routingKey));

SimpleResourceHolder.unbind(((RabbitAdmin) amqpAdmin).getRabbitTemplate().getConnectionFactory());

However AmqpAdmin keeps using "/"

is there a way to tell it to use a specific vHost programmatically at runtime?

update 1: based on @artem-bilan I've had (partial) success by doing:


    public void sendToTopic(String domain, String topic, String routingKey, Object payload) {
        bindToVirtualHost(template, domain);
        try {
            template.setUsePublisherConnection(true);
            template.convertAndSend(topic, routingKey, payload);
        } finally {
            unbindFromVirtualHost(template);
            template.setUsePublisherConnection(false);
        }
    }

    private void bindToVirtualHost(RabbitTemplate rabbitTemplate, String vHost) {
        AbstractConnectionFactory factory = (AbstractConnectionFactory) rabbitTemplate.getConnectionFactory();
        LOG.debug("binding {} to {}", factory, vHost);
        factory.setVirtualHost(vHost);
    }

    private void unbindFromVirtualHost(RabbitTemplate rabbitTemplate) {
        AbstractConnectionFactory factory = (AbstractConnectionFactory) rabbitTemplate.getConnectionFactory();
        LOG.debug("unbinding {} back to default {}", factory, DEFAULT_VHOST);
        factory.setVirtualHost(DEFAULT_VHOST);
    }

I say (partial) because if I do:

// pre :Manually create vHost foo

        sendToTopic("bar","myTopic","key","The payload"); // connection error; protocol method: #method<connection.close>(reply-code=530, reply-text=NOT_ALLOWED - vhost TNo not found, as expected
        sendToTopic("foo","myTopic","key","The payload2"); // success, as expected
        sendToTopic("bar","myTopic","key","The payload3"); // success, NOT EXPECTED!

and the message of payload3 goes to vHost foo


Solution

  • The RabbitAdmin cannot do more than its ConnectionFactory allows. So, the vHost is similar to the host/port and it cannot be managed from end-user perspectiv.

    See:

    /**
     * Create a new CachingConnectionFactory given a host name
     * and port.
     * @param hostNameArg the host name to connect to
     * @param port the port number
     */
    public CachingConnectionFactory(@Nullable String hostNameArg, int port) {
    

    and its:

    public void setVirtualHost(String virtualHost) {
    

    The RabbitAdmin, on its turn, is just like this:

    /**
     * Construct an instance using the provided {@link ConnectionFactory}.
     * @param connectionFactory the connection factory - must not be null.
     */
    public RabbitAdmin(ConnectionFactory connectionFactory) {
    

    So, to deal with different vHost, you need to have its own ConnectionFactory and RabbitAdmin.

    No, AmqpAdmin cannot create for you a vHost. This is not an AMQP protocol operation. See https://docs.spring.io/spring-amqp/docs/2.2.7.RELEASE/reference/html/#management-rest-api for more info.