org.seedstack.seed.ws.internal.jms.WSJmsPlugin.java Source code

Java tutorial

Introduction

Here is the source code for org.seedstack.seed.ws.internal.jms.WSJmsPlugin.java

Source

/**
 * Copyright (c) 2013-2015 by The SeedStack authors. All rights reserved.
 *
 * This file is part of SeedStack, An enterprise-oriented full development stack.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package org.seedstack.seed.ws.internal.jms;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import io.nuun.kernel.api.Plugin;
import io.nuun.kernel.api.plugin.InitState;
import io.nuun.kernel.api.plugin.PluginException;
import io.nuun.kernel.api.plugin.context.InitContext;
import io.nuun.kernel.core.AbstractPlugin;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.seedstack.seed.core.api.SeedException;
import org.seedstack.seed.core.internal.application.ApplicationPlugin;
import org.seedstack.seed.jms.internal.JmsPlugin;
import org.seedstack.seed.jms.spi.ConnectionDefinition;
import org.seedstack.seed.jms.spi.JmsFactory;
import org.seedstack.seed.jms.spi.MessageListenerInstanceDefinition;
import org.seedstack.seed.jms.spi.MessagePoller;
import org.seedstack.seed.ws.internal.EndpointDefinition;
import org.seedstack.seed.ws.internal.WSPlugin;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.naming.NamingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * This plugin provides JMS transport integration for WS support.
 *
 * @author adrien.lauer@mpsa.com
 */
public class WSJmsPlugin extends AbstractPlugin {
    public static final List<String> SUPPORTED_BINDINGS = ImmutableList.of("http://www.w3.org/2010/soapjms/");
    public static final String WS_CONFIGURATION_PREFIX = "org.seedstack.seed.ws";

    public static final int DEFAULT_CACHE_CONCURRENCY = 4;
    public static final int DEFAULT_CACHE_SIZE = 16;
    public static final String LISTENER_NAME_PATTERN = "ws-%s-listener";
    public static final String ANONYMOUS_CONNECTION_PATTERN = "ws-anon-connection-%d";

    private final Set<WSJmsMessageListener> wsJmsMessageListeners = new HashSet<WSJmsMessageListener>();

    private LoadingCache<SoapJmsUri, Connection> connectionCache;

    private JmsPlugin jmsPlugin;
    private WSPlugin wsPlugin;

    @Override
    public String name() {
        return "seed-ws-jms-plugin";
    }

    @Override
    public InitState init(InitContext initContext) {
        Configuration wsConfiguration = null;

        for (Plugin plugin : initContext.pluginsRequired()) {
            if (plugin instanceof JmsPlugin) {
                jmsPlugin = (JmsPlugin) plugin;
            }

            if (plugin instanceof WSPlugin) {
                wsPlugin = (WSPlugin) plugin;
            }

            if (plugin instanceof ApplicationPlugin) {
                wsConfiguration = ((ApplicationPlugin) plugin).getApplication().getConfiguration()
                        .subset(WS_CONFIGURATION_PREFIX);
            }
        }

        if (wsConfiguration == null) {
            throw new PluginException("Missing required application plugin");
        }

        int cacheSize = wsConfiguration.getInt("jms.transport-cache.max-size", DEFAULT_CACHE_SIZE);
        final Configuration finalWsConfiguration = wsConfiguration;
        connectionCache = CacheBuilder.newBuilder().maximumSize(cacheSize)
                .concurrencyLevel(wsConfiguration.getInt("transport-cache.concurrency", DEFAULT_CACHE_CONCURRENCY))
                .initialCapacity(wsConfiguration.getInt("transport-cache.initial-size", cacheSize / 4))
                .build(new CacheLoader<SoapJmsUri, Connection>() {
                    private AtomicInteger atomicInteger = new AtomicInteger(0);

                    @Override
                    public Connection load(SoapJmsUri soapJmsUri) throws NamingException, JMSException {
                        String lookupVariant = soapJmsUri.getLookupVariant();
                        JmsFactory jmsFactory = jmsPlugin.getJmsFactory();
                        Connection connection;

                        if (SoapJmsUri.JNDI_LOOKUP_VARIANT.equals(lookupVariant)) {
                            String jndiConnectionFactoryName = soapJmsUri.getParameter("jndiConnectionFactoryName");
                            if (StringUtils.isBlank(jndiConnectionFactoryName)) {
                                throw new IllegalArgumentException(
                                        "Missing jndiConnectionFactoryName parameter for JMS URI "
                                                + soapJmsUri.toString());
                            }

                            String connectionName = soapJmsUri.getConnectionName();
                            if (connectionName == null) {
                                connectionName = String.format(ANONYMOUS_CONNECTION_PATTERN,
                                        atomicInteger.getAndIncrement());
                            }

                            ConnectionDefinition connectionDefinition = jmsFactory.createConnectionDefinition(
                                    connectionName, soapJmsUri.getConfiguration(finalWsConfiguration),
                                    (ConnectionFactory) SoapJmsUri.getContext(soapJmsUri)
                                            .lookup(jndiConnectionFactoryName));

                            connection = jmsFactory.createConnection(connectionDefinition);
                            jmsPlugin.registerConnection(connection, connectionDefinition);
                        } else if (SoapJmsUri.SEED_QUEUE_LOOKUP_VARIANT.equals(lookupVariant)
                                || SoapJmsUri.SEED_TOPIC_LOOKUP_VARIANT.equals(lookupVariant)) {
                            String connectionName = soapJmsUri.getConnectionName();

                            if (StringUtils.isBlank(connectionName)) {
                                throw new IllegalArgumentException(
                                        "Missing connectionName parameter for JMS URI " + soapJmsUri.toString());
                            }

                            connection = jmsPlugin.getConnection(connectionName);
                        } else {
                            throw new IllegalArgumentException("Unsupported lookup variant " + lookupVariant
                                    + " for JMS URI " + soapJmsUri.toString());
                        }

                        if (connection == null) {
                            throw new PluginException(
                                    "Unable to resolve connection for JMS URI " + soapJmsUri.toString());
                        }

                        return connection;
                    }
                });

        for (Map.Entry<String, EndpointDefinition> endpointEntry : wsPlugin
                .getEndpointDefinitions(SUPPORTED_BINDINGS).entrySet()) {
            EndpointDefinition endpointDefinition = endpointEntry.getValue();
            String endpointName = endpointEntry.getKey();
            String serviceName = endpointDefinition.getServiceName().getLocalPart();
            String portName = endpointDefinition.getPortName().getLocalPart();
            String serviceNameAndServicePort = serviceName + "-" + portName;

            SoapJmsUri uri;
            try {
                uri = SoapJmsUri.parse(new URI(endpointDefinition.getUrl()));
                uri.setEndpointName(endpointName);
            } catch (URISyntaxException e) {
                throw new PluginException("Unable to parse endpoint URI", e);
            }

            Configuration endpointConfiguration = uri.getConfiguration(wsConfiguration);
            Connection connection;
            try {
                connection = connectionCache.get(uri);
            } catch (Exception e) {
                throw new PluginException("Unable to create JMS connection for WS " + serviceNameAndServicePort, e);
            }

            Session session;
            try {
                session = connection.createSession(endpointConfiguration.getBoolean("transactional", true),
                        Session.AUTO_ACKNOWLEDGE);
            } catch (JMSException e) {
                throw new PluginException("Unable to create JMS session for WS " + serviceNameAndServicePort, e);
            }

            Destination destination;
            try {
                destination = SoapJmsUri.getDestination(uri, session);
            } catch (Exception e) {
                throw new PluginException("Unable to create JMS destination for WS " + serviceNameAndServicePort,
                        e);
            }

            WSJmsMessageListener messageListener = new WSJmsMessageListener(uri,
                    new JmsAdapter(wsPlugin.createWSEndpoint(endpointDefinition, null)), session);

            String messageListenerName = String.format(LISTENER_NAME_PATTERN, endpointName);
            try {
                Class<? extends MessagePoller> poller = getPoller(endpointConfiguration);

                jmsPlugin.registerMessageListener(
                        new MessageListenerInstanceDefinition(messageListenerName, uri.getConnectionName(), session,
                                destination, endpointConfiguration.getString("selector"), messageListener, poller));
            } catch (Exception e) {
                throw SeedException.wrap(e, WSJmsErrorCodes.UNABLE_TO_REGISTER_MESSAGE_LISTENER)
                        .put("messageListenerName", messageListenerName);
            }
            wsJmsMessageListeners.add(messageListener);
        }

        return InitState.INITIALIZED;
    }

    @SuppressWarnings("unchecked")
    private Class<? extends MessagePoller> getPoller(Configuration endpointConfiguration)
            throws ClassNotFoundException {
        String pollerClassName = endpointConfiguration.getString("poller");
        if (pollerClassName != null) {
            return (Class<? extends MessagePoller>) Class.forName(pollerClassName);
        }
        return null;
    }

    @Override
    public void stop() {
        if (connectionCache != null) {
            connectionCache.invalidateAll();
            connectionCache.cleanUp();
        }
    }

    @Override
    public Collection<Class<? extends Plugin>> requiredPlugins() {
        Collection<Class<? extends Plugin>> plugins = new ArrayList<Class<? extends Plugin>>();
        plugins.add(WSPlugin.class);
        plugins.add(ApplicationPlugin.class);
        plugins.add(JmsPlugin.class);

        return plugins;
    }

    @Override
    public Object nativeUnitModule() {
        return new WSJmsModule(wsJmsMessageListeners, connectionCache);
    }
}