/* * 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.agent.kura.firealarm.core.communication.xmpp; 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.FromContainsFilter; import org.jivesoftware.smack.filter.OrFilter; 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.device.mgt.iot.agent.kura.firealarm.core.communication.CommunicationHandler; import org.wso2.carbon.device.mgt.iot.agent.kura.firealarm.core.communication .CommunicationHandlerException; /** * This class contains the IoT-Server specific implementation for all the XMPP functionality. * This includes connecting to a XMPP Server & Login-In using the device's/server's XMPP-Account, * Setting listeners and filters on incoming XMPP messages and Sending XMPP replies for messages * received. Makes use of the 'Smack-XMPP' library provided by jivesoftware/igniterealtime. * <p/> * It is an abstract class that implements the common interface "CommunicationHandler". Whilst * providing some methods which handle key XMPP relevant tasks, this class implements only the * most generic methods of the "CommunicationHandler" interface. The rest of the methods are left * for any extended concrete-class to implement as per its need. */ public abstract class XMPPCommunicationHandler implements CommunicationHandler<Message> { private static final Log log = LogFactory.getLog(XMPPCommunicationHandler.class); protected String server; protected int timeoutInterval; // millis private static final int DEFAULT_XMPP_PORT = 5222; private XMPPConnection connection; private int port; private ConnectionConfiguration config; private PacketFilter filter; private PacketListener listener; /** * Constructor for XMPPCommunicationHandler passing only the server-IP. * * @param server the IP of the XMPP server. */ protected XMPPCommunicationHandler(String server) { this.server = server; this.port = DEFAULT_XMPP_PORT; this.timeoutInterval = DEFAULT_TIMEOUT_INTERVAL; initXMPPClient(); } /** * Constructor for XMPPCommunicationHandler passing server-IP and the XMPP-port. * * @param server the IP of the XMPP server. * @param port the XMPP server's port to connect to. (default - 5222) */ protected XMPPCommunicationHandler(String server, int port) { this.server = server; this.port = port; this.timeoutInterval = DEFAULT_TIMEOUT_INTERVAL; initXMPPClient(); } /** * Constructor for XMPPCommunicationHandler passing server-IP, the XMPP-port and the * timeoutInterval used by listeners to the server and for reconnection schedules. * * @param server the IP of the XMPP server. * @param port the XMPP server's port to connect to. (default - 5222) * @param timeoutInterval the timeout interval to use for the connection and reconnection */ protected XMPPCommunicationHandler(String server, int port, int timeoutInterval) { this.server = server; this.port = port; this.timeoutInterval = timeoutInterval; initXMPPClient(); } /** * Sets the client's time-out-limit whilst waiting for XMPP-replies from server. * * @param millis the time in millis to be set as the time-out-limit whilst waiting for a * XMPP-reply. */ public void setTimeoutInterval(int millis) { this.timeoutInterval = millis; } /** * Checks whether the connection to the XMPP-Server persists. * * @return true if the client is connected to the XMPP-Server, else false. */ @Override public boolean isConnected() { return connection.isConnected(); } /** * Initializes the XMPP Client. Sets the time-out-limit whilst waiting for XMPP-replies from * server. Sets the XMPP configurations to connect to the server and creates the * XMPPConnection object used for connecting and Logging-In. */ private void initXMPPClient() { log.info(String.format("Initializing connection to XMPP Server at %1$s via port " + "%2$d......", server, port)); SmackConfiguration.setPacketReplyTimeout(timeoutInterval); config = new ConnectionConfiguration(server, port); // TODO:: Need to enable SASL-Authentication appropriately config.setSASLAuthenticationEnabled(false); config.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled); connection = new XMPPConnection(config); } /** * Connects to the XMPP-Server and if attempt unsuccessful, then throws exception. * * @throws CommunicationHandlerException in the event of 'Connecting to' the XMPP server fails. */ protected void connectToServer() throws CommunicationHandlerException { try { connection.connect(); log.info(String.format( "Connection to XMPP Server at %1$s established successfully......", server)); } catch (XMPPException xmppExcepion) { String errorMsg = "Connection attempt to the XMPP Server at " + server + " via port " + port + " failed."; log.info(errorMsg); throw new CommunicationHandlerException(errorMsg, xmppExcepion); } } /** * If successfully established connection, then tries to Log in using the device's XMPP * Account credentials. * * @param username the username of the device's XMPP-Account. * @param password the password of the device's XMPP-Account. * @param resource the resource the resource, specific to the XMPP-Account to which the login * is made to * @throws CommunicationHandlerException in the event of 'Logging into' the XMPP server fails. */ protected void loginToServer(String username, String password, String resource) throws CommunicationHandlerException { if (isConnected()) { try { if (resource == null) { connection.login(username, password); log.info(String.format("Logged into XMPP Server at %1$s as user %2$s......", server, username)); } else { connection.login(username, password, resource); log.info(String.format( "Logged into XMPP Server at %1$s as user %2$s on resource %3$s......", server, username, resource)); } } catch (XMPPException xmppException) { String errorMsg = "Login attempt to the XMPP Server at " + server + " with username - " + username + " failed."; log.info(errorMsg); throw new CommunicationHandlerException(errorMsg, xmppException); } } else { String errorMsg = "Not connected to XMPP-Server to attempt Login. Please 'connectToServer' " + "before Login"; if (log.isDebugEnabled()) { log.debug(errorMsg); } throw new CommunicationHandlerException(errorMsg); } } /** * Sets a filter for all the incoming XMPP-Messages on the Sender's JID (XMPP-Account ID). * Also creates a listener for the incoming messages and connects the listener to the * XMPPConnection alongside the set filter. * * @param senderJID the JID (XMPP-Account ID of the sender) to which the filter is to be set. */ protected void setFilterOnSender(String senderJID) { filter = new AndFilter(new PacketTypeFilter(Message.class), new FromContainsFilter( senderJID)); listener = 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.setDaemon(true); msgProcessThread.start(); } } }; connection.addPacketListener(listener, filter); } /** * Sets a filter for all the incoming XMPP-Messages on the Receiver's JID (XMPP-Account ID). * Also creates a listener for the incoming messages and connects the listener to the * XMPPConnection alongside the set filter. * * @param receiverJID the JID (XMPP-Account ID of the receiver) to which the filter is to be * set. */ protected void setFilterOnReceiver(String receiverJID) { filter = new AndFilter(new PacketTypeFilter(Message.class), new ToContainsFilter( receiverJID)); listener = 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.setDaemon(true); msgProcessThread.start(); } } }; connection.addPacketListener(listener, filter); } /** * Sets a filter for all the incoming XMPP-Messages on the From-JID & To-JID (XMPP-Account IDs) * passed in. Also creates a listener for the incoming messages and connects the listener to * the XMPPConnection alongside the set filter. * * @param senderJID the From-JID (XMPP-Account ID) to which the filter is to be set. * @param receiverJID the To-JID (XMPP-Account ID) to which the filter is to be set. * @param andCondition if true: then filter is set with 'AND' operator (senderJID && * receiverJID), * if false: then the filter is set with 'OR' operator (senderJID | * receiverJID) */ protected void setMessageFilterAndListener(String senderJID, String receiverJID, boolean andCondition) { PacketFilter jidFilter; if (andCondition) { jidFilter = new AndFilter(new FromContainsFilter(senderJID), new ToContainsFilter( receiverJID)); } else { jidFilter = new OrFilter(new FromContainsFilter(senderJID), new ToContainsFilter( receiverJID)); } filter = new AndFilter(new PacketTypeFilter(Message.class), jidFilter); listener = 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.setDaemon(true); msgProcessThread.start(); } } }; connection.addPacketListener(listener, filter); } /** * Sends an XMPP message. Calls the overloaded method with Subject set to "Reply-From-Device" * * @param JID the JID (XMPP Account ID) to which the message is to be sent to. * @param message the XMPP-Message that is to be sent. */ protected void sendXMPPMessage(String JID, String message) { sendXMPPMessage(JID, message, "XMPP-Message"); } /** * Overloaded method to send an XMPP message. Includes the subject to be mentioned in the * message that is sent. * * @param JID the JID (XMPP Account ID) to which the message is to be sent to. * @param message the XMPP-Message that is to be sent. * @param subject the subject that the XMPP-Message would carry. */ protected void sendXMPPMessage(String JID, String message, String subject) { Message xmppMessage = new Message(); xmppMessage.setTo(JID); xmppMessage.setSubject(subject); xmppMessage.setBody(message); xmppMessage.setType(Message.Type.chat); sendXMPPMessage(JID, xmppMessage); } /** * Sends an XMPP message. * * @param JID the JID (XMPP Account ID) to which the message is to be sent to. * @param xmppMessage the XMPP-Message that is to be sent. */ protected void sendXMPPMessage(String JID, Message xmppMessage) { connection.sendPacket(xmppMessage); if (log.isDebugEnabled()) { log.debug("Message: '" + xmppMessage.getBody() + "' sent to XMPP JID [" + JID + "] sent successfully"); } } /** * Disables default debugger provided by the XMPPConnection. */ protected void disableDebugger() { connection.DEBUG_ENABLED = false; } /** * Closes the connection to the XMPP Server. */ public void closeConnection() { if (connection != null && isConnected()) { connection.disconnect(); } } }