/*******************************************************************************
* Copyright (C) 2014, International Business Machines Corporation
* All Rights Reserved
*******************************************************************************/
/* Generated by Streams Studio: 28 February, 2014 12:15:29 PM EST */
package com.ibm.streamsx.messaging.mqtt;
import java.io.IOException;
import java.io.InputStream;
import java.lang.Thread.State;
import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import org.apache.log4j.Logger;
import org.eclipse.paho.client.mqttv3.MqttException;
import com.ibm.streams.operator.Attribute;
import com.ibm.streams.operator.OperatorContext;
import com.ibm.streams.operator.OperatorContext.ContextCheck;
import com.ibm.streams.operator.OutputTuple;
import com.ibm.streams.operator.StreamSchema;
import com.ibm.streams.operator.StreamingData.Punctuation;
import com.ibm.streams.operator.StreamingInput;
import com.ibm.streams.operator.StreamingOutput;
import com.ibm.streams.operator.Tuple;
import com.ibm.streams.operator.Type;
import com.ibm.streams.operator.Type.MetaType;
import com.ibm.streams.operator.compile.OperatorContextChecker;
import com.ibm.streams.operator.log4j.LoggerNames;
import com.ibm.streams.operator.log4j.TraceLevel;
import com.ibm.streams.operator.model.Icons;
import com.ibm.streams.operator.model.InputPortSet;
import com.ibm.streams.operator.model.InputPortSet.WindowMode;
import com.ibm.streams.operator.model.InputPortSet.WindowPunctuationInputMode;
import com.ibm.streams.operator.model.InputPorts;
import com.ibm.streams.operator.model.Libraries;
import com.ibm.streams.operator.model.OutputPortSet;
import com.ibm.streams.operator.model.OutputPortSet.WindowPunctuationOutputMode;
import com.ibm.streams.operator.model.OutputPorts;
import com.ibm.streams.operator.model.Parameter;
import com.ibm.streams.operator.model.PrimitiveOperator;
import com.ibm.streams.operator.state.Checkpoint;
import com.ibm.streams.operator.state.ConsistentRegionContext;
import com.ibm.streams.operator.state.StateHandler;
import com.ibm.streams.operator.types.Blob;
import com.ibm.streams.operator.types.RString;
import com.ibm.streamsx.messaging.common.DataGovernanceUtil;
import com.ibm.streamsx.messaging.common.IGovernanceConstants;
import com.ibm.streamsx.messaging.common.PropertyProvider;
import com.ibm.streamsx.messaging.mqtt.Messages;
/**
* Class for an operator that consumes tuples and does not produce an output stream.
* This pattern supports a number of input streams and no output streams.
* <P>
* The following event methods from the Operator interface can be called:
* </p>
* <ul>
* <li><code>initialize()</code> to perform operator initialization</li>
* <li>allPortsReady() notification indicates the operator's ports are ready to process and submit tuples</li>
* <li>process() handles a tuple arriving on an input port
* <li>processPuncuation() handles a punctuation mark arriving on an input port
* <li>shutdown() to shutdown the operator. A shutdown request may occur at any time,
* such as a request to stop a PE or cancel a job.
* Thus the shutdown() may occur while the operator is processing tuples, punctuation marks,
* or even during port ready notification.</li>
* </ul>
* <p>With the exception of operator initialization, all the other events may occur concurrently with each other,
* which lead to these methods being called concurrently by different threads.</p>
*/
@PrimitiveOperator(name="MQTTSink", namespace="com.ibm.streamsx.messaging.mqtt",
description=SPLDocConstants.MQTTSINK_OP_DESCRIPTION)
@InputPorts({
@InputPortSet(description = SPLDocConstants.MQTTSINK_INPUTPORT0, cardinality = 1, optional = false, windowingMode = WindowMode.NonWindowed, windowPunctuationInputMode = WindowPunctuationInputMode.Oblivious),
@InputPortSet(description = SPLDocConstants.MQTTSINK_INPUTPORT1, optional = true, windowingMode = WindowMode.NonWindowed, windowPunctuationInputMode = WindowPunctuationInputMode.Oblivious) })
@OutputPorts({
@OutputPortSet(description = SPLDocConstants.MQTTSINK_OUTPUT_PORT0, cardinality = 1, optional = true, windowPunctuationOutputMode = WindowPunctuationOutputMode.Free) })
@Libraries(value = {"opt/downloaded/*"} )
@Icons(location16="icons/MQTTSink_16.gif", location32="icons/MQTTSink_32.gif")
public class MqttSinkOperator extends AbstractMqttOperator implements StateHandler{
private static final String CLASS_NAME = "com.ibm.streamsx.messaging.mqtt.MqttSinkOperator"; //$NON-NLS-1$
static Logger TRACE = Logger.getLogger(MqttSinkOperator.class);
static Logger LOGGER = Logger.getLogger(LoggerNames.LOG_FACILITY + "." + CLASS_NAME); //$NON-NLS-1$
// Parameters
private String topic;
private int qos = 0;
private int reconnectionBound = IMqttConstants.DEFAULT_RECONNECTION_BOUND; // default 5, 0 = no retry, -1 = infinite retry
private long period = IMqttConstants.DEFAULT_RECONNECTION_PERIOD;
private boolean retain = false;
private String topicAttributeName;
private String qosAttributeName;
private MqttClientWrapper mqttWrapper;
private ArrayBlockingQueue<Tuple> tupleQueue;
private boolean shutdown;
private ConsistentRegionContext crContext;
private Object drainLock = new Object();
private Thread publishThread;
private InitialState initState;
private boolean isRelaunching;
private class InitialState {
String initialServerUri;
private InitialState() {
initialServerUri = getServerUri();
}
}
private class PublishRunnable implements Runnable {
@Override
public void run() {
StreamSchema streamSchema = getInput(0).getStreamSchema();
String dataAttributeName = getDataAttributeName() == null ? IMqttConstants.MQTT_DEFAULT_DATA_ATTRIBUTE_NAME : getDataAttributeName();
int dataAttrIndex = streamSchema.getAttributeIndex(dataAttributeName);
// if neither dataAttributeName is specified or schema attribute named "data" can be found
// then it is assumed this schema contains only a single attribute and it is the data attribute
if(dataAttrIndex == -1) {
dataAttrIndex = 0;
}
Type.MetaType dataAttributeType = streamSchema.getAttribute(dataAttrIndex).getType().getMetaType();
boolean isBlob = false;
if(dataAttributeType.equals(MetaType.BLOB))
isBlob = true;
else if (dataAttributeType.equals(MetaType.RSTRING))
isBlob = false;
while (!shutdown)
{
// publish tuple in the background thread
// max 50 tuples in flight
try {
Tuple tuple = tupleQueue.take();
String pubTopic = topic;
int msgQos = qos;
if (topicAttributeName != null)
{
pubTopic = tuple.getString(topicAttributeName);
}
if (qosAttributeName != null)
{
msgQos = tuple.getInt(qosAttributeName);
}
// disconnect if we have received a control signal
if (!mqttWrapper.getPendingBrokerUri().isEmpty())
{
mqttWrapper.disconnect();
}
// if connected, go straight to publishing
if (mqttWrapper.isConnected())
{
// inline this block of code instead of method call
// to avoid unnecessary method call overhead
if (pubTopic != null && pubTopic.length() > 0
&& msgQos >= 0 && msgQos < 3){
byte[] byteArray;
if(isBlob) {
Blob blockMsg = tuple.getBlob(dataAttrIndex);
InputStream inputStream = blockMsg.getInputStream();
int length = (int) blockMsg.getLength();
byteArray = new byte[length];
inputStream.read(byteArray, 0, length);
}
else {
RString rstringObj = (RString)tuple.getObject(dataAttrIndex);
byteArray = rstringObj.getData();
}
mqttWrapper.publish(pubTopic, msgQos, byteArray, retain);
}
else
{
String errorMsg = Messages.getString("TOPIC_OR_QOS_INVALID", pubTopic, msgQos); //$NON-NLS-1$
TRACE.log(TraceLevel.ERROR, errorMsg);
submitToErrorPort(errorMsg, crContext);
}
}
else
{
// if not connected, connect before publishing
boolean connected = validateConnection();
while (!connected && mqttWrapper.isUriChanged(mqttWrapper.getBrokerUri()))
{
connected = validateConnection();
}
if (!connected)
{
String errorMsg = Messages.getString("UNABLE_TO_CONNECT_TO_SERVER_WITH_URI", getServerUri()); //$NON-NLS-1$
submitToErrorPort(errorMsg, crContext);
throw new RuntimeException(errorMsg);
}
// inline this block of code instead of method call
// to avoid unnecessary method call overhead
if (pubTopic != null && pubTopic.length() > 0
&& msgQos >= 0 && msgQos < 3){
byte[] byteArray;
if(isBlob) {
Blob blockMsg = tuple.getBlob(dataAttrIndex);
InputStream inputStream = blockMsg.getInputStream();
int length = (int) blockMsg.getLength();
byteArray = new byte[length];
inputStream.read(byteArray, 0, length);
}
else {
RString rstringObj = (RString)tuple.getObject(dataAttrIndex);
byteArray = rstringObj.getData();
}
mqttWrapper.publish(pubTopic, msgQos, byteArray, retain);
}
else
{
String errorMsg = Messages.getString("TOPIC_OR_QOS_INVALID", pubTopic, msgQos); //$NON-NLS-1$
TRACE.log(TraceLevel.ERROR, errorMsg); //$NON-NLS-1$
submitToErrorPort(errorMsg, crContext);
}
}
}
catch (MqttClientConnectException e)
{
// we should exit if we get a connect exception
if (e instanceof MqttClientConnectException)
{
throw new RuntimeException(e);
}
}
catch (Exception e) {
// do not rethrow exception, log and keep going
if(e instanceof MqttException && ((MqttException) e).getReasonCode() == MqttException.REASON_CODE_CLIENT_TIMEOUT ) {
// defected a command timeout
TRACE.log(TraceLevel.WARN, Messages.getString("TIMED_OUT_WAITING_FOR_SERVER_RESPONSE")); //$NON-NLS-1$
}
else {
String errorMsg = Messages.getString("UNABLE_TO_PUBLISH_MSG"); //$NON-NLS-1$
TRACE.log(TraceLevel.ERROR, errorMsg, e);
submitToErrorPort(errorMsg, crContext);
}
}
finally {
if(crContext != null) {
// if internal buffer has been cleared, notify waiting thread.
if(tupleQueue.peek() == null) {
synchronized(drainLock) {
drainLock.notifyAll();
}
}
}
}
}
}
private boolean validateConnection() throws MqttClientConnectException{
if (!mqttWrapper.isConnected())
{
try {
if (!mqttWrapper.getPendingBrokerUri().isEmpty())
{
mqttWrapper.setBrokerUri(mqttWrapper.getPendingBrokerUri());
// need to update parameter value too
setServerUri(mqttWrapper.getPendingBrokerUri());
}
mqttWrapper.connect(getReconnectionBound(), getPeriod());
} catch (URISyntaxException e) {
String errorMsg = Messages.getString(Messages.getString("UNABLE_TO_CONNECT_TO_SERVER")); //$NON-NLS-1$
TRACE.log(TraceLevel.ERROR, errorMsg, e);
submitToErrorPort(errorMsg, crContext);
throw new RuntimeException(e);
} catch (Exception e) {
String errorMsg = Messages.getString(Messages.getString("UNABLE_TO_CONNECT_TO_SERVER")); //$NON-NLS-1$
TRACE.log(TraceLevel.ERROR, errorMsg, e);
submitToErrorPort(errorMsg, crContext);
if (e instanceof MqttClientConnectException)
{
throw (MqttClientConnectException)e;
}
}
}
return mqttWrapper.isConnected();
}
}
@ContextCheck(compile=true)
public static void checkConsistentRegion(OperatorContextChecker checker) {
// check if this operator is placed at start of a consistent region
OperatorContext oContext = checker.getOperatorContext();
ConsistentRegionContext cContext = oContext.getOptionalContext(ConsistentRegionContext.class);
if(cContext != null) {
List<StreamingInput<Tuple>> inputPorts = checker.getOperatorContext().getStreamingInputs();
// if there is a control port, a warning message is issued as control port is not supported in a consistent region
if(inputPorts.size() > 1) {
LOGGER.warn(Messages.getString("CTRL_PORT_IN_CONSISTENT_REGION_NOT_SUPPORTED")); //$NON-NLS-1$
}
if(cContext.isStartOfRegion()) {
checker.setInvalidContext(Messages.getString("OP_CANNOT_BE_START_OF_CONSISTENT_REGION"), new String[] {"MQTTSink"}); //$NON-NLS-1$
}
}
}
@ContextCheck(compile=true, runtime=false)
public static boolean compileCheckTopic(OperatorContextChecker checker)
{
OperatorContext context = checker.getOperatorContext();
// check the topic and topicAttributeName parameters are mutually exclusive
boolean check = checker.checkExcludedParameters("topic", "topicAttributeName") && //$NON-NLS-1$ //$NON-NLS-2$
checker.checkExcludedParameters("topicAttributeName", "topic"); //$NON-NLS-1$ //$NON-NLS-2$
// check that at least one of topic or topicAttributeName parameter is specified
Set<String> parameterNames = context.getParameterNames();
boolean hasTopic = parameterNames.contains("topic") || parameterNames.contains("topicAttributeName"); //$NON-NLS-1$ //$NON-NLS-2$
if (!hasTopic)
{
checker.setInvalidContext(Messages.getString("TOPIC_OR_TOPICATTRIB_MUST_BE_SPECIFIED"), null); //$NON-NLS-1$
}
check = check & hasTopic;
return check;
}
@ContextCheck(compile=false)
public static void runtimeChecks(OperatorContextChecker checker) {
validateNumber(checker, "period", 0, Long.MAX_VALUE); //$NON-NLS-1$
validateNumber(checker, "qos", 0, 2); //$NON-NLS-1$
validateNumber(checker, "reconnectionBound", -1, Long.MAX_VALUE); //$NON-NLS-1$
checkInputAttribute(checker, "qosAttributeName", MetaType.INT32); //$NON-NLS-1$
checkInputAttribute(checker, "topicAttributeName", MetaType.RSTRING, MetaType.USTRING); //$NON-NLS-1$
checkInputAttribute(checker, "dataAttributeName", MetaType.RSTRING, MetaType.BLOB); //$NON-NLS-1$
}
private static void checkInputAttribute(OperatorContextChecker checker, String parameterName, MetaType... validTypes) {
if (checker.getOperatorContext().getParameterNames().contains(parameterName)) {
List<String> parameterValues = checker.getOperatorContext().getParameterValues(parameterName);
String attributeName = parameterValues.get(0);
List<StreamingInput<Tuple>> inputPorts = checker.getOperatorContext().getStreamingInputs();
if (inputPorts.size() > 0)
{
StreamingInput<Tuple> inputPort = inputPorts.get(0);
StreamSchema streamSchema = inputPort.getStreamSchema();
boolean check = checker.checkRequiredAttributes(streamSchema, attributeName);
if (check)
checker.checkAttributeType(streamSchema.getAttribute(attributeName), validTypes);
}
}
}
@ContextCheck(compile=true, runtime=false)
public static void checkInputPortSchema(OperatorContextChecker checker) {
List<StreamingInput<Tuple>> inputPorts = checker.getOperatorContext().getStreamingInputs();
if (inputPorts.size() > 0)
{
// if user is not specifying dataAttributeName attribute
// then we check if stream schema contains default data attribute
// or if schema contains only single attribute
if(!checker.getOperatorContext().getParameterNames().contains("dataAttributeName")) { //$NON-NLS-1$
StreamingInput<Tuple> dataPort = inputPorts.get(0);
StreamSchema streamSchema = dataPort.getStreamSchema();
Attribute dataAttribute = null;
if(streamSchema.getAttributeCount() == 1) {
dataAttribute = streamSchema.getAttribute(0);
}
else {
dataAttribute = streamSchema.getAttribute("data"); //$NON-NLS-1$
}
// the default data attribute must be present and must be either BLOB or RSTRING
if(dataAttribute != null) {
checker.checkAttributeType(dataAttribute, MetaType.RSTRING, MetaType.BLOB );
}
else {
checker.setInvalidContext(Messages.getString("DATA_ATTRIB_NOT_FOUND_FROM_INPUT_PORT"), new Object[]{}); //$NON-NLS-1$
}
}
}
//TODO: check control input port
}
@ContextCheck(compile = true, runtime = false)
public static void checkOutputPort(OperatorContextChecker checker) {
validateSchemaForErrorOutputPort(checker, getErrorPortFromContext(checker.getOperatorContext()));
}
/**
* Initialize this operator. Called once before any tuples are processed.
* @param context OperatorContext for this operator.
* @throws Exception Operator failure, will cause the enclosing PE to terminate.
*/
@Override
public synchronized void initialize(OperatorContext context)
throws Exception {
// Must call super.initialize(context) to correctly setup an operator.
super.initialize(context);
Logger.getLogger(this.getClass()).trace("Operator " + context.getName() + " initializing in PE: " + context.getPE().getPEId() + " in Job: " + context.getPE().getJobId() ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
crContext = context.getOptionalContext(ConsistentRegionContext.class);
tupleQueue = new ArrayBlockingQueue<Tuple>(50);
mqttWrapper = new MqttClientWrapper();
initFromConnectionDocument();
mqttWrapper.setBrokerUri(getServerUri());
mqttWrapper.setReconnectionBound(getReconnectionBound());
mqttWrapper.setPeriod(getPeriod());
mqttWrapper.setUserID(getUserID());
mqttWrapper.setPassword(getPassword());
mqttWrapper.setClientID(getClientID());
mqttWrapper.setCommandTimeout(getCommandTimeout());
mqttWrapper.setKeepAliveInterval(getKeepAliveInterval());
mqttWrapper.setConnectionLostMetric(nConnectionLost);
mqttWrapper.setIsConnectedMetric(isConnected);
setupSslProperties(mqttWrapper);
if(getAppConfigName() != null) {
mqttWrapper.setPropProvider(new PropertyProvider(context.getPE(), getAppConfigName()));
mqttWrapper.setUserPropName(getUserPropName());
mqttWrapper.setPasswordPropName(getPasswordPropName());
}
if(crContext != null) {
initState = new InitialState();
}
initRelaunching(context);
// do not connect here... connection is done on the publish thread when a message
// is ready to be published
// register for data governance
// if static topic, then register topic, else only register the server
if (topicAttributeName == null) {
registerForDataGovernance();
} else {
// register the "server" for governance
registerServerForDataGovernance();
}
}
private void registerForDataGovernance() {
String uri = getServerUri();
String topic = getTopics();
TRACE.log(TraceLevel.INFO,
"MQTTSink - Registering for data governance with server uri: " + uri + " and topic: " + topic); //$NON-NLS-1$ //$NON-NLS-2$
if (topic != null && !topic.isEmpty() && uri != null && !uri.isEmpty()) {
DataGovernanceUtil.registerForDataGovernance(this, topic, IGovernanceConstants.ASSET_MQTT_TOPIC_TYPE, uri,
IGovernanceConstants.ASSET_MQTT_SERVER_TYPE, false, "MQTTSink"); //$NON-NLS-1$
} else {
TRACE.log(TraceLevel.INFO,
"MQTTSink - Registering for data governance -- aborted. topic and/or url is null"); //$NON-NLS-1$
}
}
private void registerServerForDataGovernance() {
String uri = getServerUri();
TRACE.log(TraceLevel.INFO, "MQTTSource - Registering only server for data governance with server uri: " + uri); //$NON-NLS-1$
if (uri != null && !uri.isEmpty()) {
DataGovernanceUtil.registerForDataGovernance(this, uri, IGovernanceConstants.ASSET_MQTT_SERVER_TYPE, null,
null, false, "MQTTSink"); //$NON-NLS-1$
} else {
TRACE.log(TraceLevel.INFO,
"MQTTSource - Registering only server for data governance -- aborted. uri is null"); //$NON-NLS-1$
}
}
/**
* Notification that initialization is complete and all input and output ports
* are connected and ready to receive and submit tuples.
* @throws Exception Operator failure, will cause the enclosing PE to terminate.
*/
@Override
public synchronized void allPortsReady() throws Exception {
// This method is commonly used by source operators.
// Operators that process incoming tuples generally do not need this notification.
OperatorContext context = getOperatorContext();
Logger.getLogger(this.getClass()).trace("Operator " + context.getName() + " all ports are ready in PE: " + context.getPE().getPEId() + " in Job: " + context.getPE().getJobId() ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
publishThread = context.getThreadFactory().newThread(new PublishRunnable());
publishThread.start();
}
/**
* Process an incoming tuple that arrived on the specified port.
* @param stream Port the tuple is arriving on.
* @param tuple Object representing the incoming tuple.
* @throws Exception Operator failure, will cause the enclosing PE to terminate.
*/
@Override
public void process(StreamingInput<Tuple> stream, Tuple tuple) throws Exception {
if(isRelaunching()) {
TRACE.log(TraceLevel.DEBUG, "Operator is re-launching, discard incoming tuple " + tuple.toString()); //$NON-NLS-1$
return;
}
// if data port
if (stream.getPortNumber() == 0)
{
// put tuple to queue
tupleQueue.put(tuple);
}
// else if control input port
else {
TRACE.log(TraceLevel.DEBUG, "[Control Port:] Control Signal Received"); //$NON-NLS-1$
handleControlSignal(tuple);
}
}
private void handleControlSignal(Tuple tuple) {
// handle control signal to switch server
try {
Object object = tuple.getObject(0);
TRACE.log(TraceLevel.DEBUG, "[Control Port:] object: " + object + " " + object.getClass().getName()); //$NON-NLS-1$ //$NON-NLS-2$
if (object instanceof Map)
{
Map map = (Map)object;
Set keySet = map.keySet();
for (Iterator iterator = keySet.iterator(); iterator
.hasNext();) {
Object key = (Object) iterator.next();
TRACE.log(TraceLevel.DEBUG, "[Control Port:] " + key + " " + key.getClass()); //$NON-NLS-1$ //$NON-NLS-2$
String keyStr = key.toString();
// case insensitive checks
if (keyStr.toLowerCase().equals(IMqttConstants.CONN_SERVERURI.toLowerCase()))
{
Object serverUri = map.get(key);
String serverUriStr = serverUri.toString();
// only handle if server URI has changed
if (!serverUriStr.toLowerCase().equals(getServerUri().toLowerCase()))
{
TRACE.log(TraceLevel.DEBUG, "[Control Port:] " + IMqttConstants.CONN_SERVERURI + ":" + serverUri); //$NON-NLS-1$ //$NON-NLS-2$
// set pending broker URI to get wrapper out of retry loop
mqttWrapper.setPendingBrokerUri(serverUriStr);
// interrupt the publish thread in case it is sleeping
if (publishThread.getState() == State.TIMED_WAITING)
publishThread.interrupt();
}
}
}
}
} catch (Exception e) {
String errorMsg = Messages.getString("CANNOT_PROCESS_CTRL_SIGNAL", tuple.toString()); //$NON-NLS-1$
TRACE.log(TraceLevel.ERROR, errorMsg); //$NON-NLS-1$
submitToErrorPort(errorMsg, crContext);
}
}
/**
* Process an incoming punctuation that arrived on the specified port.
* @param stream Port the punctuation is arriving on.
* @param mark The punctuation mark
* @throws Exception Operator failure, will cause the enclosing PE to terminate.
*/
@Override
public void processPunctuation(StreamingInput<Tuple> stream,
Punctuation mark) throws Exception {
// TODO: If window punctuations are meaningful to the external system or data store,
// insert code here to process the incoming punctuation.
}
/**
* Shutdown this operator.
* @throws Exception Operator failure, will cause the enclosing PE to terminate.
*/
@Override
public synchronized void shutdown() throws Exception {
OperatorContext context = getOperatorContext();
Logger.getLogger(this.getClass()).trace("Operator " + context.getName() + " shutting down in PE: " + context.getPE().getPEId() + " in Job: " + context.getPE().getJobId() ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
shutdown = true;
mqttWrapper.disconnect();
mqttWrapper.shutdown();
// Must call super.shutdown()
super.shutdown();
}
@Parameter(name="topic", description=SPLDocConstants.MQTTSINK_PARAM_TOPIC_DESC, optional=true)
public void setTopics(String topic) {
this.topic = topic;
if (topic.startsWith("$")) //$NON-NLS-1$
{
topicAttributeName = topic.substring(1);
}
}
@Parameter(name="qos", description=SPLDocConstants.MQTTSINK_PARAM_QOS_DESC, optional=true)
public void setQos(int qos) {
this.qos = qos;
}
public String getTopics() {
return topic;
}
public int getQos() {
return qos;
}
@Parameter(name="reconnectionBound", description=SPLDocConstants.MQTTSINK_PARAM_RECONN_BOUND_DESC, optional=true)
public void setReconnectionBound(int reconnectionBound) {
this.reconnectionBound = reconnectionBound;
}
@Parameter(name="period", description=SPLDocConstants.MQTTSINK_PARAM_PERIOD_DESC, optional=true)
public void setPeriod(long period) {
this.period = period;
}
public int getReconnectionBound() {
return reconnectionBound;
}
public long getPeriod() {
return period;
}
public boolean isRetain() {
return retain;
}
@Parameter(name="retain", description=SPLDocConstants.MQTTSINK_PARAM_RETAIN_DESC, optional=true)
public void setRetain(boolean retain) {
this.retain = retain;
}
@Parameter(name="topicAttributeName", description=SPLDocConstants.MQTTSINK_PARAM_TOPIC_ATTR_NAME_DESC, optional=true)
public void setTopicAttrName(String topicAttr) {
this.topicAttributeName = topicAttr;
}
public String getTopicAttrName() {
return topicAttributeName;
}
@Parameter(name="qosAttributeName", description=SPLDocConstants.MQTTSINK_PARAM_QOS_ATTR_NAME_DESC, optional=true)
public void setQosAttributeName(String qosAttributeName) {
this.qosAttributeName = qosAttributeName;
}
public String getQosAttributeName() {
return qosAttributeName;
}
protected StreamingOutput<OutputTuple> getErrorOutputPort() {
return getErrorPortFromContext(getOperatorContext());
}
private static StreamingOutput<OutputTuple> getErrorPortFromContext(OperatorContext opContext) {
List<StreamingOutput<OutputTuple>> streamingOutputs = opContext.getStreamingOutputs();
if (streamingOutputs.size() > 0) {
return streamingOutputs.get(0);
}
return null;
}
@Override
public void close() throws IOException {
TRACE.log(TraceLevel.DEBUG, "StateHandler close"); //$NON-NLS-1$
}
@Override
public void checkpoint(Checkpoint checkpoint) throws Exception {
TRACE.log(TraceLevel.DEBUG, "Checkpoint " + checkpoint.getSequenceId()); //$NON-NLS-1$
String currentServerUri = this.getServerUri();
checkpoint.getOutputStream().writeObject(currentServerUri);
}
@Override
public void drain() throws Exception {
TRACE.log(TraceLevel.DEBUG, "Drain pending tuples..."); //$NON-NLS-1$
if(tupleQueue.peek() != null) {
synchronized(drainLock) {
if(tupleQueue.peek() != null) {
drainLock.wait(IMqttConstants.CONSISTENT_REGION_DRAIN_WAIT_TIME);
if(tupleQueue.peek() != null) {
throw new Exception(Messages.getString("TIMED_OUT_WAITING_FOR_TUPLES_DRAINING")); //$NON-NLS-1$
}
}
}
}
}
@Override
public void reset(Checkpoint checkpoint) throws Exception {
TRACE.log(TraceLevel.DEBUG, "Reset to checkpoint " + checkpoint.getSequenceId()); //$NON-NLS-1$
resetServerUri((String) checkpoint.getInputStream().readObject());
resetRelaunching();
}
@Override
public void resetToInitialState() throws Exception {
TRACE.log(TraceLevel.DEBUG, "Reset to initial state"); //$NON-NLS-1$
if(initState != null && initState.initialServerUri != null) {
resetServerUri(initState.initialServerUri);
}
resetRelaunching();
}
@Override
public void retireCheckpoint(long id) throws Exception {
TRACE.log(TraceLevel.DEBUG, "Retire checkpoint" + id); //$NON-NLS-1$
}
private void resetServerUri(String serverUri) {
// if current server uri is not same as the uri saved in last checkpoint
// then we want to set it as pending broker uri.
if(!getServerUri().equals(serverUri)) {
mqttWrapper.setPendingBrokerUri(serverUri);
}
}
private void initRelaunching(OperatorContext opContext) {
TRACE.log(TraceLevel.DEBUG, "Relaunching set to true"); //$NON-NLS-1$
isRelaunching = false;
if (crContext != null )
{
int relaunchCount = opContext.getPE().getRelaunchCount();
if (relaunchCount > 0) {
isRelaunching = true;
}
}
}
private void resetRelaunching() {
TRACE.log(TraceLevel.DEBUG, "Relaunching set to false"); //$NON-NLS-1$
isRelaunching = false;
}
private boolean isRelaunching() {
return isRelaunching;
}
}