/*
* Copyright 2015 the original author or authors.
*
* 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.springframework.xd.dirt.plugins.spark.streaming;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.interceptor.WireTap;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.util.Assert;
import org.springframework.util.MimeType;
import org.springframework.xd.dirt.integration.bus.MessageBus;
import org.springframework.xd.spark.streaming.SparkMessageSender;
/**
* Binds as a producer to the MessageBus in order to send RDD elements from DStreams.
*
* @author Ilayaperumal Gopinathan
* @author Mark Fisher
* @author Gary Russell
* @since 1.1
*/
@SuppressWarnings("serial")
class MessageBusSender extends SparkMessageSender {
/**
* Logger.
*/
private static final Logger logger = LoggerFactory.getLogger(MessageBusSender.class);
private final String outputChannelName;
private final LocalMessageBusHolder messageBusHolder;
private final Properties messageBusProperties;
private final Properties moduleProducerProperties;
private final Properties moduleProperties;
private MessageBus messageBus;
private ConfigurableApplicationContext applicationContext;
private boolean running = false;
private static final String OUTPUT = "output";
private final MimeType contentType;
private final SparkStreamingChannel outputChannel;
private final String tapChannelName;
private static final String ENABLE_TAP_PROP = "enableTap";
public MessageBusSender(String outputChannelName, String tapChannelName, Properties messageBusProperties,
Properties moduleProducerProperties, MimeType contentType, Properties moduleProperties) {
this(null, outputChannelName, tapChannelName, messageBusProperties, moduleProducerProperties, contentType,
moduleProperties);
}
public MessageBusSender(LocalMessageBusHolder messageBusHolder, String outputChannelName,
String tapChannelName, Properties messageBusProperties, Properties moduleProducerProperties,
MimeType contentType, Properties moduleProperties) {
this.messageBusHolder = messageBusHolder;
this.outputChannelName = outputChannelName;
this.tapChannelName = tapChannelName;
this.messageBusProperties = messageBusProperties;
this.moduleProducerProperties = moduleProducerProperties;
this.moduleProperties = moduleProperties;
this.contentType = contentType;
this.outputChannel = new SparkStreamingChannel();
}
@Override
public synchronized void start() {
if (!this.isRunning()) {
outputChannel.setBeanName(OUTPUT);
logger.info("starting MessageBusSender");
if (messageBus == null) {
if (messageBusHolder != null) {
messageBus = messageBusHolder.get();
}
else {
applicationContext = MessageBusConfiguration.createApplicationContext(messageBusProperties);
messageBus = applicationContext.getBean(MessageBus.class);
}
if (contentType != null) {
outputChannel.configureMessageConverter(contentType);
}
messageBus.bindProducer(outputChannelName, outputChannel, moduleProducerProperties);
if (isTapEnabled()) {
addTapChannel();
}
}
this.running = true;
}
}
/**
* Check if the tap is enabled for this module's output.
*
* @return boolean
*/
private boolean isTapEnabled() {
boolean tapEnabled = false;
String enableTap = this.moduleProperties.getProperty(ENABLE_TAP_PROP);
if (enableTap != null) {
tapEnabled = enableTap.equalsIgnoreCase(Boolean.TRUE.toString());
}
return tapEnabled;
}
/**
* Add tap channel to the module's output channel.
* Also, bind the tap channel to the message bus.
*/
private void addTapChannel() {
Assert.notNull(outputChannel, "Output channel can not be null.");
Assert.notNull(messageBus, "MessageBus can not be null.");
logger.info("creating and binding tap channel for {}", tapChannelName);
DirectChannel tapChannel = new DirectChannel();
tapChannel.setBeanName(tapChannelName + ".tap.bridge");
messageBus.bindPubSubProducer(tapChannelName, tapChannel, null);
outputChannel.addInterceptor(new WireTap(tapChannel));
}
@Override
@SuppressWarnings("rawtypes")
public synchronized void send(Message message) {
this.outputChannel.send(message);
}
@Override
public synchronized void stop() {
if (this.isRunning() && messageBus != null) {
logger.info("stopping MessageBusSender");
messageBus.unbindProducer(outputChannelName, outputChannel);
for (ChannelInterceptor interceptor : outputChannel.getChannelInterceptors()) {
if (interceptor instanceof WireTap) {
((WireTap) interceptor).stop();
}
}
if (isTapEnabled()) {
messageBus.unbindProducers(tapChannelName);
}
messageBus = null;
}
if (applicationContext != null) {
applicationContext.close();
applicationContext = null;
}
this.running = false;
}
@Override
public synchronized boolean isRunning() {
return this.running;
}
}