/** * Copyright 2014-2017 Linagora, Université Joseph Fourier, Floralis * * The present code is developed in the scope of the joint LINAGORA - * Université Joseph Fourier - Floralis research program and is designated * as a "Result" pursuant to the terms and conditions of the LINAGORA * - Université Joseph Fourier - Floralis research program. Each copyright * holder of Results enumerated here above fully & independently holds complete * ownership of the complete Intellectual Property rights applicable to the whole * of said Results, and may freely exploit it in any manner which does not infringe * the moral rights of the other copyright holders. * * 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 net.roboconf.messaging.rabbitmq.internal.utils; import static net.roboconf.core.utils.Utils.getValue; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.DEFAULT_SSL_KEY_STORE_TYPE; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.DEFAULT_SSL_MNGR_FACTORY; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.DEFAULT_SSL_PROTOCOL; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.DEFAULT_SSL_TRUST_STORE_TYPE; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SERVER_IP; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SERVER_PASSWORD; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SERVER_USERNAME; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SSL_KEY_MNGR_FACTORY; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SSL_KEY_STORE_PASSPHRASE; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SSL_KEY_STORE_PATH; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SSL_KEY_STORE_TYPE; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SSL_PROTOCOL; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SSL_TRUST_MNGR_FACTORY; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SSL_TRUST_STORE_PASSPHRASE; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SSL_TRUST_STORE_PATH; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SSL_TRUST_STORE_TYPE; import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_USE_SSL; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.util.Map; import java.util.logging.Logger; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; import com.rabbitmq.client.Channel; import com.rabbitmq.client.ConnectionFactory; import net.roboconf.core.utils.Utils; import net.roboconf.messaging.api.extensions.MessagingContext; import net.roboconf.messaging.api.extensions.MessagingContext.RecipientKind; import net.roboconf.messaging.rabbitmq.RabbitMqConstants; /** * @author Vincent Zurczak - Linagora */ public final class RabbitMqUtils { /** * Constructor. */ private RabbitMqUtils() { // nothing } /** * Configures the connection factory with the right settings. * @param factory the connection factory * @param configuration the messaging configuration * @throws IOException if something went wrong * @see RabbitMqConstants */ public static void configureFactory( ConnectionFactory factory, Map<String,String> configuration ) throws IOException { final Logger logger = Logger.getLogger( RabbitMqUtils.class.getName()); logger.fine( "Configuring a connection factory for RabbitMQ." ); String messageServerIp = configuration.get( RABBITMQ_SERVER_IP ); if( messageServerIp != null ) { Map.Entry<String,Integer> entry = Utils.findUrlAndPort( messageServerIp ); factory.setHost( entry.getKey()); if( entry.getValue() > 0 ) factory.setPort( entry.getValue()); } factory.setUsername( configuration.get( RABBITMQ_SERVER_USERNAME )); factory.setPassword( configuration.get( RABBITMQ_SERVER_PASSWORD )); // Timeout for connection establishment: 5s factory.setConnectionTimeout( 5000 ); // Configure automatic reconnection factory.setAutomaticRecoveryEnabled( true ); // Recovery interval: 10s factory.setNetworkRecoveryInterval( 10000 ); // Exchanges and so on should be redeclared if necessary factory.setTopologyRecoveryEnabled( true ); // SSL if( Boolean.parseBoolean( configuration.get( RABBITMQ_USE_SSL ))) { logger.fine( "Connection factory for RabbitMQ: SSL is used." ); InputStream clientIS = null; InputStream storeIS = null; try { clientIS = new FileInputStream( configuration.get( RABBITMQ_SSL_KEY_STORE_PATH )); storeIS = new FileInputStream( configuration.get( RABBITMQ_SSL_TRUST_STORE_PATH )); char[] keyStorePassphrase = configuration.get( RABBITMQ_SSL_KEY_STORE_PASSPHRASE ).toCharArray(); KeyStore ks = KeyStore.getInstance( getValue( configuration, RABBITMQ_SSL_KEY_STORE_TYPE, DEFAULT_SSL_KEY_STORE_TYPE )); ks.load( clientIS, keyStorePassphrase ); String value = getValue( configuration, RABBITMQ_SSL_KEY_MNGR_FACTORY, DEFAULT_SSL_MNGR_FACTORY ); KeyManagerFactory kmf = KeyManagerFactory.getInstance( value ); kmf.init( ks, keyStorePassphrase ); char[] trustStorePassphrase = configuration.get( RABBITMQ_SSL_TRUST_STORE_PASSPHRASE ).toCharArray(); KeyStore tks = KeyStore.getInstance( getValue( configuration, RABBITMQ_SSL_TRUST_STORE_TYPE, DEFAULT_SSL_TRUST_STORE_TYPE )); tks.load( storeIS, trustStorePassphrase ); value = getValue( configuration, RABBITMQ_SSL_TRUST_MNGR_FACTORY, DEFAULT_SSL_MNGR_FACTORY ); TrustManagerFactory tmf = TrustManagerFactory.getInstance( value ); tmf.init( tks ); SSLContext c = SSLContext.getInstance( getValue( configuration, RABBITMQ_SSL_PROTOCOL, DEFAULT_SSL_PROTOCOL )); c.init( kmf.getKeyManagers(), tmf.getTrustManagers(), null ); factory.useSslProtocol( c ); } catch( GeneralSecurityException e ) { throw new IOException( "SSL configuration for the RabbitMQ factory failed.", e ); } finally { Utils.closeQuietly( storeIS ); Utils.closeQuietly( clientIS ); } } } /** * Closes the connection to a channel. * @param channel the channel to close (can be null) * @throws IOException if something went wrong */ public static void closeConnection( Channel channel ) throws IOException { if( channel != null ) { if( channel.isOpen()) channel.close(); if( channel.getConnection().isOpen()) channel.getConnection().close(); } } /** * Declares the required exchanges for an application (only for agents). * @param domain the domain name * @param applicationName the application name * @param channel the RabbitMQ channel * @throws IOException if an error occurs */ public static void declareApplicationExchanges( String domain, String applicationName, Channel channel ) throws IOException { // "topic" is a keyword for RabbitMQ. if( applicationName != null ) { String exch = buildExchangeNameForAgent( domain, applicationName ); channel.exchangeDeclare( exch, "topic" ); } } /** * Declares the global exchanges (those that do not depend on an application). * <p> * It includes the DM exchange and the one for inter-application exchanges. * </p> * * @param channel the RabbitMQ channel * @throws IOException if an error occurs */ public static void declareGlobalExchanges( String domain, Channel channel ) throws IOException { // "topic" is a keyword for RabbitMQ. channel.exchangeDeclare( buildExchangeNameForTheDm( domain ), "topic" ); channel.exchangeDeclare( buildExchangeNameForInterApp( domain ), "topic" ); } /** * Builds the name of an exchange for agents (related to the application name). * @param domain the domain * @param applicationName the application name * @return a non-null string */ public static String buildExchangeNameForAgent( String domain, String applicationName ) { return domain + "." + applicationName + ".agents"; } /** * Builds the name of the exchange for the DM. * @param domain the domain * @return a non-null string */ public static String buildExchangeNameForTheDm( String domain ) { return domain + "." + RabbitMqConstants.EXCHANGE_DM; } /** * Builds the name of the exchange for inter-application exchanges. * @param domain the domain * @return a non-null string */ public static String buildExchangeNameForInterApp( String domain ) { return domain + "." + RabbitMqConstants.EXCHANGE_INTER_APP; } /** * Builds an exchange name from a messaging context. * @param ctx a non-null context * @return a non-null string */ public static String buildExchangeName( MessagingContext ctx ) { String exchangeName; if( ctx.getKind() == RecipientKind.DM ) exchangeName = buildExchangeNameForTheDm( ctx.getDomain()); else if( ctx.getKind() == RecipientKind.INTER_APP ) exchangeName = buildExchangeNameForInterApp( ctx.getDomain()); else exchangeName = RabbitMqUtils.buildExchangeNameForAgent( ctx.getDomain(), ctx.getApplicationName()); return exchangeName; } }