/**
* Copyright 2016 StreamSets Inc.
*
* Licensed under the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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 com.streamsets.pipeline.lib.rabbitmq.common;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConnectionFactory;
import com.streamsets.pipeline.api.Stage;
import com.streamsets.pipeline.api.impl.Utils;
import com.streamsets.pipeline.config.DataFormat;
import com.streamsets.pipeline.lib.rabbitmq.config.BaseRabbitConfigBean;
import com.streamsets.pipeline.lib.rabbitmq.config.Errors;
import com.streamsets.pipeline.lib.rabbitmq.config.Groups;
import com.streamsets.pipeline.lib.rabbitmq.config.RabbitExchangeConfigBean;
import com.streamsets.pipeline.stage.common.DataFormatConfig;
import com.streamsets.pipeline.stage.destination.rabbitmq.BasicPropertiesConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;
public final class RabbitUtil {
private RabbitUtil() {}
private static final Logger LOG = LoggerFactory.getLogger(RabbitUtil.class);
private static final String RABBIT_DATA_FORMAT_CONFIG_PREFIX = "conf.dataFormatConfig.";
public static void initRabbitStage(
Stage.Context context,
BaseRabbitConfigBean conf,
DataFormat dataFormat,
DataFormatConfig dataFormatConfig,
RabbitCxnManager rabbitCxnManager,
List<Stage.ConfigIssue> issues
) {
conf.init(context, issues);
if (!issues.isEmpty()) {
return;
}
try {
rabbitCxnManager.init(conf);
initRabbitConf(rabbitCxnManager.getChannel(), conf);
dataFormatConfig.init(
context,
dataFormat,
Groups.RABBITMQ.name(),
RABBIT_DATA_FORMAT_CONFIG_PREFIX,
issues
);
} catch (IllegalArgumentException e) {
LOG.error("Configuration Error", e);
issues.add(context.createConfigIssue(
Groups.RABBITMQ.name(),
"conf.uri",
Errors.RABBITMQ_03,
e.toString()
));
} catch (TimeoutException | IOException e) {
// Connecting to RabbitMQ timed out or some other issue.
LOG.error("Rabbit MQ issue ", e);
String reason = (e.getCause() == null) ? e.toString() : e.getCause().toString() ;
issues.add(context.createConfigIssue(
Groups.RABBITMQ.name(),
"conf.uri",
Errors.RABBITMQ_01,
reason
));
}
}
public static ConnectionFactory createConnectionFactory (BaseRabbitConfigBean conf) {
RabbitCxnFactoryBuilder builder =
new RabbitCxnFactoryBuilder(
conf.uri,
conf.credentialsConfig.username,
conf.credentialsConfig.password,
conf.rabbitmqProperties
).setConnectionTimeout(conf.advanced.connectionTimeout)
.setAutomaticRecoveryEnabled(conf.advanced.automaticRecoveryEnabled)
.setNetworkRecoveryInterval(conf.advanced.networkRecoveryInterval)
.setHeartbeatInterval(conf.advanced.heartbeatInterval)
.setHandshakeTimeout(conf.advanced.handshakeTimeout)
.setShutdownTimeout(conf.advanced.shutdownTimeout)
.setFrameMax(conf.advanced.frameMax)
.setChannelMax(conf.advanced.channelMax);
return builder.build();
}
public static void buildBasicProperties(
BasicPropertiesConfig basicPropertiesConfig,
Stage.Context context,
AMQP.BasicProperties.Builder builder,
List<Stage.ConfigIssue> issues
) {
if (isNotNullOrEmpty(basicPropertiesConfig.appId)) {
builder.appId(basicPropertiesConfig.appId);
}
if (isNotNullOrEmpty(basicPropertiesConfig.contentEncoding)) {
builder.contentEncoding(basicPropertiesConfig.contentEncoding);
}
if (isNotNullOrEmpty(basicPropertiesConfig.contentType)) {
builder.contentType(basicPropertiesConfig.contentType);
}
if (isNotNullOrEmpty(basicPropertiesConfig.correlationId)) {
builder.correlationId(basicPropertiesConfig.correlationId);
}
builder.deliveryMode(basicPropertiesConfig.deliveryMode.getDeliveryMode());
if (basicPropertiesConfig.expiration < 0) {
LOG.error("Invalid Configuration value for AMQP Basic Properties Expiration", basicPropertiesConfig.expiration);
issues.add(context.createConfigIssue(
Groups.RABBITMQ.name(),
"conf.basicPropertiesConfig.expiration",
Errors.RABBITMQ_09,
basicPropertiesConfig.expiration,
"Expiration"
));
return;
}
builder.expiration(String.valueOf(basicPropertiesConfig.expiration));
if (basicPropertiesConfig.headers != null && !basicPropertiesConfig.headers.isEmpty()) {
builder.headers(basicPropertiesConfig.headers);
}
if (isNotNullOrEmpty(basicPropertiesConfig.messageId)) {
builder.messageId(basicPropertiesConfig.messageId);
}
builder.priority(basicPropertiesConfig.priority.getPriority());
if (isNotNullOrEmpty(basicPropertiesConfig.replyTo)) {
builder.replyTo(basicPropertiesConfig.replyTo);
}
if (!basicPropertiesConfig.setCurrentTime) {
if (basicPropertiesConfig.timestamp < 0) {
LOG.error("Invalid Configuration value for AMQP Basic Properties TimeStamp", basicPropertiesConfig.timestamp);
issues.add(context.createConfigIssue(
Groups.RABBITMQ.name(),
"conf.basicPropertiesConfig.timestamp",
Errors.RABBITMQ_09,
basicPropertiesConfig.timestamp,
"Time Stamp"
));
return;
}
builder.timestamp(new Date(basicPropertiesConfig.timestamp));
}
if (isNotNullOrEmpty(basicPropertiesConfig.type)) {
builder.type(basicPropertiesConfig.type);
}
if (isNotNullOrEmpty(basicPropertiesConfig.userId)) {
builder.userId(basicPropertiesConfig.userId);
}
}
//----------------------------------------------- Private Members ---------------------------------------------------
private static void initRabbitConf(Channel channel, BaseRabbitConfigBean conf) throws IOException{
// Channel is always bound to the default exchange. When specified, we must declare the exchange.
for (RabbitExchangeConfigBean exchange : conf.exchanges) {
channel.exchangeDeclare(
exchange.name,
exchange.type.getValue(),
exchange.durable,
exchange.autoDelete,
exchange.declarationProperties
);
}
channel.queueDeclare(
conf.queue.name,
conf.queue.durable,
conf.queue.exclusive,
conf.queue.autoDelete,
conf.queue.properties
);
for (RabbitExchangeConfigBean exchange : conf.exchanges) {
bindQueue(channel, conf, exchange);
}
}
private static void bindQueue(
Channel channel,
BaseRabbitConfigBean conf,
RabbitExchangeConfigBean exchange
) throws IOException {
// If an exchange is specified, we need the routing key and then we bind it to the channel.
// Note that routing key is ignored for Fanout and Headers (unsupported) type exchanges.
String bindingKey = exchange.routingKey.isEmpty() ? conf.queue.name : exchange.routingKey;
channel.queueBind(conf.queue.name, exchange.name, bindingKey, exchange.bindingProperties);
}
private static boolean isNotNullOrEmpty(String obj) {
return obj != null && !obj.isEmpty();
}
/** ConnectionFactory Builder for Rabbit**/
private static class RabbitCxnFactoryBuilder {
ConnectionFactory factory = null;
String uri = null;
public RabbitCxnFactoryBuilder(
String uri,
String username,
String password,
Map<String, Object> rabbitmqProperties
) {
this.factory = new ConnectionFactory();
this.uri = uri;
this.factory.setUsername(username);
this.factory.setPassword(password);
this.factory.setClientProperties(rabbitmqProperties);
}
public RabbitCxnFactoryBuilder setConnectionTimeout(int connectionTimeout) {
factory.setConnectionTimeout(connectionTimeout);
return this;
}
public RabbitCxnFactoryBuilder setAutomaticRecoveryEnabled(boolean automaticRecoveryEnabled) {
factory.setAutomaticRecoveryEnabled(automaticRecoveryEnabled);
return this;
}
public RabbitCxnFactoryBuilder setNetworkRecoveryInterval(int networkRecoveryInterval) {
factory.setNetworkRecoveryInterval(networkRecoveryInterval);
return this;
}
public RabbitCxnFactoryBuilder setHeartbeatInterval(int heartbeatInterval) {
factory.setRequestedHeartbeat(heartbeatInterval);
return this;
}
public RabbitCxnFactoryBuilder setHandshakeTimeout(int handshakeTimeout) {
factory.setHandshakeTimeout(handshakeTimeout);
return this;
}
public RabbitCxnFactoryBuilder setShutdownTimeout(int shutdownTimeout) {
factory.setShutdownTimeout(shutdownTimeout);
return this;
}
public RabbitCxnFactoryBuilder setFrameMax(int frameMax) {
factory.setRequestedFrameMax(frameMax);
return this;
}
public RabbitCxnFactoryBuilder setChannelMax(int channelMax) {
factory.setRequestedChannelMax(channelMax);
return this;
}
public ConnectionFactory build() {
Utils.checkNotNull(factory.getUsername(), "username");
Utils.checkNotNull(factory.getPassword(), "password");
Utils.checkNotNull(this.uri, "uri");
try {
factory.setUri(uri);
} catch (URISyntaxException | NoSuchAlgorithmException | KeyManagementException e) {
LOG.error("URI invalid ", e);
throw new IllegalArgumentException(e.getMessage(), e.getCause());
}
return factory;
}
}
}