org.wso2.andes.server.Broker.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.andes.server.Broker.java

Source

/*
*  Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
*  WSO2 Inc. 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.wso2.andes.server;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.logging.Log;
import org.wso2.andes.AMQException;
import org.wso2.andes.amqp.AMQPUtils;
import org.wso2.andes.configuration.AndesConfigurationManager;
import org.wso2.andes.configuration.enums.AndesConfiguration;
import org.wso2.andes.configuration.modules.JKSStore;
import org.wso2.andes.configuration.qpid.ServerConfiguration;
import org.wso2.andes.configuration.qpid.ServerNetworkTransportConfiguration;
import org.wso2.andes.configuration.qpid.management.ConfigurationManagementMBean;
import org.wso2.andes.kernel.Andes;
import org.wso2.andes.kernel.AndesException;
import org.wso2.andes.kernel.AndesKernelBoot;
import org.wso2.andes.server.information.management.ServerInformationMBean;
import org.wso2.andes.server.logging.SystemOutMessageLogger;
import org.wso2.andes.server.logging.actors.BrokerActor;
import org.wso2.andes.server.logging.actors.CurrentActor;
import org.wso2.andes.server.logging.actors.GenericActor;
import org.wso2.andes.server.logging.messages.BrokerMessages;
import org.wso2.andes.server.protocol.AMQProtocolEngineFactory;
import org.wso2.andes.server.protocol.AmqpProtocolVersion;
import org.wso2.andes.server.protocol.MultiVersionProtocolEngineFactory;
import org.wso2.andes.server.registry.ApplicationRegistry;
import org.wso2.andes.server.registry.ConfigurationFileApplicationRegistry;
import org.wso2.andes.server.transport.QpidAcceptor;
import org.wso2.andes.ssl.SSLContextFactory;
import org.wso2.andes.transport.NetworkTransportConfiguration;
import org.wso2.andes.transport.network.IncomingNetworkTransport;
import org.wso2.andes.transport.network.Transport;
import org.wso2.andes.transport.network.mina.MinaNetworkTransport;

import javax.management.JMException;
import java.io.File;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static org.wso2.andes.transport.ConnectionSettings.WILDCARD_ADDRESS;

/**
 * The following class contains the startup and shutting down implementation of Andes Broker.
 */
public class Broker {
    private static Log log = org.apache.commons.logging.LogFactory.getLog(Broker.class);

    /**
     * Exception class for initializing failure
     */
    protected static class InitException extends RuntimeException {
        private static final long serialVersionUID = 1L;

        InitException(String msg, Throwable cause) {
            super(msg, cause);
        }
    }

    /**
     * Shutdowns Andes broker.
     *
     * @throws AndesException
     */
    public void shutdown() throws AndesException {
        Andes.getInstance().shutDown();
    }

    /**
     * Starts up Andes broker
     *
     * @throws AndesException
     */
    public void startup() throws AndesException {
        startup(new BrokerOptions());
    }

    /**
     * Starts up Andes broker with options(configurations).
     *
     * @param options The broker configurations.
     * @throws AndesException
     */
    public void startup(BrokerOptions options) throws AndesException {
        CurrentActor.set(new BrokerActor(new SystemOutMessageLogger()));
        startupImpl(options);
        CurrentActor.remove();
    }

    /**
     * Starts the TCP listener for handling AMQP messages.
     *
     * @param config       The configuration for application registry.
     * @param options      Broker options
     * @param serverConfig Server configuration
     * @throws AndesException
     */
    private void startAMQPListener(ApplicationRegistry config, BrokerOptions options,
            ServerConfiguration serverConfig) throws AndesException {
        try {
            if (AndesConfigurationManager.<Boolean>readValue(AndesConfiguration.TRANSPORTS_AMQP_ENABLED)) {
                ConfigurationManagementMBean configMBean = new ConfigurationManagementMBean();
                configMBean.register();

                ServerInformationMBean sysInfoMBean = new ServerInformationMBean(config);
                sysInfoMBean.register();

                Set<Integer> ports = new HashSet<Integer>(options.getPorts());
                if (ports.isEmpty()) {
                    parsePortList(ports, serverConfig.getPorts());
                }

                Set<Integer> sslPorts = new HashSet<Integer>(options.getSSLPorts());
                if (sslPorts.isEmpty()) {
                    parsePortList(sslPorts, serverConfig.getSSLPorts());
                }

                Set<Integer> exclude_0_10 = new HashSet<Integer>(options.getExcludedPorts(ProtocolExclusion.v0_10));
                if (exclude_0_10.isEmpty()) {
                    parsePortList(exclude_0_10, serverConfig.getPortExclude010());
                }

                Set<Integer> exclude_0_9_1 = new HashSet<Integer>(
                        options.getExcludedPorts(ProtocolExclusion.v0_9_1));
                if (exclude_0_9_1.isEmpty()) {
                    parsePortList(exclude_0_9_1, serverConfig.getPortExclude091());
                }

                Set<Integer> exclude_0_9 = new HashSet<Integer>(options.getExcludedPorts(ProtocolExclusion.v0_9));
                if (exclude_0_9.isEmpty()) {
                    parsePortList(exclude_0_9, serverConfig.getPortExclude09());
                }

                Set<Integer> exclude_0_8 = new HashSet<Integer>(options.getExcludedPorts(ProtocolExclusion.v0_8));
                if (exclude_0_8.isEmpty()) {
                    parsePortList(exclude_0_8, serverConfig.getPortExclude08());
                }

                String bindAddressFromBrokerOptions = options.getBind();
                if (null == bindAddressFromBrokerOptions) {
                    bindAddressFromBrokerOptions = serverConfig.getBind();
                }

                InetAddress bindAddressForHostname;
                if (WILDCARD_ADDRESS.equals(bindAddressFromBrokerOptions)) {
                    bindAddressForHostname = new InetSocketAddress(0).getAddress();
                } else {
                    bindAddressForHostname = InetAddress.getByName(bindAddressFromBrokerOptions);
                }
                String hostName = bindAddressForHostname.getCanonicalHostName();

                if (!serverConfig.getSSLOnly()) {
                    for (int port : ports) {
                        Set<AmqpProtocolVersion> supported = EnumSet.allOf(AmqpProtocolVersion.class);

                        if (exclude_0_10.contains(port)) {
                            supported.remove(AmqpProtocolVersion.v0_10);
                        }

                        if (exclude_0_9_1.contains(port)) {
                            supported.remove(AmqpProtocolVersion.v0_9_1);
                        }
                        if (exclude_0_9.contains(port)) {
                            supported.remove(AmqpProtocolVersion.v0_9);
                        }
                        if (exclude_0_8.contains(port)) {
                            supported.remove(AmqpProtocolVersion.v0_8);
                        }

                        NetworkTransportConfiguration settings = new ServerNetworkTransportConfiguration(
                                serverConfig, port, bindAddressFromBrokerOptions, Transport.TCP);

                        IncomingNetworkTransport transport = Transport.getIncomingTransportInstance();
                        MultiVersionProtocolEngineFactory protocolEngineFactory = new MultiVersionProtocolEngineFactory(
                                hostName, supported);

                        transport.accept(settings, protocolEngineFactory, null);
                        ApplicationRegistry.getInstance().addAcceptor(
                                new InetSocketAddress(bindAddressForHostname, port),
                                new QpidAcceptor(transport, "TCP"));
                        CurrentActor.get().message(BrokerMessages.LISTENING("TCP", port));

                    }
                }

                if (serverConfig.getEnableSSL()) {
                    JKSStore keyStore = AndesConfigurationManager
                            .readValue(AndesConfiguration.TRANSPORTS_AMQP_SSL_CONNECTION_KEYSTORE);
                    JKSStore trustStore = AndesConfigurationManager
                            .readValue(AndesConfiguration.TRANSPORTS_AMQP_SSL_CONNECTION_TRUSTSTORE);

                    String sslProtocols = AndesConfigurationManager
                            .readValue(AndesConfiguration.TRANSPORTS_AMQP_SSL_CONNECTION_PROTOCOLS);
                    String ciphers = AndesConfigurationManager
                            .readValue(AndesConfiguration.TRANSPORTS_AMQP_SSL_CONNECTION_CIPHERS);

                    SSLContextFactory sslFactory = new SSLContextFactory(trustStore.getStoreLocation(),
                            trustStore.getPassword(), trustStore.getStoreAlgorithm(), keyStore.getStoreLocation(),
                            keyStore.getPassword(), keyStore.getStoreAlgorithm(), sslProtocols, ciphers);

                    for (int sslPort : sslPorts) {
                        NetworkTransportConfiguration settings = new ServerNetworkTransportConfiguration(
                                serverConfig, sslPort, bindAddressFromBrokerOptions, Transport.TCP);

                        IncomingNetworkTransport transport = new MinaNetworkTransport();

                        transport.accept(settings, new AMQProtocolEngineFactory(), sslFactory);

                        ApplicationRegistry.getInstance().addAcceptor(
                                new InetSocketAddress(bindAddressForHostname, sslPort),
                                new QpidAcceptor(transport, "TCP"));
                        CurrentActor.get().message(BrokerMessages.LISTENING("TCP/SSL", sslPort));
                    }
                }

                CurrentActor.get().message(BrokerMessages.READY());
            } else {
                log.warn("AMQP Transport is disabled as per configuration.");
            }
        } catch (JMException e) {
            throw new AndesException("Unable to register an MBean", e);
        } catch (UnknownHostException e) {
            throw new AndesException("Unable to get bind address", e);
        }
    }

    /**
     * Andes broker startup implementation.
     *
     * @param options The broker options for configurations.
     * @throws AndesException
     */
    private void startupImpl(final BrokerOptions options) throws AndesException {

        boolean isActorSet = false;
        try {

            final String qpidHome = options.getQpidHome();
            File configFile;

            configFile = getConfigFile(options.getConfigFile(), BrokerOptions.DEFAULT_ANDES_CONFIG_FILE, qpidHome,
                    true);

            log.info("Starting Qpid using configuration : " + configFile.getAbsolutePath());

            /*File logConfigFile = getConfigFile(options.getLogConfigFile(),
                                BrokerOptions.DEFAULT_LOG_CONFIG_FILE, qpidHome, false);
                
            configureLogging(logConfigFile, options.getLogWatchFrequency());*/

            ConfigurationFileApplicationRegistry config = new ConfigurationFileApplicationRegistry(configFile);
            ServerConfiguration serverConfig = config.getConfiguration();
            updateManagementPort(serverConfig, options.getJmxPort());

            ApplicationRegistry.initialise(config);

            // We have already loaded the BrokerMessages class by this point so we
            // need to refresh the locale setting in-case we had a different value in
            // the configuration.
            BrokerMessages.reload();

            // AR.initialise() sets and removes its own actor so we now need to set the actor
            // for the remainder of the startup, and the default actor if the stack is empty
            CurrentActor.set(new BrokerActor(config.getCompositeStartupMessageLogger()));
            CurrentActor.setDefault(new BrokerActor(config.getRootMessageLogger()));
            GenericActor.setDefaultMessageLogger(config.getRootMessageLogger());

            isActorSet = true;

            /**
             * Boot andes kernel before starting AMQP transport.
             */
            AndesKernelBoot.initializeComponents();

            startAMQPListener(config, options, serverConfig);

            AndesKernelBoot.startMessaging();
            AndesKernelBoot.createSuperTenantDLC();

            AMQPUtils.DEFAULT_CONTENT_CHUNK_SIZE = AndesConfigurationManager
                    .readValue(AndesConfiguration.PERFORMANCE_TUNING_MAX_CONTENT_CHUNK_SIZE);

        } catch (ConfigurationException ce) {
            throw new AndesException("Unable to create configuration files based application registry", ce);
        } catch (AMQException amqe) {
            throw new AndesException("Unable to register a memory configuration", amqe);
        } catch (Exception e) {
            throw new AndesException("Unable to initialise application registry", e);
        } finally {
            if (isActorSet) {
                // Startup is complete so remove the AR initialised Startup actor
                CurrentActor.remove();
            }
        }
    }

    /**
     * Gets the configuration file.
     *
     * @param fileName            The file name for the configuration file.
     * @param defaultFileName     The default configuration file name.
     * @param qpidHome            The qpid home path.
     * @param throwOnFileNotFound Throws error if configuration file is not found.
     * @return The configuration file.
     * @throws InitException
     */
    private File getConfigFile(final String fileName, final String defaultFileName, final String qpidHome,
            boolean throwOnFileNotFound) throws InitException {
        File configFile;
        if (null != fileName) {
            configFile = new File(fileName);
        } else {
            configFile = new File(qpidHome, defaultFileName);
        }

        if (!configFile.exists() && throwOnFileNotFound) {
            String error = "File " + fileName + " could not be found. Check the file exists and is readable.";

            if (null == qpidHome) {
                error = error + "\nNote: " + BrokerOptions.ANDES_HOME + " is not set.";
            }

            throw new InitException(error, null);
        }

        return configFile;
    }

    /**
     * Parsing a port list to an integer set.
     *
     * @param output The integer set.
     * @param ports  The list of ports.
     * @throws InitException
     */
    public static void parsePortList(Set<Integer> output, List<?> ports) throws InitException {
        if (null != ports) {
            for (Object port : ports) {
                try {
                    output.add(Integer.parseInt(String.valueOf(port)));
                } catch (NumberFormatException e) {
                    throw new InitException("Invalid port: " + port, e);
                }
            }
        }
    }

    /**
     * Update the configuration data with the management port.
     *
     * @param configuration  The server configuration.
     * @param managementPort The string from the command line
     */
    private void updateManagementPort(ServerConfiguration configuration, Integer managementPort) {
        if (null != managementPort) {
            try {
                configuration.setJMXManagementPort(managementPort);
            } catch (NumberFormatException e) {
                throw new InitException("Invalid management port: " + managementPort, null);
            }
        }
    }

}