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

org.projectodd.wunderboss.messaging.hornetq.HQMessaging Maven / Gradle / Ivy

/*
 * Copyright 2014-2015 Red Hat, Inc, and individual contributors.
 *
 * 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.
 */

package org.projectodd.wunderboss.messaging.hornetq;

import org.hornetq.api.core.TransportConfiguration;
import org.hornetq.api.jms.HornetQJMSClient;
import org.hornetq.api.jms.JMSFactoryType;
import org.hornetq.jms.client.HornetQConnectionFactory;
import org.hornetq.jms.client.HornetQXAConnectionFactory;
import org.hornetq.jms.server.JMSServerManager;
import org.projectodd.wunderboss.Options;
import org.projectodd.wunderboss.WunderBoss;
import org.projectodd.wunderboss.messaging.Context;
import org.projectodd.wunderboss.messaging.Messaging;
import org.projectodd.wunderboss.messaging.Queue;
import org.projectodd.wunderboss.messaging.Topic;
import org.slf4j.Logger;

import javax.jms.ConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.XAConnectionFactory;
import javax.jms.XAJMSContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

public class HQMessaging implements Messaging {

    public static final String REMOTE_TYPE_WILDFLY = "hornetq_wildfly";
    public static final String REMOTE_TYPE_STANDALONE = "hornetq_standalone";
    public static final String BROKER_ID = UUID.randomUUID().toString();

    public HQMessaging(String name, Options options) {
        this.name = name;
        this.options = options;
    }

    @Override
    public synchronized void start() throws Exception {
        if (!started) {
            this.server = new EmbeddedServer();

            ClassLoader cl = this.getClass().getClassLoader();
            if (cl.getResource("hornetq-configuration.xml") == null) {
                this.server.setConfigResourcePath("default-hornetq-configuration.xml");
            }

            if (cl.getResource("hornetq-jms.xml") == null) {
                this.server.setJmsConfigResourcePath("default-hornetq-jms.xml");
            }

            this.server.start();

            this.started = true;
        }
    }

    @Override
    public synchronized void stop() throws Exception {
        if (started) {
            closeCloseables();
            this.server.stop();
            this.server = null;
            this.started = false;
        }
    }

    @Override
    public boolean isRunning() {
        return started;
    }

    public JMSServerManager jmsServerManager() {
        if (this.started) {
            return this.server.serverManager();
        }

        return null;
    }

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

    private HQSpecificContext createContext(ConnectionFactory cf, Options options) {
        int mode = HQContext.modeToJMSMode((Context.Mode) options.get(Messaging.CreateContextOption.MODE));
        JMSContext jmsContext;

        if (options.has(CreateContextOption.USERNAME)) {
            jmsContext = cf.createContext(options.getString(CreateContextOption.USERNAME),
                                          options.getString(CreateContextOption.PASSWORD),
                                          mode);
        } else {
            jmsContext = cf.createContext(mode);
        }

        return new HQContext(jmsContext, this,
                                     (Context.Mode)options.get(CreateContextOption.MODE),
                                     options.has(CreateContextOption.HOST));
    }

    private HQSpecificContext createContext(String factoryName, Options options) {
        return createContext((ConnectionFactory) lookupJNDI(factoryName), options);
    }

    private HQSpecificContext createXAContext(XAConnectionFactory cf, Options options) {
        XAJMSContext context;

        if (HQXAContext.tm == null) {
            throw new NullPointerException("TransactionManager not found; is transactions module on the classpath?");
        }

        if (options.has(CreateContextOption.USERNAME)) {
            context = cf.createXAContext(options.getString(CreateContextOption.USERNAME),
                                      options.getString(CreateContextOption.PASSWORD));
        } else {
            context = cf.createXAContext();
        }

        return new HQXAContext(context, this,
                               (Context.Mode)options.get(CreateContextOption.MODE),
                               options.has(CreateContextOption.HOST));
    }

    private HQSpecificContext createXAContext(String factoryName, Options options) {
        return createXAContext((XAConnectionFactory) lookupJNDI(factoryName), options);
    }

    private ConnectionFactory createHQConnectionFactory(final Options options) {
        //TODO: possibly cache the remote cf's?
        TransportConfiguration config =
                new TransportConfiguration("org.hornetq.core.remoting.impl.netty.NettyConnectorFactory",
                        new HashMap() {{
                            put("host", options.getString(CreateContextOption.HOST));
                            put("port", options.getInt(CreateContextOption.PORT));
                            put("http-upgrade-enabled",
                                    REMOTE_TYPE_WILDFLY.equals(options.getString(CreateContextOption.REMOTE_TYPE)));
                        }});
        HornetQConnectionFactory hornetQcf = HornetQJMSClient
                .createConnectionFactoryWithoutHA(options.has(CreateContextOption.XA) ?
                                                          JMSFactoryType.XA_CF :
                                                          JMSFactoryType.CF,
                                                  config);

        hornetQcf.setReconnectAttempts(options.getInt(CreateContextOption.RECONNECT_ATTEMPTS));
        hornetQcf.setRetryInterval(options.getLong(CreateContextOption.RECONNECT_RETRY_INTERVAL));
        hornetQcf.setRetryIntervalMultiplier(options.getDouble(CreateContextOption.RECONNECT_RETRY_INTERVAL_MULTIPLIER));
        hornetQcf.setMaxRetryInterval(options.getLong(CreateContextOption.RECONNECT_MAX_RETRY_INTERVAL));

        return hornetQcf;
    }

    @Override
    public Context createContext(Map options) throws Exception {
        final Options opts = new Options<>(options);
        HQSpecificContext context;

        boolean xa = opts.getBoolean(CreateContextOption.XA);
        if (opts.has(CreateContextOption.HOST)) {
            if (xa) {
                context = createXAContext((HornetQXAConnectionFactory) createHQConnectionFactory(opts), opts);
            } else {
                context = createContext(createHQConnectionFactory(opts), opts);
            }
        }  else {
            start();

            if (xa) {
                context = createXAContext("java:/JmsXA", opts);
            } else {
                context = createContext("java:/ConnectionFactory", opts);
            }
        }

        if (opts.has(CreateContextOption.CLIENT_ID)) {
            context.jmsContext().setClientID(opts.getString(CreateContextOption.CLIENT_ID));
        }


        return context;
    }

    @Override
    public synchronized Queue findOrCreateQueue(String name,
                                                Map options) throws Exception {
        Options opts = new Options<>(options);
        javax.jms.Queue queue;
        HQSpecificContext givenContext = (HQSpecificContext)opts.get(CreateQueueOption.CONTEXT);
        if (givenContext != null) {
            if (!givenContext.isRemote()) {
                throw new IllegalArgumentException("Queue lookup only accepts a remote context.");
            }
            if (opts.size() > 1) {
                throw new IllegalArgumentException("Creation options provided for a remote queue.");
            }
            queue = givenContext.jmsContext().createQueue(name);
        } else {
            start();

            queue = lookupQueue(name);
            if (queue == null) {
                if (HQDestination.isJndiName(name)) {
                    throw new IllegalArgumentException("queue " + name + " does not exist, and jndi names are lookup only.");
                } else {
                    queue = createQueue(name,
                                        opts.getString(CreateQueueOption.SELECTOR, ""),
                                        opts.getBoolean(CreateQueueOption.DURABLE));
                    this.createdDestinations.add(HQQueue.fullName(name));
                }
            } else {
                if (opts.size() > 0) {
                    log.warn("Ignoring the queue creation options provided for " + name + ", the queue already exists.");
                }
            }
        }

        return new HQQueue(name, queue, this);
    }

    @Override
    public synchronized Topic findOrCreateTopic(String name,
                                                Map options) throws Exception {
        Options opts = new Options<>(options);
        javax.jms.Topic topic;
        HQSpecificContext givenContext = (HQSpecificContext)opts.get(CreateTopicOption.CONTEXT);
        if (givenContext != null) {
            if (!givenContext.isRemote()) {
                throw new IllegalArgumentException("Topic lookup only accepts a remote context.");
            }
            if (opts.size() > 1) {
                throw new IllegalArgumentException("Creation options provided for a remote topic.");
            }
            topic = givenContext.jmsContext().createTopic(name);
        } else {
            start();

            topic = lookupTopic(name);
            if (topic == null) {
                if (HQDestination.isJndiName(name)) {
                    throw new IllegalArgumentException("topic " + name + " does not exist, and jndi names are lookup only.");
                } else {
                    topic = createTopic(name);
                    this.createdDestinations.add(HQTopic.fullName(name));
                }
            } else {
                if (opts.size() > 0) {
                    log.warn("Ignoring the topic creation options provided for " + name + ", the topic already exists.");
                }
            }
        }

        return new HQTopic(name, topic, this);
    }

    protected void addCloseable(AutoCloseable closeable) {
        this.closeables.add(closeable);
    }

    protected void closeCloseables() throws Exception {
        for(AutoCloseable c : this.closeables) {
            c.close();
        }
        this.closeables.clear();
    }

    protected javax.jms.Topic createTopic(String name) throws Exception {
        this.server
                .serverManager()
                .createTopic(false, name, HQDestination.jndiName(name, "topic"));

        return lookupTopic(name);
    }

    protected javax.jms.Queue createQueue(String name, String selector, boolean durable) throws Exception {
        this.server
                .serverManager()
                .createQueue(false, name, selector, durable, HQDestination.jndiName(name, "queue"));

        return lookupQueue(name);
    }

    protected boolean destroyDestination(HQDestination dest) throws Exception {
        String fullName = dest.fullName();
        if (this.closeablesForDestination.containsKey(fullName)) {
            for(AutoCloseable each : this.closeablesForDestination.get(fullName)) {
                each.close();
            }
            this.closeablesForDestination.remove(fullName);
        }

        if (this.createdDestinations.contains(fullName)) {
            if (dest instanceof HQQueue) {
                destroyQueue(dest.name());
            } else {
                destroyTopic(dest.name());
            }
            this.createdDestinations.remove(fullName);

            return true;
        }

        return false;
    }

    protected void destroyQueue(String name) throws Exception {
        this.jmsServerManager().destroyQueue(name, true);
    }

    protected void destroyTopic(String name) throws Exception {
        this.jmsServerManager().destroyTopic(name, true);
    }

    protected void addCloseableForDestination(HQDestination dest, AutoCloseable c) {
        String fullName = dest.fullName();
        List closeables = this.closeablesForDestination.get(fullName);
        if (closeables == null) {
            closeables = new ArrayList<>();
            this.closeablesForDestination.put(fullName, closeables);
        }

        closeables.add(c);
    }

    protected javax.jms.Topic lookupTopic(String name) {
        List jndiNames = new ArrayList<>();

        if (this.server != null) {
            jndiNames.addAll(Arrays.asList(this.server.serverManager().getJNDIOnTopic(name)));
        }
        jndiNames.add(name);
        jndiNames.add(HQTopic.jmsName(name));
        jndiNames.add(HQDestination.jndiName(name, "topic"));

        return (javax.jms.Topic)lookupJNDI(jndiNames);
    }

    protected javax.jms.Queue lookupQueue(String name) {
        List jndiNames = new ArrayList<>();

        if (this.server != null) {
            jndiNames.addAll(Arrays.asList(this.server.serverManager().getJNDIOnQueue(name)));
        }
        jndiNames.add(name);
        jndiNames.add(HQQueue.jmsName(name));
        jndiNames.add(HQDestination.jndiName(name, "queue"));

        return (javax.jms.Queue)lookupJNDI(jndiNames);
    }

    protected Object lookupJNDI(String jndiName) {
        return server.getRegistry().lookup(jndiName);
    }

    protected Object lookupJNDI(List jndiNames) {
        for (String jndiName : jndiNames) {
            Object jndiObject = lookupJNDI(jndiName);
            if (jndiObject != null) {
                return jndiObject;
            }
        }

        return null;
    }

    private final String name;
    private final Options options;
    private final Set createdDestinations = new HashSet<>();
    private final Map> closeablesForDestination = new HashMap<>();
    private final List closeables = new ArrayList<>();
    protected boolean started = false;
    protected EmbeddedServer server;

    private final static Logger log = WunderBoss.logger("org.projectodd.wunderboss.messaging.hornetq");
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy