diff --git a/components/iot-plugins/androidsense-plugin/org.wso2.carbon.device.mgt.iot.androidsense.agent/app/androidsense.apk b/components/iot-plugins/androidsense-plugin/org.wso2.carbon.device.mgt.iot.androidsense.agent/app/androidsense.apk deleted file mode 100644 index 89acc8aa0..000000000 Binary files a/components/iot-plugins/androidsense-plugin/org.wso2.carbon.device.mgt.iot.androidsense.agent/app/androidsense.apk and /dev/null differ diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/pom.xml b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/pom.xml new file mode 100644 index 000000000..922cb286c --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/pom.xml @@ -0,0 +1,177 @@ + + + + + iot-base-plugin + org.wso2.carbon.devicemgt-plugins + 2.1.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + org.wso2.carbon.device.mgt.iot.input.adapter + bundle + WSO2 Carbon - Device Mgt Input Adaptor Module + Provides the back-end functionality of Input adaptor + http://wso2.org + + + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.input.adapter.core + + + org.wso2.carbon + org.wso2.carbon.logging + + + org.wso2.carbon + org.wso2.carbon.core + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + + + org.apache.httpcomponents.wso2 + httpcore + + + org.wso2.orbit.org.apache.httpcomponents + httpclient + + + com.googlecode.json-simple.wso2 + json-simple + + + org.wso2.carbon.devicemgt + org.wso2.carbon.identity.jwt.client.extension + + + com.jayway.jsonpath + json-path + + + org.wso2.carbon.identity + org.wso2.carbon.identity.oauth.stub + + + org.igniterealtime.smack.wso2 + smack + + + org.igniterealtime.smack.wso2 + smackx + + + + + + + org.apache.felix + maven-scr-plugin + + + generate-scr-descriptor + + scr + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.artifactId} + ${project.artifactId} + + org.wso2.carbon.device.mgt.iot.input.adapter.internal, + org.wso2.carbon.device.mgt.iot.input.adapter.internal.* + + + !org.wso2.carbon.device.mgt.iot.input.adapter.internal, + !org.wso2.carbon.device.mgt.iot.input.adapter.internal.*, + org.wso2.carbon.device.mgt.iot.input.adapter.* + + + org.wso2.carbon.event.input.adapter.core, + org.wso2.carbon.event.input.adapter.core.*, + javax.xml.namespace; version=0.0.0, + org.eclipse.paho.client.mqttv3.*, + org.apache.http;version="${httpclient.version.range}", + org.apache.http.message;version="${httpclient.version.range}", + org.apache.http.client;version="${httpclient.version.range}", + org.apache.http.impl;version="${httpclient.version.range}", + org.apache.http.conn.*;version="${httpclient.version.range}", + org.apache.http.util;version="${httpclient.version.range}", + org.apache.http.client.entity;version="${httpclient.version.range}", + org.apache.http.client.methods;version="${httpclient.version.range}", + org.apache.http.impl.client;version="${httpclient.version.range}", + org.json.simple.*, + org.wso2.carbon.identity.jwt.client.extension.*, + com.jayway.jsonpath.*, + javax.net.ssl, + org.apache.commons.logging, + org.apache.http.entity, + org.osgi.framework, + org.osgi.service.component, + org.wso2.carbon.context, + org.wso2.carbon.core, + javax.servlet, + javax.servlet.http, + org.apache.axiom.om.util, + org.osgi.service.http, + org.wso2.carbon.user.api, + org.wso2.carbon.user.core.service, + org.wso2.carbon.user.core.tenant, + org.wso2.carbon.utils, + org.wso2.carbon.utils.multitenancy, + org.wso2.carbon.identity.oauth2.stub;version="${carbon.identity.version.range}", + org.wso2.carbon.identity.oauth2.stub.dto;version="${carbon.identity.version.range}", + org.apache.axis2, + org.apache.axis2.client, + org.apache.axis2.context, + org.apache.axis2.transport.http, + org.apache.commons.httpclient, + org.apache.commons.httpclient.contrib.ssl, + org.apache.commons.httpclient.params, + org.apache.commons.httpclient.protocol, + org.apache.commons.pool, + org.apache.commons.pool.impl, + org.jivesoftware.smack.*, + org.apache.log4j, + org.wso2.carbon.base, + org.wso2.carbon.core.util + + * + + + + + + + \ No newline at end of file diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/ContentInfo.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/ContentInfo.java new file mode 100644 index 000000000..0f1cc48f2 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/ContentInfo.java @@ -0,0 +1,55 @@ +/* +* Copyright (c) 2016, 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.device.mgt.iot.input.adapter; + +/** + * This is the return type of the ContentValidator. + */ +public class ContentInfo { + /** + * true if the content is valid if not when false then content will not be published. + */ + private boolean isValidContent; + /** + * msgText to be returned. eg: if the content is encrypted then we can decrypt the content and then validate and + * return it. + */ + private String msgText; + + public ContentInfo(boolean isValidContent, String msgText) { + this.isValidContent = isValidContent; + this.msgText = msgText; + } + + public boolean isValidContent() { + return isValidContent; + } + + public void setIsValidContent(boolean isValidContent) { + this.isValidContent = isValidContent; + } + + public String getMsgText() { + return msgText; + } + + public void setMsgText(String msgText) { + this.msgText = msgText; + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/ContentTransformer.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/ContentTransformer.java new file mode 100644 index 000000000..1e6857d28 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/ContentTransformer.java @@ -0,0 +1,8 @@ +package org.wso2.carbon.device.mgt.iot.input.adapter; + +import java.util.Map; + +public interface ContentTransformer { + + String transform(String message, Map dynamicProperties); +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/ContentValidator.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/ContentValidator.java new file mode 100644 index 000000000..738d2fa53 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/ContentValidator.java @@ -0,0 +1,33 @@ +/* +* Copyright (c) 2016, 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.device.mgt.iot.input.adapter; + +import java.util.Map; + +/** + * This interface will be triggered to validate the stream content before publishing. + */ +public interface ContentValidator { + /** + * @param contentValidationParams that related to input adapter. + * @param dynamicParameter that message. + * @return ContentInfo. + */ + ContentInfo validate(String msgPayload, Map contentValidationParams, Map dynamicParameter); +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/DefaultContentTransformer.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/DefaultContentTransformer.java new file mode 100644 index 000000000..f127c29ed --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/DefaultContentTransformer.java @@ -0,0 +1,14 @@ +package org.wso2.carbon.device.mgt.iot.input.adapter; + +import java.util.Map; + +/** + * This holds the default implementation of ContentTransformer + */ +public class DefaultContentTransformer implements ContentTransformer{ + + @Override + public String transform(String message, Map dynamicProperties) { + return message; + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/DefaultContentValidator.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/DefaultContentValidator.java new file mode 100644 index 000000000..f14a5c796 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/DefaultContentValidator.java @@ -0,0 +1,11 @@ +package org.wso2.carbon.device.mgt.iot.input.adapter; + +import java.util.Map; + +public class DefaultContentValidator implements ContentValidator { + @Override + public ContentInfo validate(String msgPayload, Map params, Map dynamicPaarams) { + return new ContentInfo(true, msgPayload); + } + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/HTTPEventAdapter.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/HTTPEventAdapter.java new file mode 100644 index 000000000..1bf79aca6 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/HTTPEventAdapter.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * 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.wso2.carbon.device.mgt.iot.input.adapter.http; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.service.http.HttpService; +import org.osgi.service.http.NamespaceException; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.iot.input.adapter.internal.EventAdapterServiceDataHolder; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapter; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterConfiguration; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterListener; +import org.wso2.carbon.event.input.adapter.core.exception.InputEventAdapterException; +import org.wso2.carbon.event.input.adapter.core.exception.InputEventAdapterRuntimeException; +import org.wso2.carbon.event.input.adapter.core.exception.TestConnectionNotSupportedException; +import org.wso2.carbon.device.mgt.iot.input.adapter.http.util.HTTPEventAdapterConstants; +import org.wso2.carbon.utils.multitenancy.MultitenantConstants; + +import javax.servlet.ServletException; +import java.util.Hashtable; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public final class HTTPEventAdapter implements InputEventAdapter { + + private final InputEventAdapterConfiguration eventAdapterConfiguration; + private final Map globalProperties; + private InputEventAdapterListener eventAdaptorListener; + private final String id = UUID.randomUUID().toString(); + public static ExecutorService executorService; + private static final Log log = LogFactory.getLog(HTTPEventAdapter.class); + private boolean isConnected = false; + + public HTTPEventAdapter(InputEventAdapterConfiguration eventAdapterConfiguration, + Map globalProperties) { + this.eventAdapterConfiguration = eventAdapterConfiguration; + this.globalProperties = globalProperties; + } + + @Override + public void init(InputEventAdapterListener eventAdaptorListener) throws InputEventAdapterException { + this.eventAdaptorListener = eventAdaptorListener; + + //ThreadPoolExecutor will be assigned if it is null + if (executorService == null) { + int minThread; + int maxThread; + long defaultKeepAliveTime; + int jobQueueSize; + + //If global properties are available those will be assigned else constant values will be assigned + if (globalProperties.get(HTTPEventAdapterConstants.ADAPTER_MIN_THREAD_POOL_SIZE_NAME) != null) { + minThread = Integer + .parseInt(globalProperties.get(HTTPEventAdapterConstants.ADAPTER_MIN_THREAD_POOL_SIZE_NAME)); + } else { + minThread = HTTPEventAdapterConstants.ADAPTER_MIN_THREAD_POOL_SIZE; + } + + if (globalProperties.get(HTTPEventAdapterConstants.ADAPTER_MAX_THREAD_POOL_SIZE_NAME) != null) { + maxThread = Integer + .parseInt(globalProperties.get(HTTPEventAdapterConstants.ADAPTER_MAX_THREAD_POOL_SIZE_NAME)); + } else { + maxThread = HTTPEventAdapterConstants.ADAPTER_MAX_THREAD_POOL_SIZE; + } + + if (globalProperties.get(HTTPEventAdapterConstants.ADAPTER_KEEP_ALIVE_TIME_NAME) != null) { + defaultKeepAliveTime = Integer + .parseInt(globalProperties.get(HTTPEventAdapterConstants.ADAPTER_KEEP_ALIVE_TIME_NAME)); + } else { + defaultKeepAliveTime = HTTPEventAdapterConstants.DEFAULT_KEEP_ALIVE_TIME_IN_MILLS; + } + + if (globalProperties.get(HTTPEventAdapterConstants.ADAPTER_EXECUTOR_JOB_QUEUE_SIZE_NAME) != null) { + jobQueueSize = Integer + .parseInt(globalProperties.get(HTTPEventAdapterConstants.ADAPTER_EXECUTOR_JOB_QUEUE_SIZE_NAME)); + } else { + jobQueueSize = HTTPEventAdapterConstants.ADAPTER_EXECUTOR_JOB_QUEUE_SIZE; + } + + RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() { + @Override + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { + try { + executor.getQueue().put(r); + } catch (InterruptedException e) { + log.error("Exception while adding event to executor queue : " + e.getMessage(), e); + } + } + + }; + + executorService = new ThreadPoolExecutor(minThread, maxThread, defaultKeepAliveTime, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue(jobQueueSize), rejectedExecutionHandler); + + } + } + + @Override + public void testConnect() throws TestConnectionNotSupportedException { + throw new TestConnectionNotSupportedException("not-supported"); + } + + @Override + public void connect() { + registerDynamicEndpoint(eventAdapterConfiguration.getName()); + isConnected = true; + } + + @Override + public void disconnect() { + if (isConnected){ + isConnected = false; + unregisterDynamicEndpoint(eventAdapterConfiguration.getName()); + } + } + + @Override + public void destroy() { + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof HTTPEventAdapter)) + return false; + + HTTPEventAdapter that = (HTTPEventAdapter) o; + + return id.equals(that.id); + + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean isEventDuplicatedInCluster() { + return false; + } + + @Override + public boolean isPolling() { + return false; + } + + private void registerDynamicEndpoint(String adapterName) { + + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + + String endpoint; + if (MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) { + endpoint = HTTPEventAdapterConstants.ENDPOINT_PREFIX + adapterName; + } else { + endpoint = HTTPEventAdapterConstants.ENDPOINT_PREFIX + HTTPEventAdapterConstants.ENDPOINT_TENANT_KEY + + HTTPEventAdapterConstants.ENDPOINT_URL_SEPARATOR + tenantDomain + + HTTPEventAdapterConstants.ENDPOINT_URL_SEPARATOR + adapterName; + } + + try { + HttpService httpService = EventAdapterServiceDataHolder.getHTTPService(); + if (httpService == null) { + throw new InputEventAdapterRuntimeException( + "HttpService not available, Error in registering endpoint " + endpoint); + } + httpService.registerServlet(endpoint, new HTTPMessageServlet(eventAdaptorListener, tenantId, + eventAdapterConfiguration), + new Hashtable(), httpService.createDefaultHttpContext()); + } catch (ServletException | NamespaceException e) { + throw new InputEventAdapterRuntimeException("Error in registering endpoint " + endpoint, e); + } + + } + + private void unregisterDynamicEndpoint(String adapterName) { + HttpService httpService = EventAdapterServiceDataHolder.getHTTPService(); + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + String endpoint; + if (MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) { + endpoint = HTTPEventAdapterConstants.ENDPOINT_PREFIX + adapterName; + } else { + endpoint = HTTPEventAdapterConstants.ENDPOINT_PREFIX + HTTPEventAdapterConstants.ENDPOINT_TENANT_KEY + + HTTPEventAdapterConstants.ENDPOINT_URL_SEPARATOR + tenantDomain + + HTTPEventAdapterConstants.ENDPOINT_URL_SEPARATOR + adapterName; + } + if (httpService != null) { + httpService.unregister(endpoint); + } + + } +} \ No newline at end of file diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/HTTPEventAdapterFactory.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/HTTPEventAdapterFactory.java new file mode 100644 index 000000000..6730c9a09 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/HTTPEventAdapterFactory.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2005 - 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * 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.wso2.carbon.device.mgt.iot.input.adapter.http; + + +import org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.Constants; +import org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.util.MQTTEventAdapterConstants; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapter; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterConfiguration; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterFactory; +import org.wso2.carbon.event.input.adapter.core.MessageType; +import org.wso2.carbon.event.input.adapter.core.Property; +import org.wso2.carbon.device.mgt.iot.input.adapter.http.util.HTTPEventAdapterConstants; +import org.wso2.carbon.utils.CarbonUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * The http event adapter factory class to create a http input adapter + */ +public class HTTPEventAdapterFactory extends InputEventAdapterFactory { + + private ResourceBundle resourceBundle = + ResourceBundle.getBundle("org.wso2.carbon.device.mgt.iot.input.adapter.http.i18n.Resources", Locale.getDefault()); + private int httpPort; + private int httpsPort; + private int portOffset; + + public HTTPEventAdapterFactory() { + portOffset = getPortOffset(); + httpPort = HTTPEventAdapterConstants.DEFAULT_HTTP_PORT + portOffset; + httpsPort = HTTPEventAdapterConstants.DEFAULT_HTTPS_PORT + portOffset; + } + + @Override + public String getType() { + return HTTPEventAdapterConstants.ADAPTER_TYPE_HTTP; + } + + @Override + public List getSupportedMessageFormats() { + List supportInputMessageTypes = new ArrayList(); + supportInputMessageTypes.add(MessageType.JSON); + supportInputMessageTypes.add(MessageType.TEXT); + supportInputMessageTypes.add(MessageType.XML); + supportInputMessageTypes.add(MessageType.WSO2EVENT); + return supportInputMessageTypes; + } + + @Override + public List getPropertyList() { + + List propertyList = new ArrayList(); + + // Transport Exposed + Property exposedTransportsProperty = new Property(HTTPEventAdapterConstants.EXPOSED_TRANSPORTS); + exposedTransportsProperty.setRequired(true); + exposedTransportsProperty.setDisplayName( + resourceBundle.getString(HTTPEventAdapterConstants.EXPOSED_TRANSPORTS)); + exposedTransportsProperty.setOptions( + new String[]{HTTPEventAdapterConstants.HTTPS, HTTPEventAdapterConstants.HTTP, + HTTPEventAdapterConstants.LOCAL, HTTPEventAdapterConstants.ALL}); + exposedTransportsProperty.setDefaultValue(HTTPEventAdapterConstants.ALL); + propertyList.add(exposedTransportsProperty); + + // OAUTH validation endpoint admin service username + Property username = new Property(HTTPEventAdapterConstants.USERNAME); + username.setRequired(true); + username.setDisplayName(resourceBundle.getString(HTTPEventAdapterConstants.USERNAME)); + username.setHint(resourceBundle.getString(HTTPEventAdapterConstants.USERNAME_HINT)); + propertyList.add(username); + + // OAUTH validation endpoint admin service password + Property password = new Property(HTTPEventAdapterConstants.PASSWORD); + password.setRequired(true); + password.setDisplayName(resourceBundle.getString(HTTPEventAdapterConstants.PASSWORD)); + password.setHint(resourceBundle.getString(HTTPEventAdapterConstants.PASSWORD_HINT)); + propertyList.add(password); + + // OAUTH validation endpoint + Property tokenValidationEndpoint = new Property(HTTPEventAdapterConstants.TOKEN_VALIDATION_ENDPOINT_URL); + tokenValidationEndpoint.setRequired(true); + tokenValidationEndpoint.setDisplayName(resourceBundle.getString(HTTPEventAdapterConstants.TOKEN_VALIDATION_ENDPOINT_URL)); + tokenValidationEndpoint.setHint(resourceBundle.getString(HTTPEventAdapterConstants.TOKEN_VALIDATION_ENDPOINT_URL_HINT)); + propertyList.add(tokenValidationEndpoint); + + Property maximumHttpConnectionPerHost = new Property(HTTPEventAdapterConstants.MAXIMUM_HTTP_CONNECTION_PER_HOST); + maximumHttpConnectionPerHost.setRequired(true); + maximumHttpConnectionPerHost.setDisplayName(resourceBundle.getString( + HTTPEventAdapterConstants.MAXIMUM_HTTP_CONNECTION_PER_HOST)); + maximumHttpConnectionPerHost.setHint(resourceBundle.getString( + HTTPEventAdapterConstants.MAXIMUM_HTTP_CONNECTION_PER_HOST_HINT)); + maximumHttpConnectionPerHost.setDefaultValue(HTTPEventAdapterConstants.MAX_HTTP_CONNECTION); + propertyList.add(maximumHttpConnectionPerHost); + + Property maxTotalHttpConnection = new Property(HTTPEventAdapterConstants.MAXIMUM_TOTAL_HTTP_CONNECTION); + maxTotalHttpConnection.setRequired(true); + maxTotalHttpConnection.setDisplayName(resourceBundle.getString( + HTTPEventAdapterConstants.MAXIMUM_TOTAL_HTTP_CONNECTION)); + maxTotalHttpConnection.setHint(resourceBundle.getString( + HTTPEventAdapterConstants.MAXIMUM_TOTAL_HTTP_CONNECTION_HINT)); + maxTotalHttpConnection.setDefaultValue(HTTPEventAdapterConstants.MAX_TOTAL_HTTP_CONNECTION); + propertyList.add(maxTotalHttpConnection); + + //Content Validator details + Property contentValidator = new Property(HTTPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME); + contentValidator.setDisplayName( + resourceBundle.getString(HTTPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME)); + contentValidator.setRequired(false); + contentValidator.setHint( + resourceBundle.getString(HTTPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME_HINT)); + contentValidator.setDefaultValue(HTTPEventAdapterConstants.DEFAULT); + propertyList.add(contentValidator); + + //Content Validator Params details + Property contentValidatorParams = new Property(HTTPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS); + contentValidatorParams.setDisplayName( + resourceBundle.getString(HTTPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS)); + contentValidatorParams.setRequired(false); + contentValidatorParams.setHint( + resourceBundle.getString(HTTPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS_HINT)); + contentValidatorParams.setDefaultValue(HTTPEventAdapterConstants.MQTT_CONTENT_VALIDATION_DEFAULT_PARAMETERS); + propertyList.add(contentValidatorParams); + + //Content Transformer details + Property contentTransformer = new Property(HTTPEventAdapterConstants.ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME); + contentTransformer.setDisplayName( + resourceBundle.getString(HTTPEventAdapterConstants.ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME)); + contentTransformer.setRequired(false); + contentTransformer.setHint( + resourceBundle.getString(HTTPEventAdapterConstants.ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME_HINT)); + contentTransformer.setDefaultValue(Constants.DEFAULT); + propertyList.add(contentTransformer); + return propertyList; + } + + @Override + public String getUsageTips() { + return resourceBundle.getString(HTTPEventAdapterConstants.ADAPTER_USAGE_TIPS_PREFIX) + httpPort + + resourceBundle.getString(HTTPEventAdapterConstants.ADAPTER_USAGE_TIPS_MID1) + httpsPort + + resourceBundle.getString(HTTPEventAdapterConstants.ADAPTER_USAGE_TIPS_MID2) + httpPort + + resourceBundle.getString(HTTPEventAdapterConstants.ADAPTER_USAGE_TIPS_MID3) + httpsPort + + resourceBundle.getString(HTTPEventAdapterConstants.ADAPTER_USAGE_TIPS_POSTFIX); + } + + @Override + public InputEventAdapter createEventAdapter(InputEventAdapterConfiguration eventAdapterConfiguration, + Map globalProperties) { + return new HTTPEventAdapter(eventAdapterConfiguration, globalProperties); + } + + private int getPortOffset() { + return CarbonUtils.getPortFromServerConfig(HTTPEventAdapterConstants.CARBON_CONFIG_PORT_OFFSET_NODE) + 1; + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/HTTPMessageServlet.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/HTTPMessageServlet.java new file mode 100644 index 000000000..90934de30 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/HTTPMessageServlet.java @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2005 - 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * 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.wso2.carbon.device.mgt.iot.input.adapter.http; + +import org.apache.axis2.context.ServiceContext; +import org.apache.axis2.transport.http.HTTPConstants; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.pool.impl.GenericObjectPool; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.iot.input.adapter.ContentInfo; +import org.wso2.carbon.device.mgt.iot.input.adapter.ContentTransformer; +import org.wso2.carbon.device.mgt.iot.input.adapter.DefaultContentTransformer; +import org.wso2.carbon.device.mgt.iot.input.adapter.DefaultContentValidator; +import org.wso2.carbon.device.mgt.iot.input.adapter.http.exception.HTTPContentInitializationException; +import org.wso2.carbon.device.mgt.iot.input.adapter.http.oauth.OAuthTokenValidaterStubFactory; +import org.wso2.carbon.device.mgt.iot.input.adapter.http.util.AuthenticationInfo; +import org.wso2.carbon.device.mgt.iot.input.adapter.http.util.HTTPEventAdapterConstants; +import org.wso2.carbon.device.mgt.iot.input.adapter.internal.EventAdapterServiceDataHolder; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterConfiguration; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterListener; +import org.wso2.carbon.device.mgt.iot.input.adapter.ContentValidator; +import org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.exception.MQTTContentInitializationException; +import org.wso2.carbon.identity.oauth2.stub.OAuth2TokenValidationServiceStub; +import org.wso2.carbon.identity.oauth2.stub.dto.OAuth2TokenValidationRequestDTO; +import org.wso2.carbon.identity.oauth2.stub.dto.OAuth2TokenValidationRequestDTO_OAuth2AccessToken; +import org.wso2.carbon.identity.oauth2.stub.dto.OAuth2TokenValidationResponseDTO; +import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.user.core.service.RealmService; +import org.wso2.carbon.utils.multitenancy.MultitenantUtils; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.rmi.RemoteException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * This will act as the event reciver. + */ +public class HTTPMessageServlet extends HttpServlet { + + private static final String AUTHORIZATION_HEADER = "Authorization"; + private static final String AUTH_MESSAGE_STORE_AUTHENTICATION_INFO = "AUTH_MESSAGE_STORE_AUTHENTICATION_INFO"; + private static final String AUTH_FAILURE_RESPONSE = "_AUTH_FAILURE_"; + private static final Pattern PATTERN = Pattern.compile("[B|b]earer\\s"); + private static final String TOKEN_TYPE = "bearer"; + private static String cookie; + private static Log log = LogFactory.getLog(HTTPMessageServlet.class); + private GenericObjectPool stubs; + private static Map contentValidationProperties; + private static ContentValidator contentValidator; + private static ContentTransformer contentTransformer; + private InputEventAdapterListener eventAdaptorListener; + private int tenantId; + private String exposedTransports; + + public HTTPMessageServlet(InputEventAdapterListener eventAdaptorListener, int tenantId, + InputEventAdapterConfiguration eventAdapterConfiguration) { + this.eventAdaptorListener = eventAdaptorListener; + this.tenantId = tenantId; + this.exposedTransports = eventAdapterConfiguration.getProperties().get( + HTTPEventAdapterConstants.EXPOSED_TRANSPORTS); + this.stubs = new GenericObjectPool(new OAuthTokenValidaterStubFactory(eventAdapterConfiguration)); + HTTPMessageServlet.contentValidationProperties = new HashMap<>(); + String contentValidationParams = eventAdapterConfiguration.getProperties().get( + HTTPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS); + if (contentValidationParams != null && !contentValidationParams.isEmpty()) { + String validationParams[] = contentValidationParams.split(","); + for (String validationParam : validationParams) { + String[] validationProperty = validationParam.split(":"); + if (validationProperty.length == 2) { + contentValidationProperties.put(validationProperty[0], validationProperty[1]); + } + } + } + + String className = eventAdapterConfiguration.getProperties().get( + HTTPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME); + if (HTTPEventAdapterConstants.DEFAULT.equals(className)) { + contentValidator = new DefaultContentValidator(); + } else { + try { + Class contentValidatorClass = Class.forName(className) + .asSubclass(ContentValidator.class); + contentValidator = contentValidatorClass.newInstance(); + } catch (ClassNotFoundException e) { + throw new HTTPContentInitializationException( + "Unable to find the class validator: " + className, e); + } catch (InstantiationException e) { + throw new HTTPContentInitializationException( + "Unable to create an instance of :" + className, e); + } catch (IllegalAccessException e) { + throw new HTTPContentInitializationException("Access of the instance in not allowed.", e); + } + } + + String contentTransformerClassName = eventAdapterConfiguration.getProperties().get( + HTTPEventAdapterConstants.ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME); + if (contentTransformerClassName != null && contentTransformerClassName.equals(HTTPEventAdapterConstants.DEFAULT)) { + contentTransformer = new DefaultContentTransformer(); + } else if (contentTransformerClassName != null && !contentTransformerClassName.isEmpty()) { + try { + Class contentTransformerClass = Class.forName(contentTransformerClassName) + .asSubclass(ContentTransformer.class); + contentTransformer = contentTransformerClass.newInstance(); + } catch (ClassNotFoundException e) { + throw new HTTPContentInitializationException( + "Unable to find the class transformer: " + contentTransformerClassName, e); + } catch (InstantiationException e) { + throw new HTTPContentInitializationException( + "Unable to create an instance of :" + contentTransformerClassName, e); + } catch (IllegalAccessException e) { + throw new HTTPContentInitializationException("Access of the instance in not allowed.", e); + } + } + } + + private String getBearerToken(HttpServletRequest request) { + String authorizationHeader = request.getHeader(AUTHORIZATION_HEADER); + if (authorizationHeader != null) { + Matcher matcher = PATTERN.matcher(authorizationHeader); + if (matcher.find()) { + authorizationHeader = authorizationHeader.substring(matcher.end()); + } + } + return authorizationHeader; + } + + private AuthenticationInfo checkAuthentication(HttpServletRequest req) { + AuthenticationInfo authenticationInfo = (AuthenticationInfo) req.getSession().getAttribute( + AUTH_MESSAGE_STORE_AUTHENTICATION_INFO); + if (authenticationInfo != null) { + return authenticationInfo; + } + String bearerToken = getBearerToken(req); + if (bearerToken == null) { + return authenticationInfo; + } + try { + authenticationInfo = validateToken(bearerToken); + boolean success = authenticationInfo.isAuthenticated(); + if (success) { + req.getSession().setAttribute(AUTH_MESSAGE_STORE_AUTHENTICATION_INFO, authenticationInfo); + } + } catch (Exception e) { + if (log.isDebugEnabled()) { + log.debug("checkAuthentication() fail: " + e.getMessage(), e); + } + } + return authenticationInfo; + } + + /** + * This method gets a string accessToken and validates it + * + * @param token which need to be validated. + * @return AuthenticationInfo with the validated results. + */ + private AuthenticationInfo validateToken(String token) { + OAuth2TokenValidationServiceStub tokenValidationServiceStub = null; + try { + Object stub = this.stubs.borrowObject(); + if (stub != null) { + tokenValidationServiceStub = (OAuth2TokenValidationServiceStub) stub; + if (cookie != null) { + tokenValidationServiceStub._getServiceClient().getOptions().setProperty( + HTTPConstants.COOKIE_STRING, cookie); + } + return getAuthenticationInfo(token, tokenValidationServiceStub); + } else { + log.warn("Stub initialization failed."); + } + } catch (RemoteException e) { + log.error("Error on connecting with the validation endpoint.", e); + } catch (Exception e) { + log.error("Error occurred in borrowing an validation stub from the pool.", e); + + } finally { + try { + if (tokenValidationServiceStub != null) { + this.stubs.returnObject(tokenValidationServiceStub); + } + } catch (Exception e) { + log.warn("Error occurred while returning the object back to the oauth token validation service " + + "stub pool.", e); + } + } + AuthenticationInfo authenticationInfo = new AuthenticationInfo(); + authenticationInfo.setAuthenticated(false); + authenticationInfo.setTenantId(-1); + return authenticationInfo; + } + + /** + * This creates an AuthenticationInfo object that is used for authorization. This method will validate the token + * and + * sets the required parameters to the object. + * + * @param token that needs to be validated. + * @param tokenValidationServiceStub stub that is used to call the external service. + * @return AuthenticationInfo This contains the information related to authenticated client. + * @throws RemoteException that triggers when failing to call the external service.. + */ + private AuthenticationInfo getAuthenticationInfo(String token, + OAuth2TokenValidationServiceStub tokenValidationServiceStub) + throws RemoteException, UserStoreException { + AuthenticationInfo authenticationInfo = new AuthenticationInfo(); + OAuth2TokenValidationRequestDTO validationRequest = new OAuth2TokenValidationRequestDTO(); + OAuth2TokenValidationRequestDTO_OAuth2AccessToken accessToken = + new OAuth2TokenValidationRequestDTO_OAuth2AccessToken(); + accessToken.setTokenType(TOKEN_TYPE); + accessToken.setIdentifier(token); + validationRequest.setAccessToken(accessToken); + boolean authenticated; + OAuth2TokenValidationResponseDTO tokenValidationResponse; + tokenValidationResponse = tokenValidationServiceStub.validate(validationRequest); + if (tokenValidationResponse == null) { + authenticationInfo.setAuthenticated(false); + return authenticationInfo; + } + authenticated = tokenValidationResponse.getValid(); + if (authenticated) { + String authorizedUser = tokenValidationResponse.getAuthorizedUser(); + String username = MultitenantUtils.getTenantAwareUsername(authorizedUser); + String tenantDomain = MultitenantUtils.getTenantDomain(authorizedUser); + authenticationInfo.setUsername(username); + authenticationInfo.setTenantDomain(tenantDomain); + RealmService realmService = EventAdapterServiceDataHolder.getRealmService(); + int tenantId = realmService.getTenantManager().getTenantId(authenticationInfo.getTenantDomain()); + authenticationInfo.setTenantId(tenantId); + } else { + if (log.isDebugEnabled()) { + log.debug("Token validation failed for token: " + token); + } + } + ServiceContext serviceContext = tokenValidationServiceStub._getServiceClient() + .getLastOperationContext().getServiceContext(); + cookie = (String) serviceContext.getProperty(HTTPConstants.COOKIE_STRING); + authenticationInfo.setAuthenticated(authenticated); + return authenticationInfo; + } + + + private String inputStreamToString(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buff = new byte[1024]; + int i; + while ((i = in.read(buff)) > 0) { + out.write(buff, 0, i); + } + out.close(); + return out.toString(); + } + + @Override + protected void doPost(HttpServletRequest req, + HttpServletResponse res) throws IOException { + + String data = this.inputStreamToString(req.getInputStream()); + if (data == null) { + log.warn("Event Object is empty/null"); + return; + } + AuthenticationInfo authenticationInfo = null; + if (exposedTransports.equalsIgnoreCase(HTTPEventAdapterConstants.HTTPS)) { + if (!req.isSecure()) { + res.setStatus(403); + log.error("Only Secured endpoint is enabled for requests"); + return; + } else { + authenticationInfo = this.checkAuthentication(req); + int tenantId = authenticationInfo != null ? authenticationInfo.getTenantId() : -1; + if (tenantId == -1) { + res.getOutputStream().write(AUTH_FAILURE_RESPONSE.getBytes()); + res.setStatus(401); + log.error("Authentication failed for the request"); + return; + } else if (tenantId != this.tenantId) { + res.getOutputStream().write(AUTH_FAILURE_RESPONSE.getBytes()); + res.setStatus(401); + log.error("Authentication failed for the request"); + return; + } + } + } else if (exposedTransports.equalsIgnoreCase(HTTPEventAdapterConstants.HTTP)) { + if (req.isSecure()) { + res.setStatus(403); + log.error("Only unsecured endpoint is enabled for requests"); + return; + } + } else { + authenticationInfo = this.checkAuthentication(req); + int tenantId = authenticationInfo != null ? authenticationInfo.getTenantId() : -1; + if (tenantId == -1) { + res.getOutputStream().write(AUTH_FAILURE_RESPONSE.getBytes()); + res.setStatus(401); + log.error("Authentication failed for the request"); + return; + } else if (tenantId != this.tenantId) { + res.getOutputStream().write(AUTH_FAILURE_RESPONSE.getBytes()); + res.setStatus(401); + log.error("Authentication failed for the request"); + return; + } + } + + if (log.isDebugEnabled()) { + log.debug("Message : " + data); + } + + if (authenticationInfo != null) { + Map paramMap = new HashMap<>(); + Enumeration reqParameterNames = req.getParameterNames(); + while (reqParameterNames.hasMoreElements()) { + String paramterName = reqParameterNames.nextElement(); + paramMap.put(paramterName, req.getParameter(paramterName)); + } + paramMap.put(HTTPEventAdapterConstants.USERNAME_TAG, authenticationInfo.getUsername()); + paramMap.put(HTTPEventAdapterConstants.TENANT_DOMAIN_TAG, authenticationInfo.getTenantDomain()); + if (contentValidator != null && contentTransformer != null) { + data = contentTransformer.transform(data, paramMap); + ContentInfo contentInfo = contentValidator.validate(data, contentValidationProperties, paramMap); + if (contentInfo != null && contentInfo.isValidContent()) { + HTTPEventAdapter.executorService.submit(new HTTPRequestProcessor(eventAdaptorListener, + contentInfo.getMsgText(), tenantId)); + } + } + } + } + + @Override + protected void doGet(HttpServletRequest req, + HttpServletResponse res) throws IOException { + doPost(req, res); + } + + public class HTTPRequestProcessor implements Runnable { + + private InputEventAdapterListener inputEventAdapterListener; + private String payload; + private int tenantId; + + public HTTPRequestProcessor(InputEventAdapterListener inputEventAdapterListener, + String payload, int tenantId) { + this.inputEventAdapterListener = inputEventAdapterListener; + this.payload = payload; + this.tenantId = tenantId; + } + + public void run() { + try { + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId); + + if (log.isDebugEnabled()) { + log.debug("Event received in HTTP Event Adapter - " + payload); + } + + if (payload.trim() != null) { + inputEventAdapterListener.onEvent(payload); + } else { + log.warn("Dropping the empty/null event received through http adapter"); + } + } catch (Exception e) { + log.error("Error while parsing http request for processing: " + e.getMessage(), e); + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + } + + } + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/exception/HTTPContentInitializationException.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/exception/HTTPContentInitializationException.java new file mode 100644 index 000000000..3b30ed4b8 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/exception/HTTPContentInitializationException.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, 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.device.mgt.iot.input.adapter.http.exception; + +/** + * This exception will thrown when content validator is failed to intialiaze. + */ +public class HTTPContentInitializationException extends RuntimeException { + private String errMessage; + + public HTTPContentInitializationException(String msg, Exception nestedEx) { + super(msg, nestedEx); + setErrorMessage(msg); + } + + public HTTPContentInitializationException(String message, Throwable cause) { + super(message, cause); + setErrorMessage(message); + } + + public HTTPContentInitializationException(String msg) { + super(msg); + setErrorMessage(msg); + } + + public HTTPContentInitializationException() { + super(); + } + + public HTTPContentInitializationException(Throwable cause) { + super(cause); + } + + public String getErrorMessage() { + return errMessage; + } + + public void setErrorMessage(String errMessage) { + this.errMessage = errMessage; + } +} \ No newline at end of file diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/oauth/OAuthTokenValidaterStubFactory.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/oauth/OAuthTokenValidaterStubFactory.java new file mode 100644 index 000000000..101f04620 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/oauth/OAuthTokenValidaterStubFactory.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2016, 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.device.mgt.iot.input.adapter.http.oauth; + +import org.apache.axis2.AxisFault; +import org.apache.axis2.Constants; +import org.apache.axis2.client.Options; +import org.apache.axis2.client.ServiceClient; +import org.apache.axis2.transport.http.HTTPConstants; +import org.apache.axis2.transport.http.HttpTransportProperties; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpConnectionManager; +import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; +import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory; +import org.apache.commons.httpclient.params.HttpConnectionManagerParams; +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; +import org.apache.commons.pool.BasePoolableObjectFactory; +import org.apache.log4j.Logger; +import org.wso2.carbon.core.util.Utils; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterConfiguration; +import org.wso2.carbon.device.mgt.iot.input.adapter.http.oauth.exception.OAuthTokenValidationException; +import org.wso2.carbon.device.mgt.iot.input.adapter.http.util.HTTPEventAdapterConstants; +import org.wso2.carbon.identity.oauth2.stub.OAuth2TokenValidationServiceStub; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.GeneralSecurityException; + +/** + * This follows object pool pattern to manage the stub for oauth validation service. + */ +public class OAuthTokenValidaterStubFactory extends BasePoolableObjectFactory { + private static final Logger log = Logger.getLogger(OAuthTokenValidaterStubFactory.class); + private HttpClient httpClient; + InputEventAdapterConfiguration eventAdapterConfiguration; + + + public OAuthTokenValidaterStubFactory(InputEventAdapterConfiguration eventAdapterConfiguration) { + this.eventAdapterConfiguration = eventAdapterConfiguration; + this.httpClient = createHttpClient(); + } + + /** + * This creates a OAuth2TokenValidationServiceStub object to the pool. + * + * @return an OAuthValidationStub object + * @throws Exception thrown when creating the object. + */ + @Override + public Object makeObject() throws Exception { + return this.generateStub(); + } + + /** + * This is used to clean up the OAuth validation stub and releases to the object pool. + * + * @param o object that needs to be released. + * @throws Exception throws when failed to release to the pool + */ + @Override + public void passivateObject(Object o) throws Exception { + if (o instanceof OAuth2TokenValidationServiceStub) { + OAuth2TokenValidationServiceStub stub = (OAuth2TokenValidationServiceStub) o; + stub._getServiceClient().cleanupTransport(); + } + } + + /** + * This is used to create a stub which will be triggered through object pool factory, which will create an + * instance of it. + * + * @return OAuth2TokenValidationServiceStub stub that is used to call an external service. + * @throws OAuthTokenValidationException will be thrown when initialization failed. + */ + private OAuth2TokenValidationServiceStub generateStub() throws OAuthTokenValidationException { + OAuth2TokenValidationServiceStub stub; + try { + URL hostURL = new URL(Utils.replaceSystemProperty(eventAdapterConfiguration.getProperties().get( + HTTPEventAdapterConstants.TOKEN_VALIDATION_ENDPOINT_URL))); + if (hostURL != null) { + stub = new OAuth2TokenValidationServiceStub(hostURL.toString()); + if (stub != null) { + ServiceClient client = stub._getServiceClient(); + client.getServiceContext().getConfigurationContext().setProperty( + HTTPConstants.CACHED_HTTP_CLIENT, httpClient); + + HttpTransportProperties.Authenticator auth = + new HttpTransportProperties.Authenticator(); + auth.setPreemptiveAuthentication(true); + String username = eventAdapterConfiguration.getProperties().get(HTTPEventAdapterConstants + .USERNAME); + String password = eventAdapterConfiguration.getProperties().get(HTTPEventAdapterConstants + .PASSWORD); + auth.setPassword(username); + auth.setUsername(password); + Options options = client.getOptions(); + options.setProperty(HTTPConstants.AUTHENTICATE, auth); + options.setProperty(HTTPConstants.REUSE_HTTP_CLIENT, Constants.VALUE_TRUE); + client.setOptions(options); + if (hostURL.getProtocol().equals("https")) { + // set up ssl factory since axis2 https transport is used. + EasySSLProtocolSocketFactory sslProtocolSocketFactory = + createProtocolSocketFactory(); + Protocol authhttps = new Protocol(hostURL.getProtocol(), + (ProtocolSocketFactory) sslProtocolSocketFactory, + hostURL.getPort()); + Protocol.registerProtocol(hostURL.getProtocol(), authhttps); + options.setProperty(HTTPConstants.CUSTOM_PROTOCOL_HANDLER, authhttps); + } + } else { + String errorMsg = "OAuth Validation instanization failed."; + throw new OAuthTokenValidationException(errorMsg); + } + } else { + String errorMsg = "host url is invalid"; + throw new OAuthTokenValidationException(errorMsg); + } + } catch (AxisFault axisFault) { + throw new OAuthTokenValidationException( + "Error occurred while creating the OAuth2TokenValidationServiceStub.", axisFault); + } catch (MalformedURLException e) { + throw new OAuthTokenValidationException( + "Error occurred while parsing token endpoint URL", e); + } + + return stub; + } + + /** + * This is required to create a trusted connection with the external entity. + * Have to manually configure it since we use CommonHTTPTransport(axis2 transport) in axis2. + * + * @return an EasySSLProtocolSocketFactory for SSL communication. + */ + private EasySSLProtocolSocketFactory createProtocolSocketFactory() throws OAuthTokenValidationException { + try { + EasySSLProtocolSocketFactory easySSLPSFactory = new EasySSLProtocolSocketFactory(); + return easySSLPSFactory; + } catch (IOException e) { + String errorMsg = "Failed to initiate EasySSLProtocolSocketFactory."; + throw new OAuthTokenValidationException(errorMsg, e); + } catch (GeneralSecurityException e) { + String errorMsg = "Failed to set the key material in easy ssl factory."; + throw new OAuthTokenValidationException(errorMsg, e); + } + } + + /** + * This created httpclient pool that can be used to connect to external entity. This connection can be configured + * via broker.xml by setting up the required http connection parameters. + * + * @return an instance of HttpClient that is configured with MultiThreadedHttpConnectionManager + */ + private HttpClient createHttpClient() { + HttpConnectionManagerParams params = new HttpConnectionManagerParams(); + params.setDefaultMaxConnectionsPerHost(Integer.parseInt(eventAdapterConfiguration.getProperties().get( + HTTPEventAdapterConstants.MAXIMUM_HTTP_CONNECTION_PER_HOST))); + params.setMaxTotalConnections(Integer.parseInt(eventAdapterConfiguration.getProperties().get( + HTTPEventAdapterConstants.MAXIMUM_TOTAL_HTTP_CONNECTION))); + HttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); + connectionManager.setParams(params); + return new HttpClient(connectionManager); + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/oauth/exception/OAuthTokenValidationException.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/oauth/exception/OAuthTokenValidationException.java new file mode 100644 index 000000000..95cfb7cca --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/oauth/exception/OAuthTokenValidationException.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, 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.device.mgt.iot.input.adapter.http.oauth.exception; + +/** + * This Exception will be thrown, when there any interference with token validation flow. + */ +public class OAuthTokenValidationException extends Exception { + private String errMessage; + + public OAuthTokenValidationException(String msg, Exception nestedEx) { + super(msg, nestedEx); + setErrorMessage(msg); + } + + public OAuthTokenValidationException(String message, Throwable cause) { + super(message, cause); + setErrorMessage(message); + } + + public OAuthTokenValidationException(String msg) { + super(msg); + setErrorMessage(msg); + } + + public OAuthTokenValidationException() { + super(); + } + + public OAuthTokenValidationException(Throwable cause) { + super(cause); + } + + public String getErrorMessage() { + return errMessage; + } + + public void setErrorMessage(String errMessage) { + this.errMessage = errMessage; + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/util/AuthenticationInfo.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/util/AuthenticationInfo.java new file mode 100644 index 000000000..8bcf62e23 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/util/AuthenticationInfo.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * 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.wso2.carbon.device.mgt.iot.input.adapter.http.util; + +/** + * This will be return after authentication and this will consist of the authenticated user info. + */ +public class AuthenticationInfo { + + /** + * this variable is used to check whether the client is authenticated. + */ + private boolean authenticated; + private String username; + private String tenantDomain; + private int tenantId; + /** + * returns whether the client is authenticated + */ + public boolean isAuthenticated() { + return authenticated; + } + + public void setAuthenticated(boolean authenticated) { + this.authenticated = authenticated; + } + + /** + * returns the authenticated client username + */ + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + /** + * return the authenticated client tenant domain + */ + public String getTenantDomain() { + return tenantDomain; + } + + public void setTenantDomain(String tenantDomain) { + this.tenantDomain = tenantDomain; + } + + public int getTenantId() { + return tenantId; + } + + public void setTenantId(int tenantId) { + this.tenantId = tenantId; + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/util/HTTPContentValidator.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/util/HTTPContentValidator.java new file mode 100644 index 000000000..fe872781d --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/util/HTTPContentValidator.java @@ -0,0 +1,77 @@ +/* +* Copyright (c) 2016, 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.device.mgt.iot.input.adapter.http.util; + +import com.jayway.jsonpath.JsonPath; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.simple.JSONArray; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.wso2.carbon.device.mgt.iot.input.adapter.ContentInfo; +import org.wso2.carbon.device.mgt.iot.input.adapter.ContentValidator; + +import java.util.Map; + +public class HTTPContentValidator implements ContentValidator { + private static final Log log = LogFactory.getLog(HTTPContentValidator.class); + private static String JSON_ARRAY_START_CHAR = "["; + + @Override + public ContentInfo validate(String msgPayload, Map contentValidationParams, + Map dynamicParams) { + String deviceId = dynamicParams.get("deviceId"); + String msg = msgPayload; + String deviceIdJsonPath = contentValidationParams.get(HTTPEventAdapterConstants.DEVICE_ID_JSON_PATH); + boolean status; + if (msg.startsWith(JSON_ARRAY_START_CHAR)) { + status = processMultipleEvents(msg, deviceId, deviceIdJsonPath); + } else { + status = processSingleEvent(msg, deviceId, deviceIdJsonPath); + } + return new ContentInfo(status, msg); + } + + private boolean processSingleEvent(String msg, String deviceIdFromTopic, String deviceIdJsonPath) { + Object res = JsonPath.read(msg, deviceIdJsonPath); + String deviceIdFromContent = (res != null) ? res.toString() : ""; + if (deviceIdFromContent.equals(deviceIdFromTopic)) { + return true; + } + return false; + } + + private boolean processMultipleEvents(String msg, String deviceIdFromTopic, String deviceIdJsonPath) { + try { + JSONParser jsonParser = new JSONParser(); + JSONArray jsonArray = (JSONArray) jsonParser.parse(msg); + boolean status = false; + for (int i = 0; i < jsonArray.size(); i++) { + status = processSingleEvent(jsonArray.get(i).toString(), deviceIdFromTopic, deviceIdJsonPath); + if (!status) { + return status; + } + } + return status; + } catch (ParseException e) { + log.error("Invalid input " + msg, e); + return false; + } + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/util/HTTPEventAdapterConstants.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/util/HTTPEventAdapterConstants.java new file mode 100644 index 000000000..e9b81ffff --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/http/util/HTTPEventAdapterConstants.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * 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.wso2.carbon.device.mgt.iot.input.adapter.http.util; + +/** + * This holds the constants related to HTTP event adapter. + */ +public final class HTTPEventAdapterConstants { + + private HTTPEventAdapterConstants() { + } + + public static final String ADAPTER_TYPE_HTTP = "oauth-http"; + public static final String ADAPTER_USAGE_TIPS_PREFIX = "http.usage.tips_prefix"; + public static final String ADAPTER_USAGE_TIPS_MID1 = "http.usage.tips_mid1"; + public static final String ADAPTER_USAGE_TIPS_MID2 = "http.usage.tips_mid2"; + public static final String ADAPTER_USAGE_TIPS_MID3 = "http.usage.tips_mid3"; + public static final String ADAPTER_USAGE_TIPS_POSTFIX = "http.usage.tips_postfix"; + public static final int ADAPTER_MIN_THREAD_POOL_SIZE = 8; + public static final int ADAPTER_MAX_THREAD_POOL_SIZE = 100; + public static final int ADAPTER_EXECUTOR_JOB_QUEUE_SIZE = 10000; + public static final long DEFAULT_KEEP_ALIVE_TIME_IN_MILLS = 20000; + public static final String ENDPOINT_PREFIX = "/endpoints/"; + public static final String ENDPOINT_URL_SEPARATOR = "/"; + public static final String ENDPOINT_TENANT_KEY = "t"; + public static final String ADAPTER_MIN_THREAD_POOL_SIZE_NAME = "minThread"; + public static final String ADAPTER_MAX_THREAD_POOL_SIZE_NAME = "maxThread"; + public static final String ADAPTER_KEEP_ALIVE_TIME_NAME = "keepAliveTimeInMillis"; + public static final String ADAPTER_EXECUTOR_JOB_QUEUE_SIZE_NAME = "jobQueueSize"; + public static final String EXPOSED_TRANSPORTS = "transports"; + public static final String HTTPS = "https"; + public static final String HTTP = "http"; + public static final String LOCAL = "local"; + public static final String ALL = "all"; + public static final String CARBON_CONFIG_PORT_OFFSET_NODE = "Ports.Offset"; + public static final int DEFAULT_HTTP_PORT = 9763; + public static final int DEFAULT_HTTPS_PORT = 9443; + public static final String MAXIMUM_TOTAL_HTTP_CONNECTION = "maximumTotalHttpConnection"; + public static final String MAXIMUM_TOTAL_HTTP_CONNECTION_HINT = "maximumTotalHttpConnection.hint"; + public static final String MAXIMUM_HTTP_CONNECTION_PER_HOST = "maximumHttpConnectionPerHost"; + public static final String MAXIMUM_HTTP_CONNECTION_PER_HOST_HINT = "maximumHttpConnectionPerHost.hint"; + public static final String TOKEN_VALIDATION_ENDPOINT_URL = "tokenValidationEndpointUrl"; + public static final String TOKEN_VALIDATION_ENDPOINT_URL_HINT = "tokenValidationEndpointUrl.hint"; + public static final String USERNAME = "username"; + public static final String USERNAME_HINT = "username.hint"; + public static final String PASSWORD = "password"; + public static final String PASSWORD_HINT = "password.hint"; + public static final String DEFAULT_STRING = "default"; + public static final String MAX_HTTP_CONNECTION = "2"; + public static final String MAX_TOTAL_HTTP_CONNECTION = "100"; + public static final String TENANT_DOMAIN_TAG = "tenantDomain"; + public static final String USERNAME_TAG = "username"; + public static final String PAYLOAD_TAG = "payload"; + public static final String DEVICE_ID_JSON_PATH = "device_id_json_path"; + public static final String ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME = "contentValidation"; + public static final String ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME_HINT = "contentValidation.hint"; + public static final String ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS = "contentValidationParams"; + public static final String ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS_HINT = "contentValidationParams.hint"; + public static final String DEFAULT = "default"; + public static final String MQTT_CONTENT_VALIDATION_DEFAULT_PARAMETERS = + "device_id_json_path:meta_deviceId"; + public static final String ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME = "contentTransformer"; + public static final String ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME_HINT = "contentTransformer.hint"; +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/internal/EventAdapterServiceComponent.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/internal/EventAdapterServiceComponent.java new file mode 100644 index 000000000..6f101164e --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/internal/EventAdapterServiceComponent.java @@ -0,0 +1,81 @@ +/* +* 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.device.mgt.iot.input.adapter.internal; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.http.HttpService; +import org.wso2.carbon.device.mgt.iot.input.adapter.http.HTTPEventAdapterFactory; +import org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.MQTTEventAdapterFactory; +import org.wso2.carbon.device.mgt.iot.input.adapter.xmpp.XMPPEventAdapterFactory; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterFactory; +import org.wso2.carbon.user.core.service.RealmService; + +/** + * @scr.component component.name="input.iot.Mqtt.AdapterService.component" immediate="true" + */ + +/** + * @scr.component name="org.wso2.carbon.event.input.adapter.extension.EventAdapterServiceComponent" immediate="true" + * @scr.reference name="user.realmservice.default" + * interface="org.wso2.carbon.user.core.service.RealmService" cardinality="1..1" + * policy="dynamic" bind="setRealmService" unbind="unsetRealmService" + * @scr.reference name="http.service" interface="org.osgi.service.http.HttpService" + * cardinality="1..1" policy="dynamic" bind="setHttpService" unbind="unsetHttpService" + */ +public class EventAdapterServiceComponent { + + private static final Log log = LogFactory.getLog(EventAdapterServiceComponent.class); + + protected void activate(ComponentContext context) { + try { + InputEventAdapterFactory mqttEventAdapterFactory = new MQTTEventAdapterFactory(); + context.getBundleContext().registerService(InputEventAdapterFactory.class.getName(), + mqttEventAdapterFactory, null); + InputEventAdapterFactory httpEventEventAdapterFactory = new HTTPEventAdapterFactory(); + context.getBundleContext().registerService(InputEventAdapterFactory.class.getName(), + httpEventEventAdapterFactory, null); + InputEventAdapterFactory xmppEventEventAdapterFactory = new XMPPEventAdapterFactory(); + context.getBundleContext().registerService(InputEventAdapterFactory.class.getName(), + xmppEventEventAdapterFactory, null); + if (log.isDebugEnabled()) { + log.debug("Successfully deployed the input adapter service"); + } + } catch (RuntimeException e) { + log.error("Can not create the input adapter service ", e); + } + } + + protected void setRealmService(RealmService realmService) { + EventAdapterServiceDataHolder.registerRealmService(realmService); + } + + protected void unsetRealmService(RealmService realmService) { + EventAdapterServiceDataHolder.registerRealmService(null); + } + + protected void setHttpService(HttpService httpService) { + EventAdapterServiceDataHolder.registerHTTPService(httpService); + } + + protected void unsetHttpService(HttpService httpService) { + EventAdapterServiceDataHolder.registerHTTPService(null); + } + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/internal/EventAdapterServiceDataHolder.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/internal/EventAdapterServiceDataHolder.java new file mode 100644 index 000000000..cfa866e1b --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/internal/EventAdapterServiceDataHolder.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * 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.wso2.carbon.device.mgt.iot.input.adapter.internal; + +import org.osgi.service.http.HttpService; +import org.wso2.carbon.user.core.service.RealmService; + +/** + * common place to hold some OSGI service references. + */ +public final class EventAdapterServiceDataHolder { + + private static RealmService realmService; + private static HttpService httpService; + + private EventAdapterServiceDataHolder() { + } + + public static void registerRealmService( + RealmService realmService) { + EventAdapterServiceDataHolder.realmService = realmService; + } + + public static RealmService getRealmService() { + return realmService; + } + + public static void registerHTTPService( + HttpService httpService) { + EventAdapterServiceDataHolder.httpService = httpService; + } + + public static HttpService getHTTPService() { + return httpService; + } + + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/Constants.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/Constants.java new file mode 100644 index 000000000..c411c1193 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/Constants.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016, 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.device.mgt.iot.input.adapter.mqtt; + +/** + * This holds the constants related to MQTT input adapter. + */ +public class Constants { + public static final String EMPTY_STRING = ""; + public static final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer refresh_token"; + public static final String TOKEN_SCOPE = "production"; + public static final String APPLICATION_TYPE = "device"; + public static final String CLIENT_ID = "client_id"; + public static final String CLIENT_SECRET = "client_secret"; + public static final String CLIENT_NAME = "client_name"; + public static final String DEFAULT = "default"; + public static final String MQTT_CONTENT_VALIDATION_DEFAULT_PARAMETERS = + "device_id_json_path:event.metaData.deviceId,device_id_topic_hierarchy_index:2"; + public static final String TOPIC = "topic"; + public static final String PAYLOAD = "payload"; + public static final String DEVICE_ID_JSON_PATH = "device_id_json_path"; + public static final String DEVICE_ID_TOPIC_HIERARCHY_INDEX = "device_id_topic_hierarchy_index"; +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/MQTTEventAdapter.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/MQTTEventAdapter.java new file mode 100644 index 000000000..ba7070cce --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/MQTTEventAdapter.java @@ -0,0 +1,165 @@ +/* +* 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.device.mgt.iot.input.adapter.mqtt; + +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.util.MQTTBrokerConnectionConfiguration; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapter; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterConfiguration; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterListener; +import org.wso2.carbon.event.input.adapter.core.exception.InputEventAdapterException; +import org.wso2.carbon.event.input.adapter.core.exception.TestConnectionNotSupportedException; +import org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.util.MQTTAdapterListener; +import org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.util.MQTTEventAdapterConstants; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * Input XMPPEventAdapter will be used to receive events with MQTT protocol using specified broker and topic. + */ +public class MQTTEventAdapter implements InputEventAdapter { + + private final InputEventAdapterConfiguration eventAdapterConfiguration; + private final Map globalProperties; + private InputEventAdapterListener eventAdapterListener; + private final String id = UUID.randomUUID().toString(); + private MQTTAdapterListener mqttAdapterListener; + private MQTTBrokerConnectionConfiguration mqttBrokerConnectionConfiguration; + + + public MQTTEventAdapter(InputEventAdapterConfiguration eventAdapterConfiguration, + Map globalProperties) { + this.eventAdapterConfiguration = eventAdapterConfiguration; + this.globalProperties = globalProperties; + } + + @Override + public void init(InputEventAdapterListener eventAdapterListener) throws InputEventAdapterException { + this.eventAdapterListener = eventAdapterListener; + try { + int keepAlive; + + //If global properties are available those will be assigned else constant values will be assigned + if (globalProperties.get(MQTTEventAdapterConstants.ADAPTER_CONF_KEEP_ALIVE) != null) { + keepAlive = Integer.parseInt((globalProperties.get(MQTTEventAdapterConstants.ADAPTER_CONF_KEEP_ALIVE))); + } else { + keepAlive = MQTTEventAdapterConstants.ADAPTER_CONF_DEFAULT_KEEP_ALIVE; + } + String contentValidationParams = eventAdapterConfiguration.getProperties().get(MQTTEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS); + String params[] = contentValidationParams.split(","); + Map paramsMap = new HashMap<>(); + for (String param: params) { + String paramsKeyAndValue[] = splitOnFirst(param, ':'); + if (paramsKeyAndValue.length != 2) { + throw new InputEventAdapterException("Invalid parameters for content validation - " + param); + } + paramsMap.put(paramsKeyAndValue[0], paramsKeyAndValue[1]); + } + + mqttBrokerConnectionConfiguration = new MQTTBrokerConnectionConfiguration( + eventAdapterConfiguration.getProperties().get(MQTTEventAdapterConstants.ADAPTER_CONF_URL), + eventAdapterConfiguration.getProperties().get(MQTTEventAdapterConstants.ADAPTER_CONF_USERNAME), + eventAdapterConfiguration.getProperties().get(MQTTEventAdapterConstants.ADAPTER_CONF_SCOPES), + eventAdapterConfiguration.getProperties().get(MQTTEventAdapterConstants.ADAPTER_CONF_DCR_URL), + eventAdapterConfiguration.getProperties().get(MQTTEventAdapterConstants.ADAPTER_CONF_CLEAN_SESSION), + keepAlive, + eventAdapterConfiguration.getProperties().get(MQTTEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME), + paramsMap, + eventAdapterConfiguration.getProperties().get(MQTTEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME) + ); + mqttAdapterListener = new MQTTAdapterListener(mqttBrokerConnectionConfiguration, + eventAdapterConfiguration.getProperties().get(MQTTEventAdapterConstants.ADAPTER_MESSAGE_TOPIC), + eventAdapterConfiguration.getProperties().get(MQTTEventAdapterConstants.ADAPTER_CONF_CLIENTID), + eventAdapterListener, PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId()); + + } catch (Throwable t) { + throw new InputEventAdapterException(t.getMessage(), t); + } + } + + private String[] splitOnFirst(String str, char c) { + int idx = str.indexOf(c); + String head = str.substring(0, idx); + String tail = str.substring(idx + 1); + return new String[] { head, tail} ; + } + + @Override + public void testConnect() throws TestConnectionNotSupportedException { + throw new TestConnectionNotSupportedException("not-supported"); + } + + @Override + public void connect() { + mqttAdapterListener.createConnection(); + } + + @Override + public void disconnect() { + //when mqtt and this feature both together then this method becomes a blocking method, Therefore + // have used a thread to skip it. + try { + Thread thread = new Thread(new Runnable() { + public void run() { + if (mqttAdapterListener != null) { + mqttAdapterListener.stopListener(eventAdapterConfiguration.getName()); + } + } + }); + thread.start(); + thread.join(2000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + @Override + public void destroy() { + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MQTTEventAdapter)) return false; + + MQTTEventAdapter that = (MQTTEventAdapter) o; + + if (!id.equals(that.id)) return false; + + return true; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + + @Override + public boolean isEventDuplicatedInCluster() { + return true; + } + + @Override + public boolean isPolling() { + return true; + } + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/MQTTEventAdapterFactory.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/MQTTEventAdapterFactory.java new file mode 100644 index 000000000..ecff0323a --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/MQTTEventAdapterFactory.java @@ -0,0 +1,149 @@ +/* +* 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.device.mgt.iot.input.adapter.mqtt; + +import org.wso2.carbon.device.mgt.iot.input.adapter.xmpp.util.XMPPEventAdapterConstants; +import org.wso2.carbon.event.input.adapter.core.*; +import org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.util.MQTTEventAdapterConstants; + +import java.util.*; + +/** + * The mqtt event adapter factory class to create a mqtt input adapter + */ +public class MQTTEventAdapterFactory extends InputEventAdapterFactory { + + private ResourceBundle resourceBundle = ResourceBundle.getBundle + ("org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.i18n.Resources", Locale.getDefault()); + + @Override + public String getType() { + return MQTTEventAdapterConstants.ADAPTER_TYPE_MQTT; + } + + @Override + public List getSupportedMessageFormats() { + List supportInputMessageTypes = new ArrayList(); + supportInputMessageTypes.add(MessageType.JSON); + supportInputMessageTypes.add(MessageType.TEXT); + supportInputMessageTypes.add(MessageType.XML); + supportInputMessageTypes.add(MessageType.WSO2EVENT); + return supportInputMessageTypes; + } + + @Override + public List getPropertyList() { + List propertyList = new ArrayList(); + + // set topic + Property topicProperty = new Property(MQTTEventAdapterConstants.ADAPTER_MESSAGE_TOPIC); + topicProperty.setDisplayName(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_MESSAGE_TOPIC)); + topicProperty.setRequired(true); + topicProperty.setHint(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_MESSAGE_TOPIC_HINT)); + propertyList.add(topicProperty); + + //Broker Url + Property brokerUrl = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_URL); + brokerUrl.setDisplayName(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_URL)); + brokerUrl.setRequired(true); + brokerUrl.setHint(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_URL_HINT)); + propertyList.add(brokerUrl); + + //DCR endpoint details + Property dcrUrl = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_DCR_URL); + dcrUrl.setDisplayName(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_DCR_URL)); + dcrUrl.setRequired(false); + dcrUrl.setHint(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_DCR_URL_HINT)); + propertyList.add(dcrUrl); + + //Content Validator details + Property contentValidator = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME); + contentValidator.setDisplayName( + resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME)); + contentValidator.setRequired(false); + contentValidator.setHint( + resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME_HINT)); + contentValidator.setDefaultValue(Constants.DEFAULT); + propertyList.add(contentValidator); + + //Content Validator Params details + Property contentValidatorParams = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS); + contentValidatorParams.setDisplayName( + resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS)); + contentValidatorParams.setRequired(false); + contentValidatorParams.setHint( + resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS_HINT)); + contentValidatorParams.setDefaultValue(Constants.MQTT_CONTENT_VALIDATION_DEFAULT_PARAMETERS); + propertyList.add(contentValidatorParams); + + //Broker Username + Property userName = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_USERNAME); + userName.setDisplayName( + resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_USERNAME)); + userName.setRequired(false); + userName.setHint(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_USERNAME_HINT)); + propertyList.add(userName); + + //Broker Required Scopes. + Property scopes = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_SCOPES); + scopes.setDisplayName( + resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_SCOPES)); + scopes.setRequired(false); + scopes.setHint(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_SCOPES_HINT)); + propertyList.add(scopes); + + //Broker clear session + Property clearSession = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_CLEAN_SESSION); + clearSession.setDisplayName(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_CLEAN_SESSION)); + clearSession.setRequired(false); + clearSession.setOptions(new String[]{"true", "false"}); + clearSession.setDefaultValue("true"); + clearSession.setHint(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_CLEAN_SESSION_HINT)); + propertyList.add(clearSession); + + //Content Transformer details + Property contentTransformer = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME); + contentTransformer.setDisplayName( + resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME)); + contentTransformer.setRequired(false); + contentTransformer.setHint( + resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME_HINT)); + contentTransformer.setDefaultValue(Constants.DEFAULT); + propertyList.add(contentTransformer); + + // set clientId + Property clientId = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_CLIENTID); + clientId.setDisplayName(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_CLIENTID)); + clientId.setRequired(false); + clientId.setHint(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_CLIENTID_HINT)); + propertyList.add(clientId); + + return propertyList; + } + + @Override + public String getUsageTips() { + return null; + } + + @Override + public InputEventAdapter createEventAdapter(InputEventAdapterConfiguration eventAdapterConfiguration, + Map globalProperties) { + return new MQTTEventAdapter(eventAdapterConfiguration, globalProperties); + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/exception/MQTTContentInitializationException.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/exception/MQTTContentInitializationException.java new file mode 100644 index 000000000..ddb60f53e --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/exception/MQTTContentInitializationException.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, 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.device.mgt.iot.input.adapter.mqtt.exception; + +/** + * This exception will thrown when content validator is failed to intialiaze. + */ +public class MQTTContentInitializationException extends RuntimeException { + private String errMessage; + + public MQTTContentInitializationException(String msg, Exception nestedEx) { + super(msg, nestedEx); + setErrorMessage(msg); + } + + public MQTTContentInitializationException(String message, Throwable cause) { + super(message, cause); + setErrorMessage(message); + } + + public MQTTContentInitializationException(String msg) { + super(msg); + setErrorMessage(msg); + } + + public MQTTContentInitializationException() { + super(); + } + + public MQTTContentInitializationException(Throwable cause) { + super(cause); + } + + public String getErrorMessage() { + return errMessage; + } + + public void setErrorMessage(String errMessage) { + this.errMessage = errMessage; + } +} \ No newline at end of file diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTAdapterListener.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTAdapterListener.java new file mode 100644 index 000000000..d2e8a6366 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTAdapterListener.java @@ -0,0 +1,305 @@ +/* +* 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.device.mgt.iot.input.adapter.mqtt.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.eclipse.paho.client.mqttv3.*; +import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.core.ServerStatus; +import org.wso2.carbon.device.mgt.iot.input.adapter.ContentInfo; +import org.wso2.carbon.device.mgt.iot.input.adapter.ContentTransformer; +import org.wso2.carbon.device.mgt.iot.input.adapter.DefaultContentTransformer; +import org.wso2.carbon.device.mgt.iot.input.adapter.DefaultContentValidator; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterListener; +import org.wso2.carbon.event.input.adapter.core.exception.InputEventAdapterRuntimeException; +import org.wso2.carbon.device.mgt.iot.input.adapter.ContentValidator; +import org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.Constants; +import org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.exception.MQTTContentInitializationException; +import org.wso2.carbon.identity.jwt.client.extension.dto.AccessTokenInfo; +import org.wso2.carbon.identity.jwt.client.extension.exception.JWTClientException; +import org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; + +public class MQTTAdapterListener implements MqttCallback, Runnable { + private static final Log log = LogFactory.getLog(MQTTAdapterListener.class); + + private MqttClient mqttClient; + private MqttConnectOptions connectionOptions; + private boolean cleanSession; + private int keepAlive; + + private MQTTBrokerConnectionConfiguration mqttBrokerConnectionConfiguration; + private String mqttClientId; + private String topic; + private int tenantId; + private boolean connectionSucceeded = false; + ContentValidator contentValidator; + Map contentValidationParams; + ContentTransformer contentTransformer; + + private InputEventAdapterListener eventAdapterListener = null; + + + public MQTTAdapterListener(MQTTBrokerConnectionConfiguration mqttBrokerConnectionConfiguration, + String topic, String mqttClientId, + InputEventAdapterListener inputEventAdapterListener, int tenantId) { + + if(mqttClientId == null || mqttClientId.trim().isEmpty()){ + mqttClientId = MqttClient.generateClientId(); + } + + this.mqttClientId = mqttClientId; + this.mqttBrokerConnectionConfiguration = mqttBrokerConnectionConfiguration; + this.cleanSession = mqttBrokerConnectionConfiguration.isCleanSession(); + this.keepAlive = mqttBrokerConnectionConfiguration.getKeepAlive(); + this.topic = topic; + this.eventAdapterListener = inputEventAdapterListener; + this.tenantId = tenantId; + + //SORTING messages until the server fetches them + String temp_directory = System.getProperty("java.io.tmpdir"); + MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(temp_directory); + + + try { + // Construct the connection options object that contains connection parameters + // such as cleanSession and LWT + connectionOptions = new MqttConnectOptions(); + connectionOptions.setCleanSession(cleanSession); + connectionOptions.setKeepAliveInterval(keepAlive); + + // Construct an MQTT blocking mode client + mqttClient = new MqttClient(this.mqttBrokerConnectionConfiguration.getBrokerUrl(), this.mqttClientId, + dataStore); + + // Set this wrapper as the callback handler + mqttClient.setCallback(this); + String contentValidatorClassName = this.mqttBrokerConnectionConfiguration.getContentValidatorClassName(); + + if (contentValidatorClassName != null && contentValidatorClassName.equals(Constants.DEFAULT)) { + contentValidator = new DefaultContentValidator(); + } else if (contentValidatorClassName != null && !contentValidatorClassName.isEmpty()) { + try { + Class contentValidatorClass = Class.forName(contentValidatorClassName) + .asSubclass(ContentValidator.class); + contentValidator = contentValidatorClass.newInstance(); + } catch (ClassNotFoundException e) { + throw new MQTTContentInitializationException( + "Unable to find the class validator: " + contentValidatorClassName, e); + } catch (InstantiationException e) { + throw new MQTTContentInitializationException( + "Unable to create an instance of :" + contentValidatorClassName, e); + } catch (IllegalAccessException e) { + throw new MQTTContentInitializationException("Access of the instance in not allowed.", e); + } + } + + contentValidationParams = mqttBrokerConnectionConfiguration.getContentValidatorParams(); + + String contentTransformerClassName = this.mqttBrokerConnectionConfiguration.getContentTransformerClassName(); + if (contentTransformerClassName != null && contentTransformerClassName.equals(Constants.DEFAULT)) { + contentTransformer = new DefaultContentTransformer(); + } else if (contentTransformerClassName != null && !contentTransformerClassName.isEmpty()) { + try { + Class contentTransformerClass = Class.forName(contentTransformerClassName) + .asSubclass(ContentTransformer.class); + contentTransformer = contentTransformerClass.newInstance(); + } catch (ClassNotFoundException e) { + throw new MQTTContentInitializationException( + "Unable to find the class transfoer: " + contentTransformerClassName, e); + } catch (InstantiationException e) { + throw new MQTTContentInitializationException( + "Unable to create an instance of :" + contentTransformerClassName, e); + } catch (IllegalAccessException e) { + throw new MQTTContentInitializationException("Access of the instance in not allowed.", e); + } + } + } catch (MqttException e) { + log.error("Exception occurred while subscribing to MQTT broker at " + + mqttBrokerConnectionConfiguration.getBrokerUrl()); + throw new InputEventAdapterRuntimeException(e); + } catch (Throwable e) { + log.error("Exception occurred while subscribing to MQTT broker at " + + mqttBrokerConnectionConfiguration.getBrokerUrl()); + throw new InputEventAdapterRuntimeException(e); + } + + } + + public void startListener() throws MqttException { + if (this.mqttBrokerConnectionConfiguration.getBrokerUsername() != null && this.mqttBrokerConnectionConfiguration.getDcrUrl() != null) { + String username = this.mqttBrokerConnectionConfiguration.getBrokerUsername(); + String dcrUrlString = this.mqttBrokerConnectionConfiguration.getDcrUrl(); + String scopes = this.mqttBrokerConnectionConfiguration.getBrokerScopes(); + //getJWT Client Parameters. + if (dcrUrlString != null && !dcrUrlString.isEmpty()) { + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId, true); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(username); + try { + URL dcrUrl = new URL(dcrUrlString); + HttpClient httpClient = MQTTUtil.getHttpClient(dcrUrl.getProtocol()); + HttpPost postMethod = new HttpPost(dcrUrlString); + RegistrationProfile registrationProfile = new RegistrationProfile(); + registrationProfile.setCallbackUrl(Constants.EMPTY_STRING); + registrationProfile.setGrantType(Constants.GRANT_TYPE); + registrationProfile.setOwner(username); + registrationProfile.setTokenScope(Constants.TOKEN_SCOPE); + registrationProfile.setApplicationType(Constants.APPLICATION_TYPE); + registrationProfile.setClientName(username + "_" + tenantId); + String jsonString = registrationProfile.toJSON(); + StringEntity requestEntity = new StringEntity(jsonString, ContentType.APPLICATION_JSON); + postMethod.setEntity(requestEntity); + HttpResponse httpResponse = httpClient.execute(postMethod); + String response = MQTTUtil.getResponseString(httpResponse); + try { + JSONParser jsonParser = new JSONParser(); + JSONObject jsonPayload = (JSONObject) jsonParser.parse(response); + String clientId = (String) jsonPayload.get(Constants.CLIENT_ID); + String clientSecret = (String) jsonPayload.get(Constants.CLIENT_SECRET); + JWTClientManagerService jwtClientManagerService = MQTTUtil.getJWTClientManagerService(); + AccessTokenInfo accessTokenInfo = jwtClientManagerService.getJWTClient().getAccessToken( + clientId, clientSecret, username, scopes); + connectionOptions.setUserName(accessTokenInfo.getAccessToken()); + } catch (ParseException e) { + String msg = "error occurred while parsing client credential payload"; + log.error(msg, e); + } catch (JWTClientException e) { + String msg = "error occurred while parsing the response from JWT Client"; + log.error(msg, e); + } + } catch (MalformedURLException e) { + log.error("Invalid dcrUrl : " + dcrUrlString); + } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | IOException e) { + log.error("Failed to create an https connection.", e); + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + } + } + // Connect to the MQTT server + mqttClient.connect(connectionOptions); + + // Subscribe to the requested topic + // The QoS specified is the maximum level that messages will be sent to the client at. + // For instance if QoS 1 is specified, any messages originally published at QoS 2 will + // be downgraded to 1 when delivering to the client but messages published at 1 and 0 + // will be received at the same level they were published at. + mqttClient.subscribe(topic); + } + + public void stopListener(String adapterName) { + if (connectionSucceeded) { + try { + // Un-subscribe accordingly and disconnect from the MQTT server. + if (!ServerStatus.getCurrentStatus().equals(ServerStatus.STATUS_SHUTTING_DOWN) || cleanSession) { + mqttClient.unsubscribe(topic); + } + mqttClient.disconnect(3000); + } catch (MqttException e) { + log.error("Can not unsubscribe from the destination " + topic + + " with the event adapter " + adapterName, e); + } + } + //This is to stop all running reconnection threads + connectionSucceeded = true; + } + + @Override + public void connectionLost(Throwable throwable) { + log.warn("MQTT connection not reachable " + throwable); + connectionSucceeded = false; + new Thread(this).start(); + } + + @Override + public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception { + try { + String msgText = mqttMessage.toString(); + if (log.isDebugEnabled()) { + log.debug(msgText); + } + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId); + + if (log.isDebugEnabled()) { + log.debug("Event received in MQTT Event Adapter - " + msgText); + } + + if (contentValidator != null && contentTransformer != null) { + ContentInfo contentInfo; + Map dynamicProperties = new HashMap<>(); + dynamicProperties.put(Constants.TOPIC, topic); + msgText = contentTransformer.transform(msgText, dynamicProperties); + contentInfo = contentValidator.validate(msgText,contentValidationParams, dynamicProperties); + if (contentInfo != null && contentInfo.isValidContent()) { + eventAdapterListener.onEvent(contentInfo.getMsgText()); + } + } else { + eventAdapterListener.onEvent(msgText); + } + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + } + + @Override + public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { + + } + + @Override + public void run() { + while (!connectionSucceeded) { + try { + MQTTEventAdapterConstants.initialReconnectDuration = MQTTEventAdapterConstants.initialReconnectDuration + * MQTTEventAdapterConstants.reconnectionProgressionFactor; + Thread.sleep(MQTTEventAdapterConstants.initialReconnectDuration); + startListener(); + connectionSucceeded = true; + log.info("MQTT Connection successful"); + } catch (InterruptedException e) { + log.error("Interruption occurred while waiting for reconnection", e); + } catch (MqttException e) { + log.error("MQTT Exception occurred when starting listener", e); + } + } + } + + public void createConnection() { + new Thread(this).start(); + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTBrokerConnectionConfiguration.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTBrokerConnectionConfiguration.java new file mode 100644 index 000000000..14c171eba --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTBrokerConnectionConfiguration.java @@ -0,0 +1,131 @@ +/* +* 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.device.mgt.iot.input.adapter.mqtt.util; + +import org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.Constants; +import org.wso2.carbon.device.mgt.iot.input.adapter.util.PropertyUtils; + +import java.util.Map; + +/** + * This holds the configurations related to MQTT Broker. + */ +public class MQTTBrokerConnectionConfiguration { + + private String brokerUsername = null; + private String brokerScopes = null; + private boolean cleanSession = true; + private int keepAlive; + private String brokerUrl; + private String dcrUrl; + private String contentValidatorClassName; + private Map contentValidatorParams; + private String contentTransformerClassName; + + public String getBrokerScopes() { + return brokerScopes; + } + + public void setBrokerScopes(String brokerScopes) { + this.brokerScopes = brokerScopes; + } + + public String getBrokerUsername() { + return brokerUsername; + } + + public void setBrokerUsername(String brokerUsername) { + this.brokerUsername = brokerUsername; + } + + public void setCleanSession(boolean cleanSession) { + this.cleanSession = cleanSession; + } + + + public boolean isCleanSession() { + return cleanSession; + } + + public String getBrokerUrl() { + return brokerUrl; + } + + public void setBrokerUrl(String brokerUrl) { + this.brokerUrl = brokerUrl; + } + + public String getDcrUrl() { + return dcrUrl; + } + + public void setDcrUrl(String dcrUrl) { + this.dcrUrl = dcrUrl; + } + + public int getKeepAlive() { + return keepAlive; + } + + public void setKeepAlive(int keepAlive) { + this.keepAlive = keepAlive; + } + + public String getContentValidatorClassName() { + return contentValidatorClassName; + } + + public void setContentValidatorClassName(String contentValidatorClassName) { + this.contentValidatorClassName = contentValidatorClassName; + } + + public Map getContentValidatorParams() { + return contentValidatorParams; + } + + public void setContentValidatorParams(Map contentValidatorParams) { + this.contentValidatorParams = contentValidatorParams; + } + + public String getContentTransformerClassName() { + return contentTransformerClassName; + } + + public MQTTBrokerConnectionConfiguration(String brokerUrl, String brokerUsername, String brokerScopes, + String dcrUrl, String cleanSession, int keepAlive, + String contentValidatorClassName, Map contentValidatorParams, + String contentTransformerClassName) { + this.brokerUsername = brokerUsername; + this.brokerScopes = brokerScopes; + if (brokerScopes == null) { + this.brokerScopes = Constants.EMPTY_STRING; + } + this.brokerUrl = PropertyUtils.replaceMqttProperty(brokerUrl); + this.dcrUrl = PropertyUtils.replaceMqttProperty(dcrUrl); + this.contentValidatorClassName = contentValidatorClassName; + if (cleanSession != null) { + this.cleanSession = Boolean.parseBoolean(cleanSession); + } + this.keepAlive = keepAlive; + if (contentValidatorParams != null) { + this.contentValidatorParams = contentValidatorParams; + } + + this.contentTransformerClassName = contentTransformerClassName; + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTContentValidator.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTContentValidator.java new file mode 100644 index 000000000..5e24715a6 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTContentValidator.java @@ -0,0 +1,87 @@ +/* +* Copyright (c) 2016, 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.device.mgt.iot.input.adapter.mqtt.util; + +import com.jayway.jsonpath.JsonPath; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.wso2.carbon.device.mgt.iot.input.adapter.ContentInfo; +import org.wso2.carbon.device.mgt.iot.input.adapter.ContentValidator; +import org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.Constants; + +import java.util.Map; + +public class MQTTContentValidator implements ContentValidator { + private static String JSON_ARRAY_START_CHAR = "["; + private static final Log log = LogFactory.getLog(MQTTContentValidator.class); + + @Override + public ContentInfo validate(String msgPayload, Map contentValidationParams, + Map dynamicParams) { + String topic = dynamicParams.get(Constants.TOPIC); + String topics[] = topic.split("/"); + + String msg = msgPayload; + String deviceIdJsonPath = contentValidationParams.get(Constants.DEVICE_ID_JSON_PATH); + String deviceIdInTopicHierarchyLevel = contentValidationParams.get(Constants.DEVICE_ID_TOPIC_HIERARCHY_INDEX); + int deviceIdInTopicHierarchyLevelIndex = 0; + if (deviceIdInTopicHierarchyLevel != null && !deviceIdInTopicHierarchyLevel.isEmpty()) { + deviceIdInTopicHierarchyLevelIndex = Integer.parseInt(deviceIdInTopicHierarchyLevel); + } + String deviceIdFromTopic = topics[deviceIdInTopicHierarchyLevelIndex]; + boolean status; + if (msg.startsWith(JSON_ARRAY_START_CHAR)) { + status = processMultipleEvents(msg, deviceIdFromTopic, deviceIdJsonPath); + } else { + status = processSingleEvent(msg, deviceIdFromTopic, deviceIdJsonPath); + } + return new ContentInfo(status, msg); + } + + private boolean processSingleEvent(String msg, String deviceIdFromTopic, String deviceIdJsonPath) { + Object res = JsonPath.read(msg, deviceIdJsonPath); + String deviceIdFromContent = (res != null) ? res.toString() : ""; + if (deviceIdFromContent.equals(deviceIdFromTopic)) { + return true; + } + return false; + } + + private boolean processMultipleEvents(String msg, String deviceIdFromTopic, String deviceIdJsonPath) { + try { + JSONParser jsonParser = new JSONParser(); + JSONArray jsonArray = (JSONArray) jsonParser.parse(msg); + boolean status = false; + for (int i = 0; i < jsonArray.size(); i++) { + status = processSingleEvent(jsonArray.get(i).toString(), deviceIdFromTopic, deviceIdJsonPath); + if (!status) { + return status; + } + } + return status; + } catch (ParseException e) { + log.error("Invalid input " + msg, e); + return false; + } + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTEventAdapterConstants.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTEventAdapterConstants.java new file mode 100644 index 000000000..fc5ae8f2c --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTEventAdapterConstants.java @@ -0,0 +1,52 @@ +/* +* 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.device.mgt.iot.input.adapter.mqtt.util; + + +/** + * This holds the constants related to mqtt event adapter. + */ +public class MQTTEventAdapterConstants { + + public static final String ADAPTER_TYPE_MQTT = "oauth-mqtt"; + public static final String ADAPTER_CONF_URL = "url"; + public static final String ADAPTER_CONF_USERNAME = "username"; + public static final String ADAPTER_CONF_USERNAME_HINT = "username.hint"; + public static final String ADAPTER_CONF_SCOPES = "scopes"; + public static final String ADAPTER_CONF_SCOPES_HINT = "scopes.hint"; + public static final String ADAPTER_CONF_URL_HINT = "url.hint"; + public static final String ADAPTER_CONF_DCR_URL = "dcrUrl"; + public static final String ADAPTER_CONF_DCR_URL_HINT = "dcrUrl.hint"; + public static final String ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME = "contentValidation"; + public static final String ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME_HINT = "contentValidation.hint"; + public static final String ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS = "contentValidationParams"; + public static final String ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS_HINT = "contentValidationParams.hint"; + public static final String ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME = "contentTransformer"; + public static final String ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME_HINT = "contentTransformer.hint"; + public static final String ADAPTER_MESSAGE_TOPIC = "topic"; + public static final String ADAPTER_MESSAGE_TOPIC_HINT = "topic.hint"; + public static final String ADAPTER_CONF_CLIENTID = "clientId"; + public static final String ADAPTER_CONF_CLIENTID_HINT = "clientId.hint"; + public static final String ADAPTER_CONF_CLEAN_SESSION = "cleanSession"; + public static final String ADAPTER_CONF_CLEAN_SESSION_HINT = "cleanSession.hint"; + public static final String ADAPTER_CONF_KEEP_ALIVE = "keepAlive"; + public static final int ADAPTER_CONF_DEFAULT_KEEP_ALIVE = 60000; + + public static int initialReconnectDuration = 10000; + public static final int reconnectionProgressionFactor = 2; +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTUtil.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTUtil.java new file mode 100644 index 000000000..1d1fea18d --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/MQTTUtil.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016, 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.device.mgt.iot.input.adapter.mqtt.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContextBuilder; +import org.apache.http.conn.ssl.TrustSelfSignedStrategy; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; + +/** + * This is the utility class that is used for MQTT input adapater. + */ +public class MQTTUtil { + private static final String HTTPS_PROTOCOL = "https"; + private static final Log log = LogFactory.getLog(MQTTUtil.class); + /** + * Return a http client instance + * + * @param protocol- service endpoint protocol http/https + * @return + */ + public static HttpClient getHttpClient(String protocol) + throws IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException { + HttpClient httpclient; + if (HTTPS_PROTOCOL.equals(protocol)) { + SSLContextBuilder builder = new SSLContextBuilder(); + builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build()); + httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); + } else { + httpclient = HttpClients.createDefault(); + } + return httpclient; + } + + public static String getResponseString(HttpResponse httpResponse) throws IOException { + BufferedReader br = null; + try { + br = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent())); + String readLine; + String response = ""; + while (((readLine = br.readLine()) != null)) { + response += readLine; + } + return response; + } finally { + EntityUtils.consumeQuietly(httpResponse.getEntity()); + if (br != null) { + try { + br.close(); + } catch (IOException e) { + log.warn("Error while closing the connection! " + e.getMessage()); + } + } + } + } + + public static JWTClientManagerService getJWTClientManagerService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + JWTClientManagerService jwtClientManagerService = + (JWTClientManagerService) ctx.getOSGiService(JWTClientManagerService.class, null); + if (jwtClientManagerService == null) { + String msg = "JWT management service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return jwtClientManagerService; + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/RegistrationProfile.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/RegistrationProfile.java new file mode 100644 index 000000000..5871fa5b8 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/util/RegistrationProfile.java @@ -0,0 +1,73 @@ +package org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.util; + +/** + * This class represents the data that are required to register + * the oauth application. + */ +public class RegistrationProfile { + + private String callbackUrl; + private String clientName; + private String tokenScope; + private String owner; + private String grantType; + private String applicationType; + + private static final String TAG = RegistrationProfile.class.getSimpleName(); + + public String getCallbackUrl() { + return callbackUrl; + } + + public void setCallbackUrl(String callBackUrl) { + this.callbackUrl = callBackUrl; + } + + public String getClientName() { + return clientName; + } + + public void setClientName(String clientName) { + this.clientName = clientName; + } + + public String getTokenScope() { + return tokenScope; + } + + public void setTokenScope(String tokenScope) { + this.tokenScope = tokenScope; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getGrantType() { + return grantType; + } + + public void setGrantType(String grantType) { + this.grantType = grantType; + } + + public String getApplicationType() { + return applicationType; + } + + public void setApplicationType(String applicationType) { + this.applicationType = applicationType; + } + + public String toJSON() { + String jsonString = + "{\"callbackUrl\": \"" + callbackUrl + "\",\"clientName\": \"" + clientName + "\", \"tokenScope\": " + + "\"" + tokenScope + "\", \"owner\": \"" + owner + "\"," + "\"grantType\": \"" + grantType + + "\", \"saasApp\" :false }\n"; + return jsonString; + } +} \ No newline at end of file diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/util/PropertyUtils.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/util/PropertyUtils.java new file mode 100644 index 000000000..da872feb9 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/util/PropertyUtils.java @@ -0,0 +1,54 @@ +/* +* Copyright (c) 2016, 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.device.mgt.iot.input.adapter.util; + +import org.wso2.carbon.base.ServerConfiguration; +import org.wso2.carbon.core.util.Utils; + +public class PropertyUtils { + private static final String MQTT_PORT = "\\$\\{mqtt.broker.port\\}"; + private static final String MQTT_BROKER_HOST = "\\$\\{mqtt.broker.host\\}"; + private static final String CARBON_CONFIG_PORT_OFFSET = "Ports.Offset"; + private static final String DEFAULT_CARBON_SERVER_HOST_PROPERTY = "server.host"; + private static final int CARBON_DEFAULT_PORT_OFFSET = 0; + private static final int DEFAULT_MQTT_PORT = 1883; + + //This method is only used if the mb features are within DAS. + public static String replaceMqttProperty (String urlWithPlaceholders) { + urlWithPlaceholders = Utils.replaceSystemProperty(urlWithPlaceholders); + urlWithPlaceholders = urlWithPlaceholders.replaceAll(MQTT_PORT, "" + (DEFAULT_MQTT_PORT + getPortOffset())); + urlWithPlaceholders = urlWithPlaceholders.replaceAll(MQTT_BROKER_HOST, System.getProperty(DEFAULT_CARBON_SERVER_HOST_PROPERTY, + "localhost")); + return urlWithPlaceholders; + } + + private static int getPortOffset() { + ServerConfiguration carbonConfig = ServerConfiguration.getInstance(); + String portOffset = System.getProperty("portOffset", carbonConfig.getFirstProperty(CARBON_CONFIG_PORT_OFFSET)); + try { + if ((portOffset != null)) { + return Integer.parseInt(portOffset.trim()); + } else { + return CARBON_DEFAULT_PORT_OFFSET; + } + } catch (NumberFormatException e) { + return CARBON_DEFAULT_PORT_OFFSET; + } + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/Constants.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/Constants.java new file mode 100644 index 000000000..0fcec9983 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/Constants.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016, 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.device.mgt.iot.input.adapter.xmpp; + +/** + * This holds the constants related to MQTT input adapter. + */ +public class Constants { + + public static final String DEFAULT = "default"; + public static final String XMPP_CONTENT_VALIDATION_DEFAULT_PARAMETERS = + "device_id_json_path:event.metaData.deviceId,device_id_topic_hierarchy_index:2"; + public static final String FROM_KEY = "from"; + public static final String SUBJECT_KEY = "subject"; + public static final String DEVICE_ID_JSON_PATH = "device_id_json_path"; + public static final String DEVICE_ID_TOPIC_HIERARCHY_INDEX = "device_id_topic_hierarchy_index"; +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/XMPPEventAdapter.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/XMPPEventAdapter.java new file mode 100644 index 000000000..8e580717d --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/XMPPEventAdapter.java @@ -0,0 +1,161 @@ +/* +* 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.device.mgt.iot.input.adapter.xmpp; + +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.iot.input.adapter.xmpp.util.XMPPAdapterListener; +import org.wso2.carbon.device.mgt.iot.input.adapter.xmpp.util.XMPPServerConnectionConfiguration; +import org.wso2.carbon.device.mgt.iot.input.adapter.xmpp.util.XMPPEventAdapterConstants; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapter; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterConfiguration; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterListener; +import org.wso2.carbon.event.input.adapter.core.exception.InputEventAdapterException; +import org.wso2.carbon.event.input.adapter.core.exception.TestConnectionNotSupportedException; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * Input XMPPEventAdapter will be used to receive events with XMPP protocol using specified broker and topic. + */ +public class XMPPEventAdapter implements InputEventAdapter { + + private final InputEventAdapterConfiguration eventAdapterConfiguration; + private final Map globalProperties; + private InputEventAdapterListener eventAdapterListener; + private final String id = UUID.randomUUID().toString(); + private XMPPAdapterListener xmppAdapterListener; + private XMPPServerConnectionConfiguration xmppServerConnectionConfiguration; + + + public XMPPEventAdapter(InputEventAdapterConfiguration eventAdapterConfiguration, + Map globalProperties) { + this.eventAdapterConfiguration = eventAdapterConfiguration; + this.globalProperties = globalProperties; + } + + @Override + public void init(InputEventAdapterListener eventAdapterListener) throws InputEventAdapterException { + this.eventAdapterListener = eventAdapterListener; + try { + + String contentValidationParams = eventAdapterConfiguration.getProperties().get( + XMPPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS); + String params[] = contentValidationParams.split(","); + Map paramsMap = new HashMap<>(); + for (String param : params) { + String paramsKeyAndValue[] = splitOnFirst(param, ':'); + if (paramsKeyAndValue.length != 2) { + throw new InputEventAdapterException("Invalid parameters for content validation - " + param); + } + paramsMap.put(paramsKeyAndValue[0], paramsKeyAndValue[1]); + } + + int xmppPort = XMPPEventAdapterConstants.DEFAULT_XMPP_PORT; + String xmppPortString = eventAdapterConfiguration.getProperties() + .get(XMPPEventAdapterConstants.ADAPTER_CONF_PORT); + if (xmppPortString != null && !xmppPortString.trim().isEmpty()) { + xmppPort = Integer.parseInt(xmppPortString); + } + int timeoutInterval = XMPPEventAdapterConstants.DEFAULT_TIMEOUT_INTERVAL; + String timeoutIntervalString = eventAdapterConfiguration.getProperties().get( + XMPPEventAdapterConstants.ADAPTER_CONF_TIMEOUT_INTERVAL); + if (timeoutIntervalString != null && !timeoutIntervalString.trim().isEmpty()) { + timeoutInterval = Integer.parseInt(timeoutIntervalString); + } + xmppServerConnectionConfiguration = + new XMPPServerConnectionConfiguration(eventAdapterConfiguration.getProperties() + .get(XMPPEventAdapterConstants.ADAPTER_CONF_HOST), + xmppPort, + eventAdapterConfiguration.getProperties().get(XMPPEventAdapterConstants.ADAPTER_CONF_USERNAME), + eventAdapterConfiguration.getProperties().get(XMPPEventAdapterConstants.ADAPTER_CONF_PASSWORD), + timeoutInterval, + eventAdapterConfiguration.getProperties().get(XMPPEventAdapterConstants.ADAPTER_CONF_RESOURCE), + eventAdapterConfiguration.getProperties().get(XMPPEventAdapterConstants + .ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME), + paramsMap, + eventAdapterConfiguration.getProperties().get(XMPPEventAdapterConstants.ADAPTER_CONF_RECIEVER_JID), + eventAdapterConfiguration.getProperties().get(XMPPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME) + ); + + xmppAdapterListener = new XMPPAdapterListener(xmppServerConnectionConfiguration, + eventAdapterListener, PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId()); + + } catch (Throwable t) { + throw new InputEventAdapterException(t.getMessage(), t); + } + } + + private String[] splitOnFirst(String str, char c) { + int idx = str.indexOf(c); + String head = str.substring(0, idx); + String tail = str.substring(idx + 1); + return new String[] { head, tail} ; + } + + @Override + public void testConnect() throws TestConnectionNotSupportedException { + throw new TestConnectionNotSupportedException("not-supported"); + } + + @Override + public void connect() { + xmppAdapterListener.createConnection(); + } + + @Override + public void disconnect() { + if (xmppAdapterListener != null) { + xmppAdapterListener.stopListener(eventAdapterConfiguration.getName()); + } + } + + @Override + public void destroy() { + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof XMPPEventAdapter)) return false; + + XMPPEventAdapter that = (XMPPEventAdapter) o; + + if (!id.equals(that.id)) return false; + + return true; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + + @Override + public boolean isEventDuplicatedInCluster() { + return true; + } + + @Override + public boolean isPolling() { + return true; + } + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/XMPPEventAdapterFactory.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/XMPPEventAdapterFactory.java new file mode 100644 index 000000000..966703ce7 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/XMPPEventAdapterFactory.java @@ -0,0 +1,151 @@ +/* +* 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.device.mgt.iot.input.adapter.xmpp; + +import org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.util.MQTTEventAdapterConstants; +import org.wso2.carbon.device.mgt.iot.input.adapter.xmpp.util.XMPPEventAdapterConstants; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapter; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterConfiguration; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterFactory; +import org.wso2.carbon.event.input.adapter.core.MessageType; +import org.wso2.carbon.event.input.adapter.core.Property; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * The xmpp event adapter factory class to create a xmpp input adapter + */ +public class XMPPEventAdapterFactory extends InputEventAdapterFactory { + + private ResourceBundle resourceBundle = ResourceBundle.getBundle + ("org.wso2.carbon.device.mgt.iot.input.adapter.xmpp.i18n.Resources", Locale.getDefault()); + + @Override + public String getType() { + return XMPPEventAdapterConstants.ADAPTER_TYPE_XMPP; + } + + @Override + public List getSupportedMessageFormats() { + List supportInputMessageTypes = new ArrayList(); + supportInputMessageTypes.add(MessageType.JSON); + supportInputMessageTypes.add(MessageType.TEXT); + supportInputMessageTypes.add(MessageType.XML); + supportInputMessageTypes.add(MessageType.WSO2EVENT); + return supportInputMessageTypes; + } + + @Override + public List getPropertyList() { + List propertyList = new ArrayList(); + // Url + Property host = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_HOST); + host.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_HOST)); + host.setRequired(true); + host.setHint(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_HOST_HINT)); + + // Host Port + Property port = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_PORT); + port.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_PORT)); + port.setRequired(true); + port.setHint(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_PORT_HINT)); + + //Broker Username + Property userName = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_USERNAME); + userName.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_USERNAME)); + userName.setRequired(true); + userName.setHint(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_USERNAME_HINT)); + + //Broker Password + Property password = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_PASSWORD); + password.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_PASSWORD)); + password.setRequired(true); + password.setHint(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_PASSWORD_HINT)); + + //Timeout Interval + Property timooutInterval = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_TIMEOUT_INTERVAL); + timooutInterval.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_TIMEOUT_INTERVAL_HINT)); + timooutInterval.setRequired(false); + timooutInterval.setHint(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_TIMEOUT_INTERVAL_HINT)); + + //Resource + Property resource = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_RESOURCE); + resource.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_RESOURCE)); + resource.setRequired(true); + resource.setHint(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_RESOURCE_HINT)); + + //Content Validator details + Property contentValidator = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME); + contentValidator.setDisplayName( + resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME)); + contentValidator.setRequired(false); + contentValidator.setHint( + resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME_HINT)); + contentValidator.setDefaultValue(org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.Constants.DEFAULT); + + //Content Validator Params details + Property contentValidatorParams = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS); + contentValidatorParams.setDisplayName( + resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS)); + contentValidatorParams.setRequired(false); + contentValidatorParams.setHint( + resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS_HINT)); + contentValidatorParams.setDefaultValue(Constants.XMPP_CONTENT_VALIDATION_DEFAULT_PARAMETERS); + + Property jid = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_RECIEVER_JID); + jid.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_RECIEVER_JID)); + jid.setRequired(true); + jid.setHint(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_RECIEVER_JID_HINT)); + + //Content Transformer details + Property contentTransformer = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME); + contentTransformer.setDisplayName( + resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME)); + contentTransformer.setRequired(false); + contentTransformer.setHint( + resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME_HINT)); + contentTransformer.setDefaultValue(Constants.DEFAULT); + + propertyList.add(host); + propertyList.add(port); + propertyList.add(userName); + propertyList.add(password); + propertyList.add(timooutInterval); + propertyList.add(resource); + propertyList.add(contentValidator); + propertyList.add(contentValidatorParams); + propertyList.add(jid); + propertyList.add(contentTransformer); + return propertyList; + } + + @Override + public String getUsageTips() { + return null; + } + + @Override + public InputEventAdapter createEventAdapter(InputEventAdapterConfiguration eventAdapterConfiguration, + Map globalProperties) { + return new XMPPEventAdapter(eventAdapterConfiguration, globalProperties); + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/exception/XMPPContentInitializationException.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/exception/XMPPContentInitializationException.java new file mode 100644 index 000000000..ec26e4976 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/exception/XMPPContentInitializationException.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, 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.device.mgt.iot.input.adapter.xmpp.exception; + +/** + * This exception will thrown when content validator is failed to intialiaze. + */ +public class XMPPContentInitializationException extends RuntimeException { + private String errMessage; + + public XMPPContentInitializationException(String msg, Exception nestedEx) { + super(msg, nestedEx); + setErrorMessage(msg); + } + + public XMPPContentInitializationException(String message, Throwable cause) { + super(message, cause); + setErrorMessage(message); + } + + public XMPPContentInitializationException(String msg) { + super(msg); + setErrorMessage(msg); + } + + public XMPPContentInitializationException() { + super(); + } + + public XMPPContentInitializationException(Throwable cause) { + super(cause); + } + + public String getErrorMessage() { + return errMessage; + } + + public void setErrorMessage(String errMessage) { + this.errMessage = errMessage; + } +} \ No newline at end of file diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/util/XMPPAdapterListener.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/util/XMPPAdapterListener.java new file mode 100644 index 000000000..5dc61811c --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/util/XMPPAdapterListener.java @@ -0,0 +1,226 @@ +/* +* 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.device.mgt.iot.input.adapter.xmpp.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jivesoftware.smack.ConnectionConfiguration; +import org.jivesoftware.smack.PacketListener; +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.filter.AndFilter; +import org.jivesoftware.smack.filter.PacketFilter; +import org.jivesoftware.smack.filter.PacketTypeFilter; +import org.jivesoftware.smack.filter.ToContainsFilter; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Packet; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.core.ServerStatus; +import org.wso2.carbon.device.mgt.iot.input.adapter.ContentInfo; +import org.wso2.carbon.device.mgt.iot.input.adapter.ContentTransformer; +import org.wso2.carbon.device.mgt.iot.input.adapter.ContentValidator; +import org.wso2.carbon.device.mgt.iot.input.adapter.DefaultContentTransformer; +import org.wso2.carbon.device.mgt.iot.input.adapter.DefaultContentValidator; +import org.wso2.carbon.device.mgt.iot.input.adapter.xmpp.Constants; +import org.wso2.carbon.device.mgt.iot.input.adapter.xmpp.exception.XMPPContentInitializationException; +import org.wso2.carbon.event.input.adapter.core.InputEventAdapterListener; +import org.wso2.carbon.event.input.adapter.core.exception.InputEventAdapterRuntimeException; +import java.util.HashMap; +import java.util.Map; + +public class XMPPAdapterListener implements Runnable { + private static final Log log = LogFactory.getLog(XMPPAdapterListener.class); + + private XMPPConnection xmppConnection; + private XMPPServerConnectionConfiguration xmppServerConnectionConfiguration; + private int tenantId; + private boolean connectionSucceeded = false; + private ContentValidator contentValidator; + private Map contentValidationParams; + private ContentTransformer contentTransformer; + private PacketFilter packetFilter; + private PacketListener packetListener; + + private InputEventAdapterListener eventAdapterListener = null; + + + public XMPPAdapterListener(XMPPServerConnectionConfiguration xmppServerConnectionConfiguration, + InputEventAdapterListener inputEventAdapterListener, int tenantId) { + this.xmppServerConnectionConfiguration = xmppServerConnectionConfiguration; + this.eventAdapterListener = inputEventAdapterListener; + this.tenantId = tenantId; + + try { + String contentValidatorClassName = this.xmppServerConnectionConfiguration.getContentValidatorClassName(); + if (contentValidatorClassName != null && contentValidatorClassName.equals(Constants.DEFAULT)) { + contentValidator = new DefaultContentValidator(); + } else if (contentValidatorClassName != null && !contentValidatorClassName.isEmpty()) { + try { + Class contentValidatorClass = Class.forName(contentValidatorClassName) + .asSubclass(ContentValidator.class); + contentValidator = contentValidatorClass.newInstance(); + } catch (ClassNotFoundException e) { + throw new XMPPContentInitializationException( + "Unable to find the class validator: " + contentValidatorClassName, e); + } catch (InstantiationException e) { + throw new XMPPContentInitializationException( + "Unable to create an instance of :" + contentValidatorClassName, e); + } catch (IllegalAccessException e) { + throw new XMPPContentInitializationException("Access of the instance in not allowed.", e); + } + } + contentValidationParams = xmppServerConnectionConfiguration.getContentValidatorParams(); + + String contentTransformerClassName = this.xmppServerConnectionConfiguration.getContentTransformerClassName(); + if (contentTransformerClassName != null && contentTransformerClassName.equals(Constants.DEFAULT)) { + contentTransformer = new DefaultContentTransformer(); + } else if (contentTransformerClassName != null && !contentTransformerClassName.isEmpty()) { + try { + Class contentTransformerClass = Class.forName(contentTransformerClassName) + .asSubclass(ContentTransformer.class); + contentTransformer = contentTransformerClass.newInstance(); + } catch (ClassNotFoundException e) { + throw new XMPPContentInitializationException( + "Unable to find the class transformer: " + contentTransformerClassName, e); + } catch (InstantiationException e) { + throw new XMPPContentInitializationException( + "Unable to create an instance of :" + contentTransformerClassName, e); + } catch (IllegalAccessException e) { + throw new XMPPContentInitializationException("Access of the instance in not allowed.", e); + } + } + } catch (Throwable e) { + log.error("Exception occurred while subscribing to MQTT broker at " + + xmppServerConnectionConfiguration.getHost()); + throw new InputEventAdapterRuntimeException(e); + } + + } + + public void startListener() throws XMPPException { + SmackConfiguration.setPacketReplyTimeout(xmppServerConnectionConfiguration.getTimeoutInterval()); + ConnectionConfiguration config = new ConnectionConfiguration(xmppServerConnectionConfiguration.getHost(), + xmppServerConnectionConfiguration.getPort()); + config.setSASLAuthenticationEnabled(false); + config.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled); + xmppConnection = new XMPPConnection(config); + xmppConnection.connect(); + String resource = xmppServerConnectionConfiguration.getResource(); + String username = xmppServerConnectionConfiguration.getUsername(); + String password = xmppServerConnectionConfiguration.getPassword(); + try { + if (resource == null || resource.trim().isEmpty()) { + xmppConnection.login(username, password); + } else { + xmppConnection.login(username, password, resource); + } + setFilterOnReceiver(xmppServerConnectionConfiguration.getJid()); + } catch (XMPPException e) { + String errorMsg = "Login attempt to the XMPP Server with username - " + username + " failed."; + log.info(errorMsg); + throw new InputEventAdapterRuntimeException(errorMsg, e); + } + } + + public void stopListener(String adapterName) { + if (connectionSucceeded) { + // Un-subscribe accordingly and disconnect from the MQTT server. + if (!ServerStatus.getCurrentStatus().equals(ServerStatus.STATUS_SHUTTING_DOWN)) { + xmppConnection.removePacketListener(packetListener); + } + xmppConnection.disconnect(); + } + //This is to stop all running reconnection threads + connectionSucceeded = true; + } + + protected void setFilterOnReceiver(String receiverJID) { + packetFilter = new AndFilter(new PacketTypeFilter(Message.class), new ToContainsFilter( + receiverJID)); + packetListener = new PacketListener() { + @Override + public void processPacket(Packet packet) { + if (packet instanceof Message) { + final Message xmppMessage = (Message) packet; + Thread msgProcessThread = new Thread() { + public void run() { + processIncomingMessage(xmppMessage); + } + }; + msgProcessThread.start(); + } + } + }; + xmppConnection.addPacketListener(packetListener, packetFilter); + } + + public void processIncomingMessage(Message xmppMessage) { + try { + String from = xmppMessage.getFrom(); + String subject = xmppMessage.getSubject(); + String message = xmppMessage.getBody(); + if (log.isDebugEnabled()) { + log.debug(message); + } + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId); + + if (log.isDebugEnabled()) { + log.debug("Event received in MQTT Event Adapter - " + message); + } + + if (contentValidator != null && contentTransformer != null) { + Map dynamicParmaters = new HashMap<>(); + dynamicParmaters.put(Constants.FROM_KEY, from); + dynamicParmaters.put(Constants.SUBJECT_KEY, subject); + message = contentTransformer.transform(message, dynamicParmaters); + ContentInfo contentInfo = contentValidator.validate(message, contentValidationParams, dynamicParmaters); + if (contentInfo != null && contentInfo.isValidContent()) { + eventAdapterListener.onEvent(contentInfo.getMsgText()); + } + } else { + eventAdapterListener.onEvent(message); + } + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + } + + @Override + public void run() { + while (!connectionSucceeded) { + try { + XMPPEventAdapterConstants.initialReconnectDuration = XMPPEventAdapterConstants.initialReconnectDuration + * XMPPEventAdapterConstants.reconnectionProgressionFactor; + Thread.sleep(XMPPEventAdapterConstants.initialReconnectDuration); + startListener(); + connectionSucceeded = true; + log.info("XMPP Connection successful"); + } catch (InterruptedException e) { + log.error("Interruption occurred while waiting for reconnection", e); + } catch (XMPPException e) { + log.error("XMPP Exception occurred when starting listener", e); + } + } + } + + public void createConnection() { + new Thread(this).start(); + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/util/XMPPEventAdapterConstants.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/util/XMPPEventAdapterConstants.java new file mode 100644 index 000000000..dcd89c4c6 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/util/XMPPEventAdapterConstants.java @@ -0,0 +1,54 @@ +/* +* 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.device.mgt.iot.input.adapter.xmpp.util; + + +/** + * This holds the constants related to xmpp event adapter. + */ +public class XMPPEventAdapterConstants { + + public static final String ADAPTER_TYPE_XMPP = "xmpp"; + + //static properties + public static final String ADAPTER_CONF_HOST = "host"; + public static final String ADAPTER_CONF_HOST_HINT = "host.hint"; + public static final String ADAPTER_CONF_PORT = "port"; + public static final String ADAPTER_CONF_PORT_HINT = "port.hint"; + public static final String ADAPTER_CONF_USERNAME = "username"; + public static final String ADAPTER_CONF_USERNAME_HINT = "username.hint"; + public static final String ADAPTER_CONF_PASSWORD = "password"; + public static final String ADAPTER_CONF_PASSWORD_HINT = "password.hint"; + public static final String ADAPTER_CONF_RESOURCE = "resource"; + public static final String ADAPTER_CONF_RESOURCE_HINT = "resource.hint"; + public static final String ADAPTER_CONF_TIMEOUT_INTERVAL = "timeoutInterval"; + public static final String ADAPTER_CONF_TIMEOUT_INTERVAL_HINT = "timeoutInterval.hint"; + public static final String ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME = "contentValidation"; + public static final String ADAPTER_CONF_CONTENT_VALIDATOR_CLASSNAME_HINT = "contentValidation.hint"; + public static final String ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS = "contentValidationParams"; + public static final String ADAPTER_CONF_CONTENT_VALIDATOR_PARAMS_HINT = "contentValidationParams.hint"; + public static final String ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME = "contentTransformer"; + public static final String ADAPTER_CONF_CONTENT_TRANSFORMER_CLASSNAME_HINT = "contentTransformer.hint"; + public static final String ADAPTER_CONF_RECIEVER_JID = "jid"; + public static final String ADAPTER_CONF_RECIEVER_JID_HINT = "jid.hint"; + public static final int DEFAULT_XMPP_PORT = 5222; + public static final int DEFAULT_TIMEOUT_INTERVAL = 5000; + + public static int initialReconnectDuration = 10000; + public static final int reconnectionProgressionFactor = 2; +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/util/XMPPServerConnectionConfiguration.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/util/XMPPServerConnectionConfiguration.java new file mode 100644 index 000000000..34ffc8a11 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/java/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp/util/XMPPServerConnectionConfiguration.java @@ -0,0 +1,99 @@ +/* +* 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.device.mgt.iot.input.adapter.xmpp.util; + +import org.wso2.carbon.device.mgt.iot.input.adapter.util.PropertyUtils; +import org.wso2.carbon.device.mgt.iot.input.adapter.xmpp.Constants; + +import java.util.Map; + +/** + * This holds the configurations related to MQTT Broker. + */ +public class XMPPServerConnectionConfiguration { + + private String host; + private int port; + private String username; + private String password; + private int timeoutInterval; + private String resource; + private String jid; + private String contentValidatorClassName; + private Map contentValidatorParams; + private String contentTransformerClassName; + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public int getTimeoutInterval() { + return timeoutInterval; + } + + public String getResource() { + return resource; + } + + public String getContentValidatorClassName() { + return contentValidatorClassName; + } + + public Map getContentValidatorParams() { + return contentValidatorParams; + } + + public String getJid() { + return jid; + } + + public String getContentTransformerClassName() { + return contentTransformerClassName; + } + + public XMPPServerConnectionConfiguration(String host, int port, String username, String password, + int timeoutInterval, String resource, String contentValidatorClassName, + Map contentValidatorParams, String jid, + String contentTransformerClassName) { + this.host = host; + this.port = port; + this.username = username; + this.password = password; + this.timeoutInterval = timeoutInterval; + this.resource = resource; + this.contentValidatorClassName = contentValidatorClassName; + if (contentValidatorParams != null) { + this.contentValidatorParams = contentValidatorParams; + } + this.contentTransformerClassName = contentTransformerClassName; + this.jid = jid; + } + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/resources/org/wso2/carbon/device/mgt/iot/input/adapter/http/i18n/Resources.properties b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/resources/org/wso2/carbon/device/mgt/iot/input/adapter/http/i18n/Resources.properties new file mode 100644 index 000000000..f84f46ab1 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/resources/org/wso2/carbon/device/mgt/iot/input/adapter/http/i18n/Resources.properties @@ -0,0 +1,40 @@ +# +# Copyright (c) 2005-2014, 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. +# + +transports=Transport(s) +http.usage.tips_prefix=Following url formats are used to receive events
For super tenants:
  http://localhost: +http.usage.tips_mid1=/endpoints/<event_receiver_name>
  https://localhost: +http.usage.tips_mid2=/endpoints/<event_receiver_name>

For other tenants:
  http://localhost: +http.usage.tips_mid3=/endpoints/t/<tenant_domain>/<event_receiver_name>
  https://localhost: +http.usage.tips_postfix=/endpoints/t/<tenant_domain>/<event_receiver_name> +tokenValidationEndpointUrl=tokenEndpointUrl +tokenValidationEndpointUrl.hint=OAUTH Token Validation Endpoint +username=username +username.hint=username of the user to connect to the admin services +password=password +password.hint=password of the user to connect to the admin services. +maximumTotalHttpConnection=maximumTotalHttpConnection +maximumTotalHttpConnection.hint=Maximum Total connection to be made with the endpoint +maximumHttpConnectionPerHost=maximumHttpConnectionPerHost +maximumHttpConnectionPerHost.hint=Maximum Http connection per host. +contentValidation=contentValidation +contentValidation.hint=Class Name of the content Validation or 'default' to set default class, required to implement (if required) +contentValidationParams=contentValidationParams +contentValidationParams.hint=ContentValidationParams, comma seperated. (if required) +contentTransformer=contentTransformer +contentTransformer.hint=Class Name of the content transformer or 'default' to set default class, required to implement (if required) \ No newline at end of file diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/resources/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/i18n/Resources.properties b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/resources/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/i18n/Resources.properties new file mode 100644 index 000000000..d1f6676ed --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/resources/org/wso2/carbon/device/mgt/iot/input/adapter/mqtt/i18n/Resources.properties @@ -0,0 +1,40 @@ +# +# 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. +# + +topic=Topic +topic.hint=Topic subscribed +clientId=Client Id +clientId.hint=client identifier is used by the server to identify a client when it reconnects, It used for durable subscriptions or reliable delivery of messages is required. +url=Broker Url +username=Username +username.hint=Username of the broker (if required) +scopes=Scopes +scopes.hint=Scopes required to connect to broker (if required) +dcrUrl=dcrUrl +dcrUrl.hint=dynamic client registration endpoint URL to create application (if required) eg: https://localhost:9443/dynamic-client-web/register +contentValidation=contentValidation +contentValidation.hint=Class Name of the content Validation or 'default' to set default class, required to implement (if required) +contentValidationParams=contentValidationParams +contentValidationParams.hint=ContentValidationParams, comma seperated. (if required) +url.hint=MQTT broker url tcp://localhost:1883 +cleanSession=Clean Session +cleanSession.hint=Persist topic subscriptions and ack positions across client sessions +keepAlive=Keep Alive (In seconds) +events.duplicated.in.cluster=Is events duplicated in cluster +contentTransformer=contentTransformer +contentTransformer.hint=Class Name of the content transformer or 'default' to set default class, required to implement (if required) \ No newline at end of file diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/resources/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp.i18n/Resources.properties b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/resources/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp.i18n/Resources.properties new file mode 100644 index 000000000..95a62ec03 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.input.adapter/src/main/resources/org/wso2/carbon/device/mgt/iot/input/adapter/xmpp.i18n/Resources.properties @@ -0,0 +1,38 @@ +# +# 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. +# + +host=Server Url +host.hint=XMPP Server url +port=Server Port +port.hint=Server Port Number +username=Username +username.hint=Username of the broker +password=Password +password.hint=Password to connect to the server. +timeoutInterval=Time out Interval +timeoutInterval.hint=used by listeners to the server and for reconnection schedules. +resource=Resource +resource.hint=specific to the XMPP-Account to which the login is made to. +contentValidation=contentValidation +contentValidation.hint=Class Name of the content Validation or 'default' to set default class, required to implement (if required) +contentValidationParams=contentValidationParams +contentValidationParams.hint=ContentValidationParams, comma seperated. (if required) +jid=jid +jid.hint=JID - XMPP Account Name. +contentTransformer=contentTransformer +contentTransformer.hint=Class Name of the content transformer or 'default' to set default class, required to implement (if required) \ No newline at end of file diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/pom.xml b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/pom.xml new file mode 100644 index 000000000..b427bfdf9 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/pom.xml @@ -0,0 +1,132 @@ + + + + + iot-base-plugin + org.wso2.carbon.devicemgt-plugins + 2.1.0-SNAPSHOT + ../pom.xml + + 4.0.0 + org.wso2.carbon.device.mgt.iot.output.adapter.mqtt + + bundle + WSO2 Carbon - Device Mgt Output MQTT Adaptor Module + org.wso2.carbon.device.mgt.iot.output.adapter.xmpp provides the back-end functionality of mqtt adaptor + + http://wso2.org + + + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.output.adapter.core + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + + + org.wso2.carbon + org.wso2.carbon.core + + + org.wso2.carbon + org.wso2.carbon.logging + + + org.apache.httpcomponents.wso2 + httpcore + + + org.wso2.orbit.org.apache.httpcomponents + httpclient + + + com.googlecode.json-simple.wso2 + json-simple + + + org.wso2.carbon.devicemgt + org.wso2.carbon.identity.jwt.client.extension + + + + + + org.apache.felix + maven-scr-plugin + + + generate-scr-descriptor + + scr + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.artifactId} + ${project.artifactId} + + org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.internal, + org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.internal.* + + + !org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.internal, + !org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.internal.*, + org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.*, + + + org.wso2.carbon.event.output.adapter.core, + org.wso2.carbon.event.output.adapter.core.*, + javax.net.ssl, + org.apache.commons.logging, + org.apache.http, + org.apache.http.client, + org.apache.http.client.methods, + org.apache.http.conn.socket, + org.apache.http.conn.ssl, + org.apache.http.entity, + org.apache.http.impl.client, + org.apache.http.util, + org.eclipse.paho.client.mqttv3, + org.eclipse.paho.client.mqttv3.persist, + org.json.simple, + org.json.simple.parser, + org.osgi.framework, + org.osgi.service.component, + org.wso2.carbon.context, + org.wso2.carbon.identity.jwt.client.extension.* + + + + + + + \ No newline at end of file diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/MQTTEventAdapter.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/MQTTEventAdapter.java new file mode 100644 index 000000000..b8402db2d --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/MQTTEventAdapter.java @@ -0,0 +1,204 @@ +/* +* 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.device.mgt.iot.output.adapter.xmpp; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.util.MQTTAdapterPublisher; +import org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.util.MQTTBrokerConnectionConfiguration; +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.device.mgt.iot.output.adapter.xmpp.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 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 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( + 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_DCR_URL), + eventAdapterConfiguration.getStaticProperties() + .get(MQTTEventAdapterConstants.ADAPTER_CONF_SCOPES), + 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 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 (!mqttAdapterPublisher.isConnected()) { + synchronized (MQTTEventAdapter.class) { + if (!mqttAdapterPublisher.isConnected()) { + mqttAdapterPublisher.connect(); + } + } + } + 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); + } + } + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/MQTTEventAdapterFactory.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/MQTTEventAdapterFactory.java new file mode 100644 index 000000000..fd2abb182 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/MQTTEventAdapterFactory.java @@ -0,0 +1,126 @@ +/* +* 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.device.mgt.iot.output.adapter.xmpp; + +import org.wso2.carbon.event.output.adapter.core.*; +import org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.util.MQTTEventAdapterConstants; + +import java.util.*; + +/** + * The mqtt event adapter factory class to create a mqtt output adapter + */ +public class MQTTEventAdapterFactory extends OutputEventAdapterFactory { + private ResourceBundle resourceBundle = + ResourceBundle.getBundle("org.wso2.carbon.device.mgt.iot.output.adapter.mqtt.i18n.Resources", Locale.getDefault()); + + @Override + public String getType() { + return MQTTEventAdapterConstants.ADAPTER_TYPE_MQTT; + } + + @Override + public List getSupportedMessageFormats() { + List supportedMessageFormats = new ArrayList(); + supportedMessageFormats.add(MessageType.XML); + supportedMessageFormats.add(MessageType.JSON); + supportedMessageFormats.add(MessageType.TEXT); + return supportedMessageFormats; + } + + @Override + public List getStaticPropertyList() { + + List staticPropertyList = new ArrayList(); + //Broker Url + Property brokerUrl = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_URL); + brokerUrl.setDisplayName(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_URL)); + brokerUrl.setRequired(true); + brokerUrl.setHint(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_URL_HINT)); + + //Broker Username + Property userName = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_USERNAME); + userName.setDisplayName(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_USERNAME)); + userName.setRequired(true); + userName.setHint(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_USERNAME_HINT)); + + //Broker dcr URL + Property dcrUrl = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_DCR_URL); + dcrUrl.setDisplayName(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_DCR_URL)); + dcrUrl.setRequired(true); + dcrUrl.setHint(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_DCR_URL_HINT)); + + //Broker Connection Scopes + Property scopes = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_SCOPES); + scopes.setDisplayName(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_SCOPES)); + scopes.setRequired(true); + scopes.setHint(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_SCOPES_HINT)); + + // set clientId + Property clientId = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_CLIENTID); + clientId.setDisplayName(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_CLIENTID)); + clientId.setRequired(false); + clientId.setHint(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_CLIENTID_HINT)); + staticPropertyList.add(clientId); + + //Broker clear session + Property clearSession = new Property(MQTTEventAdapterConstants.ADAPTER_CONF_CLEAN_SESSION); + clearSession.setDisplayName(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_CLEAN_SESSION)); + clearSession.setRequired(false); + clearSession.setOptions(new String[]{"true", "false"}); + clearSession.setDefaultValue("true"); + clearSession.setHint(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_CONF_CLEAN_SESSION_HINT)); + + // set Quality of Service + Property qos = new Property(MQTTEventAdapterConstants.ADAPTER_MESSAGE_QOS); + qos.setDisplayName(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_MESSAGE_QOS)); + qos.setRequired(true); + qos.setOptions(new String[]{"0", "1", "2"}); + qos.setDefaultValue("1"); + + staticPropertyList.add(brokerUrl); + staticPropertyList.add(userName); + staticPropertyList.add(dcrUrl); + staticPropertyList.add(scopes); + staticPropertyList.add(clearSession); + staticPropertyList.add(qos); + return staticPropertyList; + } + + @Override + public List getDynamicPropertyList() { + List dynamicPropertyList = new ArrayList(); + // set topic + Property topicProperty = new Property(MQTTEventAdapterConstants.ADAPTER_MESSAGE_TOPIC); + topicProperty.setDisplayName(resourceBundle.getString(MQTTEventAdapterConstants.ADAPTER_MESSAGE_TOPIC)); + topicProperty.setRequired(true); + dynamicPropertyList.add(topicProperty); + return dynamicPropertyList; + } + + @Override + public String getUsageTips() { + return null; + } + + @Override + public OutputEventAdapter createEventAdapter(OutputEventAdapterConfiguration eventAdapterConfiguration, + Map globalProperties) { + return new MQTTEventAdapter(eventAdapterConfiguration, globalProperties); + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/internal/MQTTEventAdapterServiceComponent.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/internal/MQTTEventAdapterServiceComponent.java new file mode 100644 index 000000000..8af149983 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/internal/MQTTEventAdapterServiceComponent.java @@ -0,0 +1,51 @@ +/* +* 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.device.mgt.iot.output.adapter.xmpp.internal; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.service.component.ComponentContext; +import org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.MQTTEventAdapterFactory; +import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterFactory; + + +/** + * @scr.component component.name="output.Mqtt.AdapterService.component" immediate="true" + */ +public class MQTTEventAdapterServiceComponent { + + private static final Log log = LogFactory.getLog(MQTTEventAdapterServiceComponent.class); + + /** + * Deployment of the MQTT event adapter service will be done. + * @param context bundle context where service is registered + */ + protected void activate(ComponentContext context) { + try { + OutputEventAdapterFactory mqttEventAdapterFactory = new MQTTEventAdapterFactory(); + context.getBundleContext().registerService(OutputEventAdapterFactory.class.getName(), + mqttEventAdapterFactory, null); + if (log.isDebugEnabled()) { + log.debug("The MQTT publisher service has been deployed successfully"); + } + } catch (RuntimeException e) { + log.error("Exception occurred when deploying MQTT publisher service", e); + } + } + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/Constants.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/Constants.java new file mode 100644 index 000000000..7a36d247a --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/Constants.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016, 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.device.mgt.iot.output.adapter.xmpp.util; + +/** + * This holds the constants related to MQTT input adapter. + */ +public class Constants { + public static final String DEFAULT_CALLBACK = ""; + public static final String DEFAULT_PASSWORD = ""; + public static final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer refresh_token"; + public static final String TOKEN_SCOPE = "production"; + public static final String APPLICATION_TYPE = "device"; + public static final String CLIENT_ID = "client_id"; + public static final String CLIENT_SECRET = "client_secret"; + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/MQTTAdapterPublisher.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/MQTTAdapterPublisher.java new file mode 100644 index 000000000..c7e3103da --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/MQTTAdapterPublisher.java @@ -0,0 +1,191 @@ +/* +* 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.device.mgt.iot.output.adapter.xmpp.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.event.output.adapter.core.exception.ConnectionUnavailableException; +import org.wso2.carbon.event.output.adapter.core.exception.OutputEventAdapterException; +import org.wso2.carbon.event.output.adapter.core.exception.OutputEventAdapterRuntimeException; +import org.wso2.carbon.identity.jwt.client.extension.dto.AccessTokenInfo; +import org.wso2.carbon.identity.jwt.client.extension.exception.JWTClientException; +import org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; + +/** + * MQTT publisher related configuration initialization and publishing capabilties are implemented here. + */ +public class MQTTAdapterPublisher { + + private static final Log log = LogFactory.getLog(MQTTAdapterPublisher.class); + private MqttClient mqttClient; + private MQTTBrokerConnectionConfiguration mqttBrokerConnectionConfiguration; + String clientId; + + public MQTTAdapterPublisher(MQTTBrokerConnectionConfiguration mqttBrokerConnectionConfiguration, String clientId) { + if (clientId == null || clientId.trim().isEmpty()) { + this.clientId = MqttClient.generateClientId(); + } + this.mqttBrokerConnectionConfiguration = mqttBrokerConnectionConfiguration; + connect(); + } + + public void connect() { + if (clientId == null || clientId.trim().isEmpty()) { + clientId = MqttClient.generateClientId(); + } + boolean cleanSession = mqttBrokerConnectionConfiguration.isCleanSession(); + int keepAlive = mqttBrokerConnectionConfiguration.getKeepAlive(); + String temp_directory = System.getProperty(MQTTEventAdapterConstants.ADAPTER_TEMP_DIRECTORY_NAME); + MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(temp_directory); + try { + MqttConnectOptions connectionOptions = new MqttConnectOptions(); + connectionOptions.setCleanSession(cleanSession); + connectionOptions.setKeepAliveInterval(keepAlive); + if (mqttBrokerConnectionConfiguration.getBrokerUsername() != null) { + connectionOptions.setUserName(getToken(mqttBrokerConnectionConfiguration.getBrokerUsername(), + mqttBrokerConnectionConfiguration.getDcrUrl(), + mqttBrokerConnectionConfiguration.getScopes())); + connectionOptions.setPassword(Constants.DEFAULT_PASSWORD.toCharArray()); + } + // Construct an MQTT blocking mode client + mqttClient = new MqttClient(mqttBrokerConnectionConfiguration.getBrokerUrl(), clientId, dataStore); + mqttClient.connect(connectionOptions); + + } catch (MqttException e) { + log.error("Error occurred when constructing MQTT client for broker url : " + + mqttBrokerConnectionConfiguration.getBrokerUrl(), e); + handleException(e); + } + } + + public boolean isConnected() { + return mqttClient.isConnected(); + } + + public void publish(int qos, String payload, String topic) { + try { + // Create and configure a message + MqttMessage message = new MqttMessage(payload.getBytes()); + message.setQos(qos); + mqttClient.publish(topic, message); + } catch (MqttException e) { + log.error("Error occurred when publishing message for MQTT server : " + mqttClient.getServerURI(), e); + handleException(e); + } + } + + public void publish(String payload, String topic) { + try { + // Create and configure a message + MqttMessage message = new MqttMessage(payload.getBytes()); + mqttClient.publish(topic, message); + } catch (MqttException e) { + log.error("Error occurred when publishing message for MQTT server : " + mqttClient.getServerURI(), e); + handleException(e); + } + } + + public void close() throws OutputEventAdapterException { + try { + mqttClient.disconnect(1000); + mqttClient.close(); + } catch (MqttException e) { + throw new OutputEventAdapterException(e); + } + } + + private void handleException(MqttException e) { + //Check for Client not connected exception code and throw ConnectionUnavailableException + if (e.getReasonCode() == 32104) { + throw new ConnectionUnavailableException(e); + } else { + throw new OutputEventAdapterRuntimeException(e); + } + } + + private String getToken(String username, String dcrUrlString, String scopes) { + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); + if (dcrUrlString != null && !dcrUrlString.isEmpty()) { + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId, true); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(username); + try { + URL dcrUrl = new URL(dcrUrlString); + HttpClient httpClient = MQTTUtil.getHttpClient(dcrUrl.getProtocol()); + HttpPost postMethod = new HttpPost(dcrUrlString); + RegistrationProfile registrationProfile = new RegistrationProfile(); + registrationProfile.setCallbackUrl(Constants.DEFAULT_CALLBACK); + registrationProfile.setGrantType(Constants.GRANT_TYPE); + registrationProfile.setOwner(username); + registrationProfile.setTokenScope(Constants.TOKEN_SCOPE); + registrationProfile.setApplicationType(Constants.APPLICATION_TYPE); + registrationProfile.setClientName(username + "_" + tenantId); + String jsonString = registrationProfile.toJSON(); + StringEntity requestEntity = new StringEntity(jsonString, ContentType.APPLICATION_JSON); + postMethod.setEntity(requestEntity); + HttpResponse httpResponse = httpClient.execute(postMethod); + String response = MQTTUtil.getResponseString(httpResponse); + try { + JSONParser jsonParser = new JSONParser(); + JSONObject jsonPayload = (JSONObject) jsonParser.parse(response); + String clientId = (String) jsonPayload.get(Constants.CLIENT_ID); + String clientSecret = (String) jsonPayload.get(Constants.CLIENT_SECRET); + JWTClientManagerService jwtClientManagerService = MQTTUtil.getJWTClientManagerService(); + AccessTokenInfo accessTokenInfo = jwtClientManagerService.getJWTClient().getAccessToken( + clientId, clientSecret, username, scopes); + return accessTokenInfo.getAccessToken(); + } catch (ParseException e) { + String msg = "error occurred while parsing client credential payload"; + throw new OutputEventAdapterRuntimeException(msg, e); + } catch (JWTClientException e) { + String msg = "error occurred while parsing the response from JWT Client"; + throw new OutputEventAdapterRuntimeException(msg, e); + } + } catch (MalformedURLException e) { + throw new OutputEventAdapterRuntimeException("Invalid dcrUrl : " + dcrUrlString); + } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | IOException e) { + throw new OutputEventAdapterRuntimeException("Failed to create an https connection.", e); + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + } + throw new OutputEventAdapterRuntimeException("Invalid configuration for mqtt publisher"); + } + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/MQTTBrokerConnectionConfiguration.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/MQTTBrokerConnectionConfiguration.java new file mode 100644 index 000000000..1dde59d31 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/MQTTBrokerConnectionConfiguration.java @@ -0,0 +1,65 @@ +/* +* 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.device.mgt.iot.output.adapter.xmpp.util; + +public class MQTTBrokerConnectionConfiguration { + + private String brokerUsername; + private String dcrUrl; + private String scopes; + private String brokerUrl; + private boolean cleanSession = true; + private int keepAlive; + + public String getDcrUrl() { + return dcrUrl; + } + + public String getScopes() { + return scopes; + } + + public String getBrokerUsername() { + return brokerUsername; + } + + public boolean isCleanSession() { + return cleanSession; + } + + public String getBrokerUrl() { + return brokerUrl; + } + + public int getKeepAlive() { + return keepAlive; + } + + public MQTTBrokerConnectionConfiguration(String brokerUrl, String brokerUsername, + String dcrUrl, String scopes, int keepAlive, String cleanSession) { + this.brokerUsername = brokerUsername; + this.dcrUrl = dcrUrl; + this.scopes = scopes; + this.brokerUrl = brokerUrl; + this.keepAlive = keepAlive; + if (cleanSession != null) { + this.cleanSession = Boolean.parseBoolean(cleanSession); + } + } + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/MQTTEventAdapterConstants.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/MQTTEventAdapterConstants.java new file mode 100644 index 000000000..f2586c281 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/MQTTEventAdapterConstants.java @@ -0,0 +1,54 @@ +/* +* 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.device.mgt.iot.output.adapter.xmpp.util; + + +public final class MQTTEventAdapterConstants { + + private MQTTEventAdapterConstants() { + } + + public static final String ADAPTER_TYPE_MQTT = "oauth-mqtt"; + public static final String ADAPTER_CONF_URL = "url"; + public static final String ADAPTER_CONF_USERNAME = "username"; + public static final String ADAPTER_CONF_USERNAME_HINT = "username.hint"; + public static final String ADAPTER_CONF_DCR_URL = "dcrUrl"; + public static final String ADAPTER_CONF_DCR_URL_HINT = "dcrUrl.hint"; + public static final String ADAPTER_CONF_SCOPES = "scopes"; + public static final String ADAPTER_CONF_SCOPES_HINT = "scopes.hint"; + public static final String ADAPTER_CONF_URL_HINT = "url.hint"; + public static final String ADAPTER_MESSAGE_TOPIC = "topic"; + public static final String ADAPTER_MESSAGE_QOS = "qos"; + public static final String ADAPTER_CONF_CLEAN_SESSION = "cleanSession"; + public static final String ADAPTER_CONF_CLEAN_SESSION_HINT = "cleanSession.hint"; + public static final String CONNECTION_KEEP_ALIVE_INTERVAL = "connectionKeepAliveInterval"; + public static final int DEFAULT_CONNECTION_KEEP_ALIVE_INTERVAL = 60; + public static final String ADAPTER_TEMP_DIRECTORY_NAME = "java.io.tmpdir"; + public static final String ADAPTER_CONF_CLIENTID = "clientId"; + public static final String ADAPTER_CONF_CLIENTID_HINT = "clientId.hint"; + + public static final int DEFAULT_MIN_THREAD_POOL_SIZE = 8; + public static final int DEFAULT_MAX_THREAD_POOL_SIZE = 100; + public static final int DEFAULT_EXECUTOR_JOB_QUEUE_SIZE = 2000; + public static final long DEFAULT_KEEP_ALIVE_TIME_IN_MILLIS = 20000; + public static final String ADAPTER_MIN_THREAD_POOL_SIZE_NAME = "minThread"; + public static final String ADAPTER_MAX_THREAD_POOL_SIZE_NAME = "maxThread"; + public static final String ADAPTER_KEEP_ALIVE_TIME_NAME = "keepAliveTimeInMillis"; + public static final String ADAPTER_EXECUTOR_JOB_QUEUE_SIZE_NAME = "jobQueueSize"; + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/MQTTUtil.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/MQTTUtil.java new file mode 100644 index 000000000..01c185495 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/MQTTUtil.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2016, 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.device.mgt.iot.output.adapter.xmpp.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContextBuilder; +import org.apache.http.conn.ssl.TrustSelfSignedStrategy; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; + +/** + * This is the utility class that is used for MQTT input adapater. + */ +public class MQTTUtil { + private static final String HTTPS_PROTOCOL = "https"; + private static final Log log = LogFactory.getLog(MQTTUtil.class); + /** + * Return a http client instance + * @param protocol- service endpoint protocol http/https + * @return + */ + public static HttpClient getHttpClient(String protocol) + throws IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException { + HttpClient httpclient; + if (HTTPS_PROTOCOL.equals(protocol)) { + SSLContextBuilder builder = new SSLContextBuilder(); + builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build()); + httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); + } else { + httpclient = HttpClients.createDefault(); + } + return httpclient; + } + + public static String getResponseString(HttpResponse httpResponse) throws IOException { + BufferedReader br = null; + try { + br = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent())); + String readLine; + String response = ""; + while (((readLine = br.readLine()) != null)) { + response += readLine; + } + return response; + } finally { + EntityUtils.consumeQuietly(httpResponse.getEntity()); + if (br != null) { + try { + br.close(); + } catch (IOException e) { + log.warn("Error while closing the connection! " + e.getMessage()); + } + } + } + } + + public static JWTClientManagerService getJWTClientManagerService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + JWTClientManagerService jwtClientManagerService = + (JWTClientManagerService) ctx.getOSGiService(JWTClientManagerService.class, null); + if (jwtClientManagerService == null) { + String msg = "JWT management service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return jwtClientManagerService; + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/RegistrationProfile.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/RegistrationProfile.java new file mode 100644 index 000000000..9dc855654 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/RegistrationProfile.java @@ -0,0 +1,73 @@ +package org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.util; + +/** + * This class represents the data that are required to register + * the oauth application. + */ +public class RegistrationProfile { + + private String callbackUrl; + private String clientName; + private String tokenScope; + private String owner; + private String grantType; + private String applicationType; + + private static final String TAG = RegistrationProfile.class.getSimpleName(); + + public String getCallbackUrl() { + return callbackUrl; + } + + public void setCallbackUrl(String callBackUrl) { + this.callbackUrl = callBackUrl; + } + + public String getClientName() { + return clientName; + } + + public void setClientName(String clientName) { + this.clientName = clientName; + } + + public String getTokenScope() { + return tokenScope; + } + + public void setTokenScope(String tokenScope) { + this.tokenScope = tokenScope; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getGrantType() { + return grantType; + } + + public void setGrantType(String grantType) { + this.grantType = grantType; + } + + public String getApplicationType() { + return applicationType; + } + + public void setApplicationType(String applicationType) { + this.applicationType = applicationType; + } + + public String toJSON() { + String jsonString = + "{\"callbackUrl\": \"" + callbackUrl + "\",\"clientName\": \"" + clientName + "\", \"tokenScope\": " + + "\"" + tokenScope + "\", \"owner\": \"" + owner + "\"," + "\"grantType\": \"" + grantType + + "\", \"saasApp\" :false }\n"; + return jsonString; + } +} \ No newline at end of file diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/resources/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/i18n/Resources.properties b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/resources/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/i18n/Resources.properties new file mode 100644 index 000000000..c73a259ef --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.mqtt/src/main/resources/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/i18n/Resources.properties @@ -0,0 +1,33 @@ +# +# 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. +# + +topic=Topic +url=Broker Url +username=Username +username.hint=Username of the broker +dcrUrl=dcrUrl +dcrUrl.hint=Dynamic Client Registration Url +scopes=scopes +scopes.hint=Scopes that required to connect with the broker. +url.hint=MQTT broker url +cleanSession=Clean Session +cleanSession.hint=Persist topic subscriptions and ack positions across client sessions +keepAlive=Keep Alive (In seconds) +qos=Quality of Service +clientId=Client Id +clientId.hint=client identifier is used by the server to identify a client when it reconnects, It used for durable subscriptions or reliable delivery of messages is required. diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/pom.xml b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/pom.xml new file mode 100644 index 000000000..e991fa1b6 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/pom.xml @@ -0,0 +1,107 @@ + + + + + iot-base-plugin + org.wso2.carbon.devicemgt-plugins + 2.1.0-SNAPSHOT + ../pom.xml + + 4.0.0 + org.wso2.carbon.device.mgt.iot.output.adapter.xmpp + + bundle + WSO2 Carbon - Device Mgt Output MQTT Adaptor Module + org.wso2.carbon.device.mgt.iot.output.adapter.xmpp provides the back-end functionality of xmpp adaptor + + http://wso2.org + + + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.output.adapter.core + + + org.wso2.carbon + org.wso2.carbon.core + + + org.wso2.carbon + org.wso2.carbon.logging + + + org.igniterealtime.smack.wso2 + smack + + + org.igniterealtime.smack.wso2 + smackx + + + + + + org.apache.felix + maven-scr-plugin + + + generate-scr-descriptor + + scr + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.artifactId} + ${project.artifactId} + + org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.internal, + org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.internal.* + + + !org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.internal, + !org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.internal.*, + org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.*, + + + org.wso2.carbon.event.output.adapter.core, + org.wso2.carbon.event.output.adapter.core.*, + org.jivesoftware.smack.*, + org.apache.commons.logging, + org.osgi.framework, + org.osgi.service.component, + org.wso2.carbon.context + + + + + + + \ No newline at end of file diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/XMPPEventAdapter.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/XMPPEventAdapter.java new file mode 100644 index 000000000..211ab3d84 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/XMPPEventAdapter.java @@ -0,0 +1,200 @@ +/* +* 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.device.mgt.iot.output.adapter.xmpp; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.util.XMPPAdapterPublisher; +import org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.util.XMPPServerConnectionConfiguration; +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.device.mgt.iot.output.adapter.xmpp.util.XMPPEventAdapterConstants; + +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 XMPPEventAdapter will be used to publish events with MQTT protocol to specified broker and topic. + */ +public class XMPPEventAdapter implements OutputEventAdapter { + + private OutputEventAdapterConfiguration eventAdapterConfiguration; + private Map globalProperties; + private XMPPAdapterPublisher xmppAdapterPublisher; + private static ThreadPoolExecutor threadPoolExecutor; + private static final Log log = LogFactory.getLog(XMPPEventAdapter.class); + private int tenantId; + + public XMPPEventAdapter(OutputEventAdapterConfiguration eventAdapterConfiguration, + Map globalProperties) { + this.eventAdapterConfiguration = eventAdapterConfiguration; + this.globalProperties = globalProperties; + } + + @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(XMPPEventAdapterConstants.ADAPTER_MIN_THREAD_POOL_SIZE_NAME) != null) { + minThread = Integer.parseInt(globalProperties.get( + XMPPEventAdapterConstants.ADAPTER_MIN_THREAD_POOL_SIZE_NAME)); + } else { + minThread = XMPPEventAdapterConstants.DEFAULT_MIN_THREAD_POOL_SIZE; + } + + if (globalProperties.get(XMPPEventAdapterConstants.ADAPTER_MAX_THREAD_POOL_SIZE_NAME) != null) { + maxThread = Integer.parseInt(globalProperties.get( + XMPPEventAdapterConstants.ADAPTER_MAX_THREAD_POOL_SIZE_NAME)); + } else { + maxThread = XMPPEventAdapterConstants.DEFAULT_MAX_THREAD_POOL_SIZE; + } + + if (globalProperties.get(XMPPEventAdapterConstants.ADAPTER_KEEP_ALIVE_TIME_NAME) != null) { + defaultKeepAliveTime = Integer.parseInt(globalProperties.get( + XMPPEventAdapterConstants.ADAPTER_KEEP_ALIVE_TIME_NAME)); + } else { + defaultKeepAliveTime = XMPPEventAdapterConstants.DEFAULT_KEEP_ALIVE_TIME_IN_MILLIS; + } + + if (globalProperties.get(XMPPEventAdapterConstants.ADAPTER_EXECUTOR_JOB_QUEUE_SIZE_NAME) != null) { + jobQueSize = Integer.parseInt(globalProperties.get( + XMPPEventAdapterConstants.ADAPTER_EXECUTOR_JOB_QUEUE_SIZE_NAME)); + } else { + jobQueSize = XMPPEventAdapterConstants.DEFAULT_EXECUTOR_JOB_QUEUE_SIZE; + } + + threadPoolExecutor = new ThreadPoolExecutor(minThread, maxThread, defaultKeepAliveTime, + TimeUnit.MILLISECONDS, new LinkedBlockingQueue( + jobQueSize)); + } + } + + @Override + public void testConnect() throws TestConnectionNotSupportedException { + throw new TestConnectionNotSupportedException("Test connection is not available"); + } + + @Override + public void connect() { + int xmppPort = XMPPEventAdapterConstants.DEFAULT_XMPP_PORT; + String xmppPortString = eventAdapterConfiguration.getStaticProperties() + .get(XMPPEventAdapterConstants.ADAPTER_CONF_PORT); + if (xmppPortString != null && !xmppPortString.trim().isEmpty()) { + xmppPort = Integer.parseInt(xmppPortString); + } + int timeoutInterval = XMPPEventAdapterConstants.DEFAULT_TIMEOUT_INTERVAL; + String timeoutIntervalString = eventAdapterConfiguration.getStaticProperties().get( + XMPPEventAdapterConstants.ADAPTER_CONF_TIMEOUT_INTERVAL); + if (timeoutIntervalString != null && !timeoutIntervalString.trim().isEmpty()) { + timeoutInterval = Integer.parseInt(timeoutIntervalString); + } + XMPPServerConnectionConfiguration xmppServerConnectionConfiguration = + new XMPPServerConnectionConfiguration(eventAdapterConfiguration.getStaticProperties() + .get(XMPPEventAdapterConstants.ADAPTER_CONF_HOST), + xmppPort, + eventAdapterConfiguration.getStaticProperties() + .get(XMPPEventAdapterConstants.ADAPTER_CONF_USERNAME), + eventAdapterConfiguration.getStaticProperties() + .get(XMPPEventAdapterConstants.ADAPTER_CONF_PASSWORD), + timeoutInterval, + eventAdapterConfiguration.getStaticProperties() + .get(XMPPEventAdapterConstants.ADAPTER_CONF_RESOURCE) + ); + xmppAdapterPublisher = new XMPPAdapterPublisher(xmppServerConnectionConfiguration); + } + + @Override + public void publish(Object message, Map dynamicProperties) { + String jid = dynamicProperties.get(XMPPEventAdapterConstants.ADAPTER_CONF_JID); + String subject = dynamicProperties.get(XMPPEventAdapterConstants.ADAPTER_CONF_SUBJECT); + String messageType = dynamicProperties.get(XMPPEventAdapterConstants.ADAPTER_CONF_MESSAGETYPE); + try { + threadPoolExecutor.submit(new XMPPSender(jid, subject, (String)message, messageType)); + } catch (RejectedExecutionException e) { + EventAdapterUtil.logAndDrop(eventAdapterConfiguration.getName(), message, "Job queue is full", e, log, + tenantId); + } + } + + @Override + public void disconnect() { + try { + if (xmppAdapterPublisher != null) { + xmppAdapterPublisher.close(); + xmppAdapterPublisher = 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 XMPPSender implements Runnable { + + String jid; + String subject; + String message; + String messageType; + + XMPPSender(String jid, String subject, String message, String messageType) { + this.jid = jid; + this.message = message; + this.subject = subject; + this.messageType = messageType; + } + + @Override + public void run() { + try { + if (!xmppAdapterPublisher.isConnected()) { + synchronized (XMPPEventAdapter.class) { + if (!xmppAdapterPublisher.isConnected()) { + xmppAdapterPublisher.connect(); + } + } + } + xmppAdapterPublisher.publish(jid, subject, message, messageType); + } catch (Throwable t) { + EventAdapterUtil.logAndDrop(eventAdapterConfiguration.getName(), message, null, t, log, tenantId); + } + } + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/XMPPEventAdapterFactory.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/XMPPEventAdapterFactory.java new file mode 100644 index 000000000..687672120 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/XMPPEventAdapterFactory.java @@ -0,0 +1,132 @@ +/* +* 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.device.mgt.iot.output.adapter.xmpp; + +import org.wso2.carbon.event.output.adapter.core.*; +import org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.util.XMPPEventAdapterConstants; + +import java.util.*; + +/** + * The xmpp event adapter factory class to create a xmpp output adapter + */ +public class XMPPEventAdapterFactory extends OutputEventAdapterFactory { + private ResourceBundle resourceBundle = + ResourceBundle.getBundle("org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.i18n.Resources", Locale.getDefault()); + + @Override + public String getType() { + return XMPPEventAdapterConstants.ADAPTER_TYPE_XMPP; + } + + @Override + public List getSupportedMessageFormats() { + List supportedMessageFormats = new ArrayList(); + supportedMessageFormats.add(MessageType.XML); + supportedMessageFormats.add(MessageType.JSON); + supportedMessageFormats.add(MessageType.TEXT); + return supportedMessageFormats; + } + + @Override + public List getStaticPropertyList() { + + List staticPropertyList = new ArrayList(); + // Url + Property host = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_HOST); + host.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_HOST)); + host.setRequired(true); + host.setHint(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_HOST_HINT)); + + // Host Port + Property port = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_PORT); + port.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_PORT)); + port.setRequired(true); + port.setHint(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_PORT_HINT)); + + //Broker Username + Property userName = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_USERNAME); + userName.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_USERNAME)); + userName.setRequired(true); + userName.setHint(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_USERNAME_HINT)); + + //Broker Password + Property password = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_PASSWORD); + password.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_PASSWORD)); + password.setRequired(true); + password.setHint(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_PASSWORD_HINT)); + + //Timeout Interval + Property timooutInterval = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_TIMEOUT_INTERVAL); + timooutInterval.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_TIMEOUT_INTERVAL_HINT)); + timooutInterval.setRequired(false); + timooutInterval.setHint(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_TIMEOUT_INTERVAL_HINT)); + + //Resource + Property resource = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_RESOURCE); + resource.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_RESOURCE)); + resource.setRequired(true); + resource.setHint(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_RESOURCE_HINT)); + + staticPropertyList.add(host); + staticPropertyList.add(port); + staticPropertyList.add(userName); + staticPropertyList.add(password); + staticPropertyList.add(timooutInterval); + staticPropertyList.add(resource); + return staticPropertyList; + } + + @Override + public List getDynamicPropertyList() { + List dynamicPropertyList = new ArrayList(); + // set topic + Property jidProperty = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_JID); + jidProperty.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_JID_HINT)); + jidProperty.setRequired(true); + + Property subjectProperty = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_SUBJECT); + subjectProperty.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_SUBJECT_HINT)); + subjectProperty.setRequired(false); + + Property messageType = new Property(XMPPEventAdapterConstants.ADAPTER_CONF_MESSAGETYPE); + messageType.setDisplayName(resourceBundle.getString(XMPPEventAdapterConstants.ADAPTER_CONF_MESSAGETYPE_HINT)); + messageType.setRequired(true); + messageType.setOptions( + new String[]{XMPPEventAdapterConstants.MessageType.CHAT, XMPPEventAdapterConstants.MessageType.ERROR, + XMPPEventAdapterConstants.MessageType.GROUP_CHAT, XMPPEventAdapterConstants.MessageType.NORMAL, + XMPPEventAdapterConstants.MessageType.HEADLINE}); + messageType.setDefaultValue(XMPPEventAdapterConstants.MessageType.NORMAL); + + dynamicPropertyList.add(jidProperty); + dynamicPropertyList.add(subjectProperty); + dynamicPropertyList.add(messageType); + return dynamicPropertyList; + } + + @Override + public String getUsageTips() { + return null; + } + + @Override + public OutputEventAdapter createEventAdapter(OutputEventAdapterConfiguration eventAdapterConfiguration, + Map globalProperties) { + return new XMPPEventAdapter(eventAdapterConfiguration, globalProperties); + } +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/internal/XMPPEventAdapterServiceComponent.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/internal/XMPPEventAdapterServiceComponent.java new file mode 100644 index 000000000..c8d1806ed --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/internal/XMPPEventAdapterServiceComponent.java @@ -0,0 +1,51 @@ +/* +* 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.device.mgt.iot.output.adapter.xmpp.internal; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.service.component.ComponentContext; +import org.wso2.carbon.device.mgt.iot.output.adapter.xmpp.XMPPEventAdapterFactory; +import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterFactory; + + +/** + * @scr.component component.name="output.XMPP.AdapterService.component" immediate="true" + */ +public class XMPPEventAdapterServiceComponent { + + private static final Log log = LogFactory.getLog(XMPPEventAdapterServiceComponent.class); + + /** + * Deployment of the XMPP event adapter service will be done. + * @param context bundle context where service is registered + */ + protected void activate(ComponentContext context) { + try { + OutputEventAdapterFactory xmppEventAdapterFactory = new XMPPEventAdapterFactory(); + context.getBundleContext().registerService(OutputEventAdapterFactory.class.getName(), + xmppEventAdapterFactory, null); + if (log.isDebugEnabled()) { + log.debug("The XMPP publisher service has been deployed successfully"); + } + } catch (RuntimeException e) { + log.error("Exception occurred when deploying XMPP publisher service", e); + } + } + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/XMPPAdapterPublisher.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/XMPPAdapterPublisher.java new file mode 100644 index 000000000..8ed9fcc3f --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/XMPPAdapterPublisher.java @@ -0,0 +1,106 @@ +/* +* 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.device.mgt.iot.output.adapter.xmpp.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jivesoftware.smack.ConnectionConfiguration; +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.Message; +import org.wso2.carbon.event.output.adapter.core.exception.OutputEventAdapterException; +import org.wso2.carbon.event.output.adapter.core.exception.OutputEventAdapterRuntimeException; + +/** + * XMPP publisher related configuration initialization and publishing capabilties are implemented here. + */ +public class XMPPAdapterPublisher { + + private static final Log log = LogFactory.getLog(XMPPAdapterPublisher.class); + private XMPPServerConnectionConfiguration xmppServerConnectionConfiguration; + XMPPConnection xmppConnection; + + public XMPPAdapterPublisher(XMPPServerConnectionConfiguration xmppServerConnectionConfiguration) { + this.xmppServerConnectionConfiguration = xmppServerConnectionConfiguration; + connect(); + } + + public void connect() { + SmackConfiguration.setPacketReplyTimeout(xmppServerConnectionConfiguration.getTimeoutInterval()); + ConnectionConfiguration config = new ConnectionConfiguration(xmppServerConnectionConfiguration.getHost(), + xmppServerConnectionConfiguration.getPort()); + config.setSASLAuthenticationEnabled(false); + config.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled); + xmppConnection = new XMPPConnection(config); + String resource = xmppServerConnectionConfiguration.getResource(); + String username = xmppServerConnectionConfiguration.getUsername(); + String password = xmppServerConnectionConfiguration.getPassword(); + try { + xmppConnection.connect(); + if (resource == null || resource.trim().isEmpty()) { + xmppConnection.login(username, password); + } else { + xmppConnection.login(username, password, resource); + } + } catch (XMPPException e) { + String errorMsg = "Login attempt to the XMPP Server with username - " + username + " failed."; + log.info(errorMsg); + throw new OutputEventAdapterRuntimeException(errorMsg, e); + } + } + + public boolean isConnected() { + return xmppConnection.isConnected(); + } + + public void publish(String JID, String subject, String message, String messageType) { + Message xmppMessage = new Message(); + xmppMessage.setTo(JID); + xmppMessage.setSubject(subject); + xmppMessage.setBody(message); + if (messageType != null) { + switch (messageType) { + case XMPPEventAdapterConstants.MessageType.CHAT: + xmppMessage.setType(Message.Type.chat); + break; + case XMPPEventAdapterConstants.MessageType.GROUP_CHAT: + xmppMessage.setType(Message.Type.groupchat); + break; + case XMPPEventAdapterConstants.MessageType.ERROR: + xmppMessage.setType(Message.Type.error); + break; + case XMPPEventAdapterConstants.MessageType.HEADLINE: + xmppMessage.setType(Message.Type.headline); + break; + default: + xmppMessage.setType(Message.Type.normal); + } + } else { + xmppMessage.setType(Message.Type.normal); + } + xmppConnection.sendPacket(xmppMessage); + } + + public void close() throws OutputEventAdapterException { + if (xmppConnection != null && isConnected()) { + xmppConnection.disconnect(); + } + } + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/XMPPEventAdapterConstants.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/XMPPEventAdapterConstants.java new file mode 100644 index 000000000..9db36385c --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/XMPPEventAdapterConstants.java @@ -0,0 +1,71 @@ +/* +* 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.device.mgt.iot.output.adapter.xmpp.util; + + +public final class XMPPEventAdapterConstants { + + private XMPPEventAdapterConstants() { + } + + public static final String ADAPTER_TYPE_XMPP = "xmpp"; + + //dynamic properties + public static final String ADAPTER_CONF_JID = "jid"; + public static final String ADAPTER_CONF_JID_HINT = "jid.hint"; + public static final String ADAPTER_CONF_SUBJECT = "subject"; + public static final String ADAPTER_CONF_SUBJECT_HINT = "subject.hint"; + public static final String ADAPTER_CONF_MESSAGETYPE = "messageType"; + public static final String ADAPTER_CONF_MESSAGETYPE_HINT = "messageType.hint"; + + //static properties + public static final String ADAPTER_CONF_HOST = "host"; + public static final String ADAPTER_CONF_HOST_HINT = "host.hint"; + public static final String ADAPTER_CONF_PORT = "port"; + public static final String ADAPTER_CONF_PORT_HINT = "port.hint"; + public static final String ADAPTER_CONF_USERNAME = "username"; + public static final String ADAPTER_CONF_USERNAME_HINT = "username.hint"; + public static final String ADAPTER_CONF_PASSWORD = "password"; + public static final String ADAPTER_CONF_PASSWORD_HINT = "password.hint"; + public static final String ADAPTER_CONF_RESOURCE = "resource"; + public static final String ADAPTER_CONF_RESOURCE_HINT = "resource.hint"; + public static final String ADAPTER_CONF_TIMEOUT_INTERVAL = "timeoutInterval"; + public static final String ADAPTER_CONF_TIMEOUT_INTERVAL_HINT = "timeoutInterval.hint"; + public static final int DEFAULT_XMPP_PORT = 5222; + public static final int DEFAULT_TIMEOUT_INTERVAL = 5000; + + //global properties + public static final int DEFAULT_MIN_THREAD_POOL_SIZE = 8; + public static final int DEFAULT_MAX_THREAD_POOL_SIZE = 100; + public static final int DEFAULT_EXECUTOR_JOB_QUEUE_SIZE = 2000; + public static final long DEFAULT_KEEP_ALIVE_TIME_IN_MILLIS = 20000; + public static final String ADAPTER_MIN_THREAD_POOL_SIZE_NAME = "minThread"; + public static final String ADAPTER_MAX_THREAD_POOL_SIZE_NAME = "maxThread"; + public static final String ADAPTER_KEEP_ALIVE_TIME_NAME = "keepAliveTimeInMillis"; + public static final String ADAPTER_EXECUTOR_JOB_QUEUE_SIZE_NAME = "jobQueueSize"; + + public static final class MessageType { + public static final String NORMAL = "normal"; + public static final String CHAT = "chat"; + public static final String GROUP_CHAT = "groupchat"; + public static final String HEADLINE = "headline"; + public static final String ERROR = "error"; + + } + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/XMPPServerConnectionConfiguration.java b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/XMPPServerConnectionConfiguration.java new file mode 100644 index 000000000..51e696e20 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/java/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/util/XMPPServerConnectionConfiguration.java @@ -0,0 +1,63 @@ +/* +* 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.device.mgt.iot.output.adapter.xmpp.util; + +public class XMPPServerConnectionConfiguration { + + private String host; + private int port; + private String username; + private String password; + private int timeoutInterval; + private String resource; + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public int getTimeoutInterval() { + return timeoutInterval; + } + + public String getResource() { + return resource; + } + + public XMPPServerConnectionConfiguration(String host, int port, String username, String password, + int timeoutInterval, String resource) { + this.host = host; + this.port = port; + this.username = username; + this.password = password; + this.timeoutInterval = timeoutInterval; + this.resource = resource; + } + +} diff --git a/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/resources/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/i18n/Resources.properties b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/resources/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/i18n/Resources.properties new file mode 100644 index 000000000..9b4b33d90 --- /dev/null +++ b/components/iot-plugins/iot-base-plugin/org.wso2.carbon.device.mgt.iot.output.adapter.xmpp/src/main/resources/org/wso2/carbon/device/mgt/iot/output/adapter/xmpp/i18n/Resources.properties @@ -0,0 +1,36 @@ +# +# 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. +# + +host=Server Url +host.hint=XMPP Server url +port=Server Port +port.hint=Server Port Number +username=Username +username.hint=Username of the broker +password=Password +password.hint=Password to connect to the server. +timeoutInterval=Time out Interval +timeoutInterval.hint=used by listeners to the server and for reconnection schedules. +resource=Resource +resource.hint=specific to the XMPP-Account to which the login is made to. +jid=JID +jid.hint=the JID (XMPP Account ID) to which the message is to be sent to. +subject=Subject +subject.hint=Subject of the message. +messageType=Message Type +messageType.hint=Type of the XMPP Message (chat, groupchat, error, headline, normal) diff --git a/components/iot-plugins/iot-base-plugin/pom.xml b/components/iot-plugins/iot-base-plugin/pom.xml index 6c491e480..662ee621f 100644 --- a/components/iot-plugins/iot-base-plugin/pom.xml +++ b/components/iot-plugins/iot-base-plugin/pom.xml @@ -35,6 +35,9 @@ org.wso2.carbon.device.mgt.iot org.wso2.carbon.device.mgt.iot.ui + org.wso2.carbon.device.mgt.iot.output.adapter.mqtt + org.wso2.carbon.device.mgt.iot.output.adapter.xmpp + org.wso2.carbon.device.mgt.iot.input.adapter diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmControllerService.java b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmControllerService.java index 63b193889..f8f710046 100644 --- a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmControllerService.java +++ b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmControllerService.java @@ -41,27 +41,6 @@ import javax.ws.rs.core.Response; @DeviceType(value = "virtual_firealarm") public interface VirtualFireAlarmControllerService { - /** - * This is an API used/called by the device. It registers the IP of a VirtualFirealarm device against its DeviceID - * when the device connects with the server for the first time. This DeviceID to IP mapping is necessary only for - * cases where HTTP communication is to be used. At such instances, this mapping is used by the server to - * identify the IP of the device to which it has some message to be sent. This method becomes useful only in - * scenarios where HTTP communication is used in a setup where the IoT-Server and the devices communicating with it - * are in the same IP network. - * - * @param deviceId the ID of the VirtualFirealarm device from which this register-IP call was initiated. - * @param deviceIP the IP of the VirtualFirealarm device which has sent this register-IP request. - * @param devicePort the PORT on the VirtualFirealarm device (on this IP) that's open for HTTP communication. - * @param request the HTTP servlet request object received by default as part of the HTTP call to this API. - * @return a custom message indicating whether the DeviceID to IP mapping was successful. - */ - @POST - @Path("device/register/{deviceId}/{ip}/{port}") - @Permission(scope = "virtual_firealarm_admin", permissions = {"device-mgt/virtual_firealarm/admin"}) - Response registerDeviceIP(@PathParam("deviceId") String deviceId, @PathParam("ip") String deviceIP, - @PathParam("port") String devicePort, @Context HttpServletRequest request); - - /** * This is an API called/used from within the Server(Front-End) or by a device Owner. It sends a control command to * the VirtualFirealarm device to switch `ON` or `OFF` its buzzer. The method also takes in the protocol to be used @@ -79,19 +58,6 @@ public interface VirtualFireAlarmControllerService { description = "Switch on/off Virtual Fire Alarm Buzzer. (On / Off)") Response switchBuzzer(@PathParam("deviceId") String deviceId, @QueryParam("protocol") String protocol, @FormParam("state") String state); - - /** - * This is an API called/used by the VirtualFirealarm device to publish its temperature to the IoT-Server. The - * received data from the device is stored in a 'DeviceRecord' under the device's ID in the 'SensorDataManager' - * of the Server. - * - * @param dataMsg the temperature data received from the device in JSON format complying to type 'DeviceData'. - */ - @POST - @Path("device/temperature") - @Permission(scope = "virtual_firealarm_admin", permissions = {"device-mgt/virtual_firealarm/admin"}) - @Consumes(MediaType.APPLICATION_JSON) - Response pushTemperatureData(final DeviceData dataMsg); /** diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmControllerServiceImpl.java b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmControllerServiceImpl.java index e7c6a93dd..bd9bd2d10 100644 --- a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmControllerServiceImpl.java +++ b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmControllerServiceImpl.java @@ -1,20 +1,20 @@ /* - * Copyright (c) 2016, 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. - */ +* Copyright (c) 2016, 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.device.mgt.iot.virtualfirealarm.service.impl; @@ -35,6 +35,7 @@ import org.wso2.carbon.device.mgt.iot.transport.TransportHandlerException; import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.constants.VirtualFireAlarmConstants; import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.dto.DeviceData; import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.dto.SensorRecord; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.exception.VirtualFireAlarmException; import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.transport.VirtualFireAlarmMQTTConnector; import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.transport.VirtualFireAlarmXMPPConnector; import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.util.APIUtil; @@ -54,8 +55,12 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @SuppressWarnings("Non-Annoted WebService") @@ -71,26 +76,7 @@ public class VirtualFireAlarmControllerServiceImpl implements VirtualFireAlarmCo private VirtualFireAlarmMQTTConnector virtualFireAlarmMQTTConnector; // connects to the given XMPP server and handles XMPP communication private VirtualFireAlarmXMPPConnector virtualFireAlarmXMPPConnector; - // holds a mapping of the IP addresses to Device-IDs for HTTP communication - private ConcurrentHashMap deviceToIpMap = new ConcurrentHashMap<>(); - @Permission(scope = "virtual_firealarm_user", permissions = { "device-mgt/virtual_firealarm/user" }) - @POST - @Path("device/register/{deviceId}/{ip}/{port}") - public Response registerDeviceIP(@PathParam("deviceId") String deviceId, @PathParam("ip") String deviceIP, - @PathParam("port") String devicePort, @Context HttpServletRequest request) { - - if (log.isDebugEnabled()) { - log.debug("Got register call from IP: " + deviceIP + " for Device ID: " + deviceId); - } - String deviceHttpEndpoint = deviceIP + ":" + devicePort; - deviceToIpMap.put(deviceId, deviceHttpEndpoint); - String result = "Device-IP Registered"; - if (log.isDebugEnabled()) { - log.debug(result); - } - return Response.ok().entity(result).build(); - } @POST @Path("device/{deviceId}/buzz") @@ -106,7 +92,7 @@ public class VirtualFireAlarmControllerServiceImpl implements VirtualFireAlarmCo String callUrlPattern = VirtualFireAlarmConstants.BULB_CONTEXT + switchToState; if (log.isDebugEnabled()) { log.debug("Sending request to switch-bulb of device [" + deviceId + "] via " + - protocolString); + protocolString); } try { if (!APIUtil.getDeviceAccessAuthorizationService().isUserAuthorized( @@ -115,29 +101,35 @@ public class VirtualFireAlarmControllerServiceImpl implements VirtualFireAlarmCo return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build(); } switch (protocolString) { - case HTTP_PROTOCOL: - String deviceHTTPEndpoint = deviceToIpMap.get(deviceId); - if (deviceHTTPEndpoint == null) { - return Response.status(Response.Status.PRECONDITION_FAILED).build(); - } - VirtualFireAlarmServiceUtils.sendCommandViaHTTP(deviceHTTPEndpoint, callUrlPattern, true); - break; case XMPP_PROTOCOL: String xmppResource = VirtualFireAlarmConstants.BULB_CONTEXT.replace("/", ""); virtualFireAlarmXMPPConnector.publishDeviceData(deviceId, xmppResource, switchToState); break; default: String mqttResource = VirtualFireAlarmConstants.BULB_CONTEXT.replace("/", ""); - virtualFireAlarmMQTTConnector.publishDeviceData(deviceId, mqttResource, switchToState); + String publishTopic = "wso2/" + APIUtil.getTenantDomainOftheUser() + "/" + + VirtualFireAlarmConstants.DEVICE_TYPE + "/" + deviceId; + PrivateKey serverPrivateKey = SecurityManager.getServerPrivateKey(); + String actualMessage = mqttResource + ":" + state.toUpperCase(); + String encryptedMsg = VirtualFireAlarmServiceUtils.prepareSecurePayLoad(actualMessage, + serverPrivateKey); + Map dynamicProperties = new HashMap<>(); + dynamicProperties.put(VirtualFireAlarmConstants.ADAPTER_TOPIC_PROPERTY, publishTopic); + APIUtil.getOutputEventAdapterService().publish(VirtualFireAlarmConstants.ADAPTER_NAME, + dynamicProperties, encryptedMsg); break; } return Response.ok().build(); - } catch (DeviceManagementException | TransportHandlerException e) { + } catch (TransportHandlerException e) { log.error("Failed to send switch-bulb request to device [" + deviceId + "] via " + protocolString); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } catch (DeviceAccessAuthorizationException e) { log.error(e.getErrorMessage(), e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } catch (VirtualFireAlarmException e) { + String errorMsg = "Preparing Secure payload failed for device - [" + deviceId + "]"; + log.error(errorMsg); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } } @@ -157,17 +149,14 @@ public class VirtualFireAlarmControllerServiceImpl implements VirtualFireAlarmCo return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build(); } switch (protocolString) { - case HTTP_PROTOCOL: - throw new UnsupportedOperationException( - "Sending request to update-policy via HTTP protocol not supported."); - case XMPP_PROTOCOL: - String xmppResource = VirtualFireAlarmConstants.POLICY_CONTEXT.replace("/", ""); - virtualFireAlarmXMPPConnector.publishDeviceData(deviceId, xmppResource, policy); - break; - default: - String mqttResource = VirtualFireAlarmConstants.POLICY_CONTEXT.replace("/", ""); - virtualFireAlarmMQTTConnector.publishDeviceData(deviceId, mqttResource, policy); - break; + case XMPP_PROTOCOL: + String xmppResource = VirtualFireAlarmConstants.POLICY_CONTEXT.replace("/", ""); + virtualFireAlarmXMPPConnector.publishDeviceData(deviceId, xmppResource, policy); + break; + default: + String mqttResource = VirtualFireAlarmConstants.POLICY_CONTEXT.replace("/", ""); + virtualFireAlarmMQTTConnector.publishDeviceData(deviceId, mqttResource, policy); + break; } return Response.ok().build(); } catch (TransportHandlerException e) { @@ -179,36 +168,6 @@ public class VirtualFireAlarmControllerServiceImpl implements VirtualFireAlarmCo } } - @POST - @Path("device/temperature") - @Consumes(MediaType.APPLICATION_JSON) - public Response pushTemperatureData(final DeviceData dataMsg) { - String deviceId = dataMsg.deviceId; - String deviceIp = dataMsg.reply; - String registeredIp = deviceToIpMap.get(deviceId); - if (registeredIp == null) { - log.warn("Unregistered IP: Temperature Data Received from an un-registered IP " + - deviceIp + " for device ID - " + deviceId); - return Response.status(Response.Status.PRECONDITION_FAILED).build(); - } else if (!registeredIp.equals(deviceIp)) { - log.warn("Conflicting IP: Received IP is " + deviceIp + ". Device with ID " + deviceId + - " is already registered under some other IP. Re-registration required"); - return Response.status(Response.Status.CONFLICT).build(); - } - try { - if (!APIUtil.getDeviceAccessAuthorizationService().isUserAuthorized( - new DeviceIdentifier(deviceId, VirtualFireAlarmConstants.DEVICE_TYPE))) { - return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build(); - } - if (!VirtualFireAlarmServiceUtils.publishToDAS(dataMsg.deviceId, dataMsg.value)) { - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); - } - return Response.ok().build(); - } catch (DeviceAccessAuthorizationException e) { - log.error(e.getErrorMessage(), e); - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); - } - } @Path("device/stats/{deviceId}") @GET @@ -303,7 +262,8 @@ public class VirtualFireAlarmControllerServiceImpl implements VirtualFireAlarmCo if (waitForServerStartup()) { return; } - VirtualFireAlarmControllerServiceImpl.this.virtualFireAlarmXMPPConnector = virtualFireAlarmXMPPConnector; + VirtualFireAlarmControllerServiceImpl.this.virtualFireAlarmXMPPConnector = + virtualFireAlarmXMPPConnector; if (XmppConfig.getInstance().isEnabled()) { Runnable xmppStarter = new Runnable() { @@ -318,7 +278,9 @@ public class VirtualFireAlarmControllerServiceImpl implements VirtualFireAlarmCo xmppStarterThread.setDaemon(true); xmppStarterThread.start(); } else { - log.warn("XMPP disabled in 'devicemgt-config.xml'. Hence, VirtualFireAlarmXMPPConnector not started."); + log.warn( + "XMPP disabled in 'devicemgt-config.xml'. Hence, VirtualFireAlarmXMPPConnector not " + + "started."); } } }; @@ -357,11 +319,13 @@ public class VirtualFireAlarmControllerServiceImpl implements VirtualFireAlarmCo } catch (InterruptedException e) { Thread.currentThread().interrupt(); } - VirtualFireAlarmControllerServiceImpl.this.virtualFireAlarmMQTTConnector = virtualFireAlarmMQTTConnector; + VirtualFireAlarmControllerServiceImpl.this.virtualFireAlarmMQTTConnector = + virtualFireAlarmMQTTConnector; if (MqttConfig.getInstance().isEnabled()) { virtualFireAlarmMQTTConnector.connect(); } else { - log.warn("MQTT disabled in 'devicemgt-config.xml'. Hence, VirtualFireAlarmMQTTConnector not started."); + log.warn( + "MQTT disabled in 'devicemgt-config.xml'. Hence, VirtualFireAlarmMQTTConnector not started."); } } }; diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmManagerServiceImpl.java b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmManagerServiceImpl.java index d5af4821b..71e5f9667 100644 --- a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmManagerServiceImpl.java +++ b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmManagerServiceImpl.java @@ -269,8 +269,7 @@ public class VirtualFireAlarmManagerServiceImpl implements VirtualFireAlarmManag if (XmppConfig.getInstance().isEnabled()) { status = xmppServerClient.createXMPPAccount(newXmppAccount); if (!status) { - String msg = - "XMPP Account was not created for device - " + deviceId + " of owner - " + owner + + String msg = "XMPP Account was not created for device - " + deviceId + " of owner - " + owner + ".XMPP might have been disabled in org.wso2.carbon.device.mgt.iot" + ".common.config.server.configs"; throw new DeviceManagementException(msg); diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/transport/VirtualFireAlarmXMPPConnector.java b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/transport/VirtualFireAlarmXMPPConnector.java index 420567952..c39e95b69 100644 --- a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/transport/VirtualFireAlarmXMPPConnector.java +++ b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/transport/VirtualFireAlarmXMPPConnector.java @@ -257,7 +257,6 @@ public class VirtualFireAlarmXMPPConnector extends XMPPTransportHandler { } } }; - Thread terminatorThread = new Thread(stopConnection); terminatorThread.start(); } diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/APIUtil.java b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/APIUtil.java index e7b182ef4..c26586f91 100644 --- a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/APIUtil.java +++ b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/APIUtil.java @@ -16,6 +16,7 @@ import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationService; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.dto.SensorRecord; +import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterService; import org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService; import java.util.ArrayList; @@ -168,4 +169,16 @@ public class APIUtil { } return deviceAccessAuthorizationService; } + + public static OutputEventAdapterService getOutputEventAdapterService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + OutputEventAdapterService outputEventAdapterService = + (OutputEventAdapterService) ctx.getOSGiService(OutputEventAdapterService.class, null); + if (outputEventAdapterService == null) { + String msg = "Device Authorization service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return outputEventAdapterService; + } } diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/scep/ContentType.java b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/scep/ContentType.java deleted file mode 100644 index 672f773bc..000000000 --- a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/scep/ContentType.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.device.mgt.iot.virtualfirealarm.service.impl.util.scep; - -public class ContentType { - public static final String X_PKI_MESSAGE = "application/x-pki-message"; - public static final String X_X509_CA_CERT = "application/x-x509-ca-cert"; - public static final String X_X509_CA_RA_CERT = "application/x-x509-ca-ra-cert"; -} - diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/scep/SCEPOperation.java b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/scep/SCEPOperation.java deleted file mode 100644 index 563261dd4..000000000 --- a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.api/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/scep/SCEPOperation.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2016, 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.device.mgt.iot.virtualfirealarm.service.impl.util.scep; - -public enum SCEPOperation { - GET_CA_CERT("GetCACert"), - GET_CA_CAPS("GetCACaps"), - PKI_OPERATION("PKIOperation"); - - private String value; - - private SCEPOperation(String value) { - this.setValue(value); - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } -} diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/pom.xml b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/pom.xml index 29b7704e1..c7e044c13 100644 --- a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/pom.xml +++ b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/pom.xml @@ -71,11 +71,17 @@ org.wso2.carbon.device.mgt.common, org.wso2.carbon.device.mgt.iot.*, org.wso2.carbon.device.mgt.extensions.feature.mgt.*, - org.wso2.carbon.utils.* + org.wso2.carbon.utils.*, + org.wso2.carbon.event.output.adapter.core, + org.wso2.carbon.event.output.adapter.core.exception, + org.wso2.carbon.base, + org.wso2.carbon.core.util, + org.wso2.carbon.context, + org.wso2.carbon.core !org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.internal, - org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.* + org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.*, @@ -112,5 +118,10 @@ org.wso2.carbon org.wso2.carbon.utils + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.output.adapter.core + + \ No newline at end of file diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/constants/VirtualFireAlarmConstants.java b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/constants/VirtualFireAlarmConstants.java index 3c8ef2de5..7f69e90de 100644 --- a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/constants/VirtualFireAlarmConstants.java +++ b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/constants/VirtualFireAlarmConstants.java @@ -28,15 +28,28 @@ public class VirtualFireAlarmConstants { public static final String URL_PREFIX = "http://"; public static final String BULB_CONTEXT = "/BULB/"; public static final String POLICY_CONTEXT = "/POLICY/"; - public static final String HUMIDITY_CONTEXT = "/HUMIDITY/"; - public static final String TEMPERATURE_CONTEXT = "/TEMPERATURE/"; - public static final String SENSOR_TEMP = "temperature"; //sensor events sumerized table name for temperature public static final String TEMPERATURE_EVENT_TABLE = "DEVICE_TEMPERATURE_SUMMARY"; - public static final String SENSOR_HUMIDITY = "humidity"; - //sensor events summerized table name for humidity - public static final String HUMIDITY_EVENT_TABLE = "DEVICE_HUMIDITY_SUMMARY"; public static final String DATA_SOURCE_NAME = "jdbc/VirtualFireAlarmDM_DB"; public final static String DEVICE_TYPE_PROVIDER_DOMAIN = "carbon.super"; + + //tranport related constants + public static final String ADAPTER_NAME = "virtual_firealarm_mqtt"; + public static final String ADAPTER_TYPE = "oauth-mqtt"; + public static final String ADAPTER_TOPIC_PROPERTY = "topic"; + public static final String MQTT_PORT = "\\$\\{mqtt.broker.port\\}"; + public static final String MQTT_BROKER_HOST = "\\$\\{mqtt.broker.host\\}"; + public static final String CARBON_CONFIG_PORT_OFFSET = "Ports.Offset"; + public static final String DEFAULT_CARBON_SERVER_HOST_PROPERTY = "server.host"; + public static final int CARBON_DEFAULT_PORT_OFFSET = 0; + public static final int DEFAULT_MQTT_PORT = 1883; + + public static final String USERNAME_PROPERTY_KEY = "username"; + public static final String DCR_PROPERTY_KEY = "dcrUrl"; + public static final String BROKER_URL_PROPERTY_KEY = "url"; + public static final String SCOPES_PROPERTY_KEY = "scopes"; + public static final String QOS_PROPERTY_KEY = "qos"; + public static final String CLIENT_ID_PROPERTY_KEY = "qos"; + public static final String CLEAR_SESSION_PROPERTY_KEY = "clearSession"; } diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/impl/util/VirtualFireAlarmUtils.java b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/impl/util/VirtualFireAlarmUtils.java index 216b41f24..bf4bc3344 100644 --- a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/impl/util/VirtualFireAlarmUtils.java +++ b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/impl/util/VirtualFireAlarmUtils.java @@ -20,20 +20,31 @@ package org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.impl.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.wso2.carbon.device.mgt.common.Device; +import org.wso2.carbon.base.ServerConfiguration; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.core.util.Utils; import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.constants.VirtualFireAlarmConstants; import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.exception.VirtualFirealarmDeviceMgtPluginException; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.internal.VirtualFirealarmManagementDataHolder; +import org.wso2.carbon.event.output.adapter.core.MessageType; +import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterConfiguration; +import org.wso2.carbon.event.output.adapter.core.exception.OutputEventAdapterException; +import org.wso2.carbon.utils.CarbonUtils; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.List; +import java.util.HashMap; import java.util.Map; +import java.util.Properties; /** * Contains utility methods used by FireAlarm plugin. @@ -41,26 +52,9 @@ import java.util.Map; public class VirtualFireAlarmUtils { private static Log log = LogFactory.getLog(VirtualFireAlarmUtils.class); - - public static String getDeviceProperty(List deviceProperties, String propertyKey) { - String deviceProperty = ""; - for(Device.Property property :deviceProperties){ - if(propertyKey.equals(property.getName())){ - deviceProperty = property.getValue(); - } - } - return deviceProperty; - } - - public static Device.Property getProperty(String property, String value) { - if (property != null) { - Device.Property prop = new Device.Property(); - prop.setName(property); - prop.setValue(value); - return prop; - } - return null; - } + private static final String VIRTUAL_FIREALARM_CONFIG_LOCATION = + CarbonUtils.getCarbonHome() + File.separator + "repository" + File.separator + "resources" + + File.separator + "device-types" + File.separator + "virtual-firealarm.properties"; public static void cleanupResources(Connection conn, PreparedStatement stmt, ResultSet rs) { if (rs != null) { @@ -105,7 +99,89 @@ public class VirtualFireAlarmUtils { log.error("Error while looking up the data source: " + VirtualFireAlarmConstants.DATA_SOURCE_NAME); } catch (Exception e) { throw new VirtualFirealarmDeviceMgtPluginException("Error occurred while initializing Iot Device " + - "Management database schema", e); + "Management database schema", e); + } + } + + public static void setupOutputAdapter() throws IOException { + OutputEventAdapterConfiguration outputEventAdapterConfiguration = + createOutputEventAdapterConfiguration(VirtualFireAlarmConstants.ADAPTER_NAME, + VirtualFireAlarmConstants.ADAPTER_TYPE, MessageType.TEXT); + try { + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain( + VirtualFireAlarmConstants.DEVICE_TYPE_PROVIDER_DOMAIN, true); + VirtualFirealarmManagementDataHolder.getInstance().getOutputEventAdapterService() + .create(outputEventAdapterConfiguration); + } catch (OutputEventAdapterException e) { + log.error("Unable to create Output Event Adapter : " + VirtualFireAlarmConstants.ADAPTER_NAME, e); + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + } + + /** + * Create Output Event Adapter Configuration for given configuration. + * + * @param name Output Event Adapter name + * @param type Output Event Adapter type + * @param msgFormat Output Event Adapter message format + * @return OutputEventAdapterConfiguration instance for given configuration + */ + private static OutputEventAdapterConfiguration createOutputEventAdapterConfiguration(String name, String type, + String msgFormat) + throws IOException { + OutputEventAdapterConfiguration outputEventAdapterConfiguration = new OutputEventAdapterConfiguration(); + outputEventAdapterConfiguration.setName(name); + outputEventAdapterConfiguration.setType(type); + outputEventAdapterConfiguration.setMessageFormat(msgFormat); + File configFile = new File(VIRTUAL_FIREALARM_CONFIG_LOCATION); + if (configFile.exists()) { + Map mqttAdapterProperties = new HashMap<>(); + InputStream propertyStream = configFile.toURI().toURL().openStream(); + Properties properties = new Properties(); + properties.load(propertyStream); + mqttAdapterProperties.put(VirtualFireAlarmConstants.USERNAME_PROPERTY_KEY, properties.getProperty( + VirtualFireAlarmConstants.USERNAME_PROPERTY_KEY)); + mqttAdapterProperties.put(VirtualFireAlarmConstants.DCR_PROPERTY_KEY, Utils.replaceSystemProperty( + properties.getProperty(VirtualFireAlarmConstants.DCR_PROPERTY_KEY))); + mqttAdapterProperties.put(VirtualFireAlarmConstants.BROKER_URL_PROPERTY_KEY, replaceMqttProperty( + properties.getProperty(VirtualFireAlarmConstants.BROKER_URL_PROPERTY_KEY))); + mqttAdapterProperties.put(VirtualFireAlarmConstants.SCOPES_PROPERTY_KEY, properties.getProperty( + VirtualFireAlarmConstants.SCOPES_PROPERTY_KEY)); + mqttAdapterProperties.put(VirtualFireAlarmConstants.CLEAR_SESSION_PROPERTY_KEY, properties.getProperty( + VirtualFireAlarmConstants.CLEAR_SESSION_PROPERTY_KEY)); + mqttAdapterProperties.put(VirtualFireAlarmConstants.QOS_PROPERTY_KEY, properties.getProperty( + VirtualFireAlarmConstants.QOS_PROPERTY_KEY)); + mqttAdapterProperties.put(VirtualFireAlarmConstants.CLIENT_ID_PROPERTY_KEY, properties.getProperty( + VirtualFireAlarmConstants.CLIENT_ID_PROPERTY_KEY)); + outputEventAdapterConfiguration.setStaticProperties(mqttAdapterProperties); + } + return outputEventAdapterConfiguration; + } + + public static String replaceMqttProperty(String urlWithPlaceholders) { + urlWithPlaceholders = Utils.replaceSystemProperty(urlWithPlaceholders); + urlWithPlaceholders = urlWithPlaceholders.replaceAll(VirtualFireAlarmConstants.MQTT_PORT, "" + + (VirtualFireAlarmConstants.DEFAULT_MQTT_PORT + getPortOffset())); + urlWithPlaceholders = urlWithPlaceholders.replaceAll(VirtualFireAlarmConstants.MQTT_BROKER_HOST, + System.getProperty(VirtualFireAlarmConstants.DEFAULT_CARBON_SERVER_HOST_PROPERTY, "localhost")); + return urlWithPlaceholders; + } + + private static int getPortOffset() { + ServerConfiguration carbonConfig = ServerConfiguration.getInstance(); + String portOffset = System.getProperty("portOffset", carbonConfig.getFirstProperty( + VirtualFireAlarmConstants.CARBON_CONFIG_PORT_OFFSET)); + try { + if ((portOffset != null)) { + return Integer.parseInt(portOffset.trim()); + } else { + return VirtualFireAlarmConstants.CARBON_DEFAULT_PORT_OFFSET; + } + } catch (NumberFormatException e) { + return VirtualFireAlarmConstants.CARBON_DEFAULT_PORT_OFFSET; } } + } diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/impl/util/VirtualFirealarmStartupListener.java b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/impl/util/VirtualFirealarmStartupListener.java new file mode 100644 index 000000000..a88200fde --- /dev/null +++ b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/impl/util/VirtualFirealarmStartupListener.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016, 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.device.mgt.iot.virtualfirealarm.plugin.impl.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.core.ServerStartupObserver; + +import java.io.IOException; + +public class VirtualFirealarmStartupListener implements ServerStartupObserver { + private static final Log log = LogFactory.getLog(VirtualFirealarmStartupListener.class); + + @Override + public void completingServerStartup() { + } + + @Override + public void completedServerStartup() { + try { + VirtualFireAlarmUtils.setupOutputAdapter(); + } catch (IOException e) { + log.error("Failed to intilaize the virtual firealarm output adapter", e); + } + } + +} diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/internal/VirtualFirealarmManagementDataHolder.java b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/internal/VirtualFirealarmManagementDataHolder.java new file mode 100644 index 000000000..1c3759ccb --- /dev/null +++ b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/internal/VirtualFirealarmManagementDataHolder.java @@ -0,0 +1,47 @@ +/* + * 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.device.mgt.iot.virtualfirealarm.plugin.internal; + +import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterService; + +/** + * DataHolder class of virtual firealarm plugins component. + */ +public class VirtualFirealarmManagementDataHolder { + + private OutputEventAdapterService outputEventAdapterService; + + private static VirtualFirealarmManagementDataHolder thisInstance = new VirtualFirealarmManagementDataHolder(); + + private VirtualFirealarmManagementDataHolder() { + } + + public static VirtualFirealarmManagementDataHolder getInstance() { + return thisInstance; + } + + public OutputEventAdapterService getOutputEventAdapterService() { + return outputEventAdapterService; + } + + public void setOutputEventAdapterService( + OutputEventAdapterService outputEventAdapterService) { + this.outputEventAdapterService = outputEventAdapterService; + } +} diff --git a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/internal/VirtualFirealarmManagementServiceComponent.java b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/internal/VirtualFirealarmManagementServiceComponent.java index bdc3d3a75..6395656a3 100644 --- a/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/internal/VirtualFirealarmManagementServiceComponent.java +++ b/components/iot-plugins/virtual-fire-alarm-plugin/org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/plugin/internal/VirtualFirealarmManagementServiceComponent.java @@ -1,20 +1,20 @@ /* - * 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. - */ +* 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.device.mgt.iot.virtualfirealarm.plugin.internal; @@ -23,15 +23,24 @@ import org.apache.commons.logging.LogFactory; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.osgi.service.component.ComponentContext; +import org.wso2.carbon.core.ServerStartupObserver; import org.wso2.carbon.device.mgt.common.spi.DeviceManagementService; import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.exception.VirtualFirealarmDeviceMgtPluginException; import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.impl.VirtualFireAlarmManagerService; import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.impl.util.VirtualFireAlarmUtils; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.impl.util.VirtualFirealarmStartupListener; +import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterService; /** * @scr.component name="org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.internal * .VirtualFirealarmManagementServiceComponent" * immediate="true" + * @scr.reference name="event.output.adapter.service" + * interface="org.wso2.carbon.event.output.adapter.core.OutputEventAdapterService" + * cardinality="1..1" + * policy="dynamic" + * bind="setOutputEventAdapterService" + * unbind="unsetOutputEventAdapterService" */ public class VirtualFirealarmManagementServiceComponent { @@ -45,7 +54,9 @@ public class VirtualFirealarmManagementServiceComponent { try { BundleContext bundleContext = ctx.getBundleContext(); firealarmServiceRegRef = bundleContext.registerService(DeviceManagementService.class.getName(), - new VirtualFireAlarmManagerService(), null); + new VirtualFireAlarmManagerService(), null); + bundleContext.registerService(ServerStartupObserver.class.getName(), new VirtualFirealarmStartupListener(), + null); String setupOption = System.getProperty("setup"); if (setupOption != null) { if (log.isDebugEnabled()) { @@ -81,4 +92,22 @@ public class VirtualFirealarmManagementServiceComponent { log.error("Error occurred while de-activating Virtual Firealarm Device Management bundle", e); } } + + /** + * Initialize the Output EventAdapter Service dependency + * + * @param outputEventAdapterService Output EventAdapter Service reference + */ + protected void setOutputEventAdapterService(OutputEventAdapterService outputEventAdapterService) { + VirtualFirealarmManagementDataHolder.getInstance().setOutputEventAdapterService(outputEventAdapterService); + } + + /** + * De-reference the Output EventAdapter Service dependency. + * + * @param outputEventAdapterService + */ + protected void unsetOutputEventAdapterService(OutputEventAdapterService outputEventAdapterService) { + VirtualFirealarmManagementDataHolder.getInstance().setOutputEventAdapterService(null); + } } diff --git a/features/iot-plugins-feature/iot-base-plugin-feature/org.wso2.carbon.device.mgt.iot.feature/pom.xml b/features/iot-plugins-feature/iot-base-plugin-feature/org.wso2.carbon.device.mgt.iot.feature/pom.xml index de37121f2..dd33e8dd2 100644 --- a/features/iot-plugins-feature/iot-base-plugin-feature/org.wso2.carbon.device.mgt.iot.feature/pom.xml +++ b/features/iot-plugins-feature/iot-base-plugin-feature/org.wso2.carbon.device.mgt.iot.feature/pom.xml @@ -41,6 +41,10 @@ org.wso2.carbon.devicemgt-plugins org.wso2.carbon.device.mgt.iot + + org.wso2.carbon.devicemgt-plugins + org.wso2.carbon.device.mgt.iot.output.adapter.mqtt + org.json.wso2 json @@ -140,6 +144,9 @@ org.wso2.carbon.devicemgt-plugins:org.wso2.carbon.device.mgt.iot:${carbon.devicemgt.plugins.version} + + org.wso2.carbon.devicemgt-plugins:org.wso2.carbon.device.mgt.iot.output.adapter.mqtt:${carbon.devicemgt.plugins.version} + org.json.wso2:json:${commons-json.version} @@ -158,6 +165,9 @@ org.wso2.carbon.device.mgt.server:${carbon.devicemgt.version} + + org.wso2.carbon.event.output.adapter.server:${carbon.analytics.common.version} + diff --git a/features/iot-plugins-feature/iot-base-plugin-feature/org.wso2.carbon.device.mgt.iot.feature/src/main/resources/p2.inf b/features/iot-plugins-feature/iot-base-plugin-feature/org.wso2.carbon.device.mgt.iot.feature/src/main/resources/p2.inf index b493aa4af..0b3a15f07 100644 --- a/features/iot-plugins-feature/iot-base-plugin-feature/org.wso2.carbon.device.mgt.iot.feature/src/main/resources/p2.inf +++ b/features/iot-plugins-feature/iot-base-plugin-feature/org.wso2.carbon.device.mgt.iot.feature/src/main/resources/p2.inf @@ -3,4 +3,4 @@ org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../depl org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.iot_${feature.version}/jaggeryapps/,target:${installFolder}/../../deployment/server/jaggeryapps/,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../conf/iot/);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.iot_${feature.version}/conf/devicemgt-config.xml,target:${installFolder}/../../conf/iot/devicemgt-config.xml,overwrite:true);\ -org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.iot_${feature.version}/conf/devicemgt-config.xsd,target:${installFolder}/../../conf/iot/devicemgt-config.xsd,overwrite:true);\ \ No newline at end of file +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.iot_${feature.version}/conf/devicemgt-config.xsd,target:${installFolder}/../../conf/iot/devicemgt-config.xsd,overwrite:true);\ diff --git a/features/iot-plugins-feature/virtual-fire-alarm-plugin-feature/org.wso2.carbon.device.mgt.iot.virtualfirealarm.feature/src/main/resources/p2.inf b/features/iot-plugins-feature/virtual-fire-alarm-plugin-feature/org.wso2.carbon.device.mgt.iot.virtualfirealarm.feature/src/main/resources/p2.inf index 1cf4d078d..b07adfada 100644 --- a/features/iot-plugins-feature/virtual-fire-alarm-plugin-feature/org.wso2.carbon.device.mgt.iot.virtualfirealarm.feature/src/main/resources/p2.inf +++ b/features/iot-plugins-feature/virtual-fire-alarm-plugin-feature/org.wso2.carbon.device.mgt.iot.virtualfirealarm.feature/src/main/resources/p2.inf @@ -16,9 +16,12 @@ org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../data org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.iot.virtualfirealarm_${feature.version}/database/,target:${installFolder}/../../database/,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../resources/security/);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.iot.virtualfirealarm_${feature.version}/certs/,target:${installFolder}/../../resources/security/,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../resources/device-types/);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.iot.virtualfirealarm_${feature.version}/virtual-firealarm.properties,target:${installFolder}/../../resources/device-types/virtual-firealarm.properties,overwrite:true);\ instructions.unconfigure = \ org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../conf/device-types/virtual_firealarm.json);\ +org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../resources/device-types/virtual_firealarm.properties);\ org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/webapps/virtual_firealarm.war);\ org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/webapps/virtual_firealarm);\ org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/webapps/virtual_firealarm_scep.war);\ diff --git a/features/iot-plugins-feature/virtual-fire-alarm-plugin-feature/org.wso2.carbon.device.mgt.iot.virtualfirealarm.feature/src/main/resources/virtual-firealarm.properties b/features/iot-plugins-feature/virtual-fire-alarm-plugin-feature/org.wso2.carbon.device.mgt.iot.virtualfirealarm.feature/src/main/resources/virtual-firealarm.properties new file mode 100644 index 000000000..f827a0881 --- /dev/null +++ b/features/iot-plugins-feature/virtual-fire-alarm-plugin-feature/org.wso2.carbon.device.mgt.iot.virtualfirealarm.feature/src/main/resources/virtual-firealarm.properties @@ -0,0 +1,7 @@ +url=tcp://${mqtt.broker.host}:${mqtt.broker.port} +username=admin +dcrUrl=https://${server.host}:${mgt.transport.https.port}/dynamic-client-web/register +scopes= +clearSession=true +qos=0 +clientId= \ No newline at end of file diff --git a/pom.xml b/pom.xml index 646be5d21..46e17dd0d 100644 --- a/pom.xml +++ b/pom.xml @@ -304,6 +304,16 @@ + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.output.adapter.core + ${carbon.analytics.common.version} + + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.input.adapter.core + ${carbon.analytics.common.version} + org.wso2.carbon.analytics-common org.wso2.carbon.databridge.commons @@ -316,16 +326,16 @@ - - - - org.wso2.carbon.devicemgt-plugins org.wso2.carbon.device.mgt.iot ${carbon.devicemgt.plugins.version} - + + org.wso2.carbon.devicemgt-plugins + org.wso2.carbon.device.mgt.iot.output.adapter.mqtt + ${carbon.devicemgt.plugins.version} + org.wso2.carbon.devicemgt-plugins org.wso2.carbon.device.mgt.iot.api @@ -333,9 +343,7 @@ war - - org.wso2.carbon.devicemgt-plugins org.wso2.carbon.device.mgt.iot.androidsense.plugin @@ -348,9 +356,7 @@ war - - org.wso2.carbon.devicemgt-plugins org.wso2.carbon.device.mgt.iot.arduino.plugin @@ -363,9 +369,7 @@ war - - org.wso2.carbon.devicemgt-plugins org.wso2.carbon.device.mgt.iot.raspberrypi.plugin @@ -378,9 +382,7 @@ war - - org.wso2.carbon.devicemgt-plugins org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin @@ -399,27 +401,17 @@ ${carbon.devicemgt.plugins.version} war - org.wso2.carbon.devicemgt-plugins org.wso2.carbon.device.mgt.iot.virtualfirealarm.agent.impl ${carbon.devicemgt.plugins.version} - org.wso2.carbon.devicemgt-plugins org.wso2.carbon.device.mgt.iot.virtualfirealarm.agent.advanced.impl ${carbon.devicemgt.plugins.version} - - - - org.wso2.carbon.devicemgt-plugins - org.wso2.carbon.device.mgt.iot.camera.plugin - ${carbon.devicemgt.plugins.version} - - org.wso2.carbon.analytics @@ -443,11 +435,6 @@ org.wso2.carbon.device.mgt.mobile.url.printer ${carbon.devicemgt.plugins.version} - - org.wso2.carbon.devicemgt-plugins - org.wso2.carbon.device.mgt.mobile - ${carbon.devicemgt.plugins.version} - @@ -525,19 +512,6 @@ tomcat-servlet-api ${orbit.version.tomcat-servlet-api} - - - org.wso2.carbon.apimgt org.wso2.carbon.apimgt.api @@ -946,7 +920,11 @@ commons-io ${commons-io.version} - + + com.googlecode.json-simple.wso2 + json-simple + ${json-simple.version} + org.wso2.carbon.identity org.wso2.carbon.identity.oauth.stub @@ -959,7 +937,16 @@ ${carbon.identity.version} provided - + + org.apache.httpcomponents.wso2 + httpcore + ${httpcore.version} + + + org.wso2.orbit.org.apache.httpcomponents + httpclient + ${httpclient.version} + commons-collections commons-collections @@ -970,19 +957,22 @@ commons-configuration ${commons-configuration.version} - org.wso2.carbon.commons org.wso2.carbon.user.mgt ${carbon.commons.version} - junit junit test ${junit.version} + + com.jayway.jsonpath + json-path + ${json.path.version} + @@ -1096,11 +1086,16 @@ 2.4 3.0.0.wso2v1 + 0.9.1 + 1.1.wso2v1 3.2.2 1.8 7.0.59.wso2v1 - + + 4.3.1.wso2v2 + [4.3.1, 5.0.0) + 4.3.3.wso2v1 github-scm