package org.apache.axis2.transport.mqtt;/*
/*
* 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.
*/
import org.apache.axiom.om.OMOutputFormat;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.description.TransportOutDescription;
import org.apache.axis2.transport.MessageFormatter;
import org.apache.axis2.transport.OutTransportInfo;
import org.apache.axis2.transport.base.AbstractTransportSender;
import org.apache.axis2.transport.base.BaseUtils;
import org.apache.axis2.util.MessageProcessorSelector;
import org.apache.commons.io.output.WriterOutputStream;
import org.eclipse.paho.client.mqttv3.*;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Hashtable;
import java.util.Random;
public class MqttSender extends AbstractTransportSender {
private MqttConnectionFactoryManager connectionFactoryManager;
@Override
public void init(ConfigurationContext cfgCtx, TransportOutDescription transportOutDescription) throws AxisFault {
super.init(cfgCtx, transportOutDescription);
connectionFactoryManager = new MqttConnectionFactoryManager(transportOutDescription);
log.info("Mqtt Transport Sender initialized...");
}
@Override
public void sendMessage(MessageContext messageContext, String targetEPR, OutTransportInfo outTransportInfo) throws AxisFault{
Hashtable<String, String> properties;
properties = BaseUtils.getEPRProperties(targetEPR);
MqttConnectOptions mqttConnectOptions;
String mqttBlockingSenderEnable;
MqttConnectionFactory mqttConnectionFactory;
String username = properties.get(MqttConstants.MQTT_USERNAME);
String password = properties.get(MqttConstants.MQTT_PASSWORD);
String cleanSession = properties.get(MqttConstants.MQTT_SESSION_CLEAN);
String topicName = properties.get(MqttConstants.MQTT_TOPIC_NAME);
String clientId = properties.get(MqttConstants.MQTT_CLIENT_ID);
String qosValue = properties.get(MqttConstants.MQTT_QOS);
String retainedMessage = properties.get(MqttConstants.MQTT_MESSAGE_RETAINED);
mqttBlockingSenderEnable = properties.get(MqttConstants.MQTT_BLOCKING_SENDER);
int qos;
/* Default value is set to false */
boolean isMessageRetained = false;
mqttConnectOptions = new MqttConnectOptions();
if (cleanSession != null) {
mqttConnectOptions.setCleanSession(Boolean.parseBoolean(cleanSession));
}
if (password != null) {
mqttConnectOptions.setPassword(password.toCharArray());
}
if (username != null) {
mqttConnectOptions.setUserName(username);
}
mqttConnectionFactory = new MqttConnectionFactory(properties);
if (qosValue != null && !qosValue.isEmpty()) {
qos = Integer.parseInt(qosValue);
} else {
qos = mqttConnectionFactory.getQOS();
}
if (retainedMessage != null && !retainedMessage.isEmpty()) {
isMessageRetained = Boolean.parseBoolean(retainedMessage);
}
MqttClient mqttClient;
MqttAsyncClient mqttAsyncClient;
if (mqttBlockingSenderEnable != null && mqttBlockingSenderEnable.equalsIgnoreCase("true")) {
//removing the urnid:urnid:
String clientIdPostFix = messageContext.getMessageID().substring(9);
String uniqueClientId;
//appending the clientIdPostFix to create unique client id
if(clientId == null) {
uniqueClientId = clientIdPostFix;
} else {
uniqueClientId = clientId + "-" + clientIdPostFix;
}
//sending the message id to make client id unique. Otherwise accessing same store with multiple
//thread make errors
mqttClient = mqttConnectionFactory.getMqttClient(uniqueClientId, qos);
try {
mqttClient.setCallback(new MqttPublisherCallback());
mqttClient.connect(mqttConnectOptions);
if (mqttClient.isConnected()) {
if (topicName == null) {
handleException("The request doesn't contain the required topic fields");
}
MqttTopic mqttTopic = mqttClient.getTopic(topicName);
MqttMessage mqttMessage = createMqttMessage(messageContext);
mqttMessage.setRetained(isMessageRetained);
if (qos >= 0 && qos <= 2) {
mqttMessage.setQos(qos);
} else {
throw new AxisFault("Invalid value for qos " + qos);
}
mqttTopic.publish(mqttMessage);
}
} catch (MqttException e) {
handleException("Exception occurred at sending message", e);
}finally {
if (mqttClient!=null) {
try {
mqttClient.disconnect();
} catch (MqttException e) {
log.error("Error while disconnecting the mqtt client", e);
}
}
}
} else {
//removing the urnid:urnid:
String clientIdPostFix = messageContext.getMessageID().substring(9);
//sending the message id to make client id unique. Otherwise accessing same store with multiple
//thread make errors
//appending the clientIdPostFix to create unique client id
String uniqueClientId = clientId + "-" + clientIdPostFix;
mqttAsyncClient = mqttConnectionFactory.getMqttAsyncClient(uniqueClientId, qos);
try {
MqttAsyncCallback mqttAsyncClientCallback = new MqttAsyncCallback(mqttAsyncClient);
mqttAsyncClientCallback.setConOpt(mqttConnectOptions);
MqttMessage mqttMessage = createMqttMessage(messageContext);
mqttMessage.setRetained(isMessageRetained);
if ((qos >= 0 && qos <= 2)) {
mqttMessage.setQos(qos);
} else {
throw new AxisFault("Invalid value for qos " + qos);
}
mqttAsyncClientCallback.publish(topicName, mqttMessage);
} catch (MqttException me) {
handleException("Exception occurred at sending message", me);
} catch (Throwable th) {
if (th instanceof Exception) {
handleException("Exception occurred while sending message", (Exception) th);
} else {
log.error("Exception occurred while sending message", th);
}
}
}
}
private MqttMessage createMqttMessage(MessageContext messageContext) {
OMOutputFormat format = BaseUtils.getOMOutputFormat(messageContext);
MessageFormatter messageFormatter;
try {
messageFormatter = MessageProcessorSelector.getMessageFormatter(messageContext);
} catch (AxisFault axisFault) {
throw new AxisMqttException("Unable to get the message formatter to use");
}
OutputStream out;
StringWriter sw = new StringWriter();
try {
out = new WriterOutputStream(sw, format.getCharSetEncoding());
} catch (UnsupportedCharsetException ex) {
throw new AxisMqttException("Unsupported encoding " + format.getCharSetEncoding(), ex);
}
try {
messageFormatter.writeTo(messageContext, format, out, true);
out.close();
} catch (IOException e) {
throw new AxisMqttException("IO Error while creating BytesMessage", e);
}
MqttMessage mqttMessage = new MqttMessage();
mqttMessage.setPayload(sw.toString().getBytes());
return mqttMessage;
}
}