/*
* 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.
*/
package org.wso2.carbon.event.output.adapter.mqtt;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.event.output.adapter.core.EventAdapterUtil;
import org.wso2.carbon.event.output.adapter.core.OutputEventAdapter;
import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterConfiguration;
import org.wso2.carbon.event.output.adapter.core.exception.OutputEventAdapterException;
import org.wso2.carbon.event.output.adapter.core.exception.TestConnectionNotSupportedException;
import org.wso2.carbon.event.output.adapter.mqtt.internal.util.MQTTAdapterPublisher;
import org.wso2.carbon.event.output.adapter.mqtt.internal.util.MQTTBrokerConnectionConfiguration;
import org.wso2.carbon.event.output.adapter.mqtt.internal.util.MQTTEventAdapterConstants;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Output MQTTEventAdapter will be used to publish events with MQTT protocol to specified broker and topic.
*/
public class MQTTEventAdapter implements OutputEventAdapter {
private OutputEventAdapterConfiguration eventAdapterConfiguration;
private Map<String, String> globalProperties;
private MQTTAdapterPublisher mqttAdapterPublisher;
private int connectionKeepAliveInterval;
private String qos;
private static ThreadPoolExecutor threadPoolExecutor;
private static final Log log = LogFactory.getLog(MQTTEventAdapter.class);
private int tenantId;
public MQTTEventAdapter(OutputEventAdapterConfiguration eventAdapterConfiguration,
Map<String, String> globalProperties) {
this.eventAdapterConfiguration = eventAdapterConfiguration;
this.globalProperties = globalProperties;
Object keeAliveInternal = globalProperties.get(MQTTEventAdapterConstants.CONNECTION_KEEP_ALIVE_INTERVAL);
if (keeAliveInternal != null) {
try {
connectionKeepAliveInterval = Integer.parseInt(keeAliveInternal.toString());
} catch (NumberFormatException e) {
log.error("Error when configuring user specified connection keep alive time, using default value", e);
connectionKeepAliveInterval = MQTTEventAdapterConstants.DEFAULT_CONNECTION_KEEP_ALIVE_INTERVAL;
}
} else {
connectionKeepAliveInterval = MQTTEventAdapterConstants.DEFAULT_CONNECTION_KEEP_ALIVE_INTERVAL;
}
}
@Override
public void init() throws OutputEventAdapterException {
tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
//ThreadPoolExecutor will be assigned if it is null
if (threadPoolExecutor == null) {
int minThread;
int maxThread;
int jobQueSize;
long defaultKeepAliveTime;
//If global properties are available those will be assigned else constant values will be assigned
if (globalProperties.get(MQTTEventAdapterConstants.ADAPTER_MIN_THREAD_POOL_SIZE_NAME) != null) {
minThread = Integer.parseInt(globalProperties.get(MQTTEventAdapterConstants.ADAPTER_MIN_THREAD_POOL_SIZE_NAME));
} else {
minThread = MQTTEventAdapterConstants.DEFAULT_MIN_THREAD_POOL_SIZE;
}
if (globalProperties.get(MQTTEventAdapterConstants.ADAPTER_MAX_THREAD_POOL_SIZE_NAME) != null) {
maxThread = Integer.parseInt(globalProperties.get(MQTTEventAdapterConstants.ADAPTER_MAX_THREAD_POOL_SIZE_NAME));
} else {
maxThread = MQTTEventAdapterConstants.DEFAULT_MAX_THREAD_POOL_SIZE;
}
if (globalProperties.get(MQTTEventAdapterConstants.ADAPTER_KEEP_ALIVE_TIME_NAME) != null) {
defaultKeepAliveTime = Integer.parseInt(globalProperties.get(
MQTTEventAdapterConstants.ADAPTER_KEEP_ALIVE_TIME_NAME));
} else {
defaultKeepAliveTime = MQTTEventAdapterConstants.DEFAULT_KEEP_ALIVE_TIME_IN_MILLIS;
}
if (globalProperties.get(MQTTEventAdapterConstants.ADAPTER_EXECUTOR_JOB_QUEUE_SIZE_NAME) != null) {
jobQueSize = Integer.parseInt(globalProperties.get(
MQTTEventAdapterConstants.ADAPTER_EXECUTOR_JOB_QUEUE_SIZE_NAME));
} else {
jobQueSize = MQTTEventAdapterConstants.DEFAULT_EXECUTOR_JOB_QUEUE_SIZE;
}
threadPoolExecutor = new ThreadPoolExecutor(minThread, maxThread, defaultKeepAliveTime,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(jobQueSize));
}
}
@Override
public void testConnect() throws TestConnectionNotSupportedException {
throw new TestConnectionNotSupportedException("Test connection is not available");
}
@Override
public void connect() {
MQTTBrokerConnectionConfiguration mqttBrokerConnectionConfiguration =
new MQTTBrokerConnectionConfiguration(eventAdapterConfiguration.getStaticProperties()
.get(MQTTEventAdapterConstants.ADAPTER_CONF_URL),
eventAdapterConfiguration.getStaticProperties()
.get(MQTTEventAdapterConstants.ADAPTER_CONF_USERNAME),
eventAdapterConfiguration.getStaticProperties()
.get(MQTTEventAdapterConstants.ADAPTER_CONF_PASSWORD),
connectionKeepAliveInterval,
eventAdapterConfiguration.getStaticProperties()
.get(MQTTEventAdapterConstants.ADAPTER_CONF_CLEAN_SESSION)
);
String clientId = eventAdapterConfiguration.getStaticProperties().get(MQTTEventAdapterConstants.ADAPTER_CONF_CLIENTID);
qos = eventAdapterConfiguration.getStaticProperties().get(MQTTEventAdapterConstants.ADAPTER_MESSAGE_QOS);
mqttAdapterPublisher = new MQTTAdapterPublisher(mqttBrokerConnectionConfiguration, clientId);
}
@Override
public void publish(Object message, Map<String, String> dynamicProperties) {
String topic = dynamicProperties.get(MQTTEventAdapterConstants.ADAPTER_MESSAGE_TOPIC);
try {
threadPoolExecutor.submit(new MQTTSender(topic, message));
} catch (RejectedExecutionException e) {
EventAdapterUtil.logAndDrop(eventAdapterConfiguration.getName(), message, "Job queue is full", e, log, tenantId);
}
}
@Override
public void disconnect() {
try {
if (mqttAdapterPublisher != null) {
mqttAdapterPublisher.close();
mqttAdapterPublisher = null;
}
} catch (OutputEventAdapterException e) {
log.error("Exception when closing the mqtt publisher connection on Output MQTT Adapter '" + eventAdapterConfiguration.getName() + "'", e);
}
}
@Override
public void destroy() {
//not required
}
@Override
public boolean isPolled() {
return false;
}
class MQTTSender implements Runnable {
String topic;
Object message;
MQTTSender(String topic, Object message) {
this.topic = topic;
this.message = message;
}
@Override
public void run() {
try {
if (qos == null) {
mqttAdapterPublisher.publish(message.toString(), topic);
} else {
mqttAdapterPublisher.publish(Integer.parseInt(qos), message.toString(), topic);
}
} catch (Throwable t) {
EventAdapterUtil.logAndDrop(eventAdapterConfiguration.getName(), message, null, t, log, tenantId);
}
}
}
}