org.opennms.netmgt.notifd.XMPPNotificationManager.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.netmgt.notifd.XMPPNotificationManager.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2005-2014 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2014 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.netmgt.notifd;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.io.IOUtils;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smackx.muc.MultiUserChat;
import org.opennms.core.logging.Logging;
import org.opennms.core.utils.ConfigFileConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Singleton class used to send messages to an XMPP Server. Used by
 * XMPPNotificationStragetgy and XMPPGroupNotificationStrategy
 *
 * @author <a href="mailto:jonathan@opennms.org">Jonathan Sartin</a>
 * @author <a href="mailto:ranger@opennms.org">Benjamin Reed</a>
 * @version $Id: $
 */
public class XMPPNotificationManager {

    private static final Logger LOG = LoggerFactory.getLogger(XMPPNotificationManager.class);

    private final Properties props = new Properties();

    private static final String LOG4J_CATEGORY = "notifd";

    private static final String XMPP_RESOURCE = "notifd";

    private static final String XMPP_PORT = "5222";

    private final XMPPTCPConnection xmpp;

    private final String xmppServer;

    private final String xmppUser;

    private final String xmppPassword;

    private final int xmppPort;

    private final Map<String, MultiUserChat> rooms = new HashMap<String, MultiUserChat>();

    private static XMPPNotificationManager instance = null;

    private ConnectionListener conlistener = new ConnectionListener() {
        @Override
        public void connectionClosed() {
            LOG.debug("XMPP connection closed");
        }

        @Override
        public void connectionClosedOnError(Exception e) {
            LOG.warn("XMPP connection closed", e);
        }

        @Override
        public void reconnectingIn(int seconds) {
            LOG.debug("XMPP reconnecting in {} seconds", seconds);
        }

        @Override
        public void reconnectionFailed(Exception e) {
            LOG.warn("XMPP reconnection failed", e);
            try {
                xmpp.disconnect();
            } catch (NotConnectedException ex) {
                LOG.error("XMPP disconnect failed", ex);
            }
            instance = null;
        }

        @Override
        public void reconnectionSuccessful() {
            LOG.debug("XMPP reconnection succeeded");
        }

        @Override
        public void authenticated(XMPPConnection conn) {
            LOG.debug("XMPP authenticated");
        }

        @Override
        public void connected(XMPPConnection conn) {
            LOG.debug("XMPP connected");
        }
    };

    /**
     * <p>Constructor for XMPPNotificationManager.</p>
     */
    protected XMPPNotificationManager() {
        // mdc may be null when executing via unit tests
        Map<String, String> mdc = Logging.getCopyOfContextMap();
        try {
            if (mdc != null) {
                mdc.put(Logging.PREFIX_KEY, LOG4J_CATEGORY);
            }

            // Load up some properties
            File config = null;
            try {
                config = ConfigFileConstants.getFile(ConfigFileConstants.XMPP_CONFIG_FILE_NAME);
            } catch (IOException e) {
                LOG.warn("{} not readable", ConfigFileConstants.XMPP_CONFIG_FILE_NAME, e);
            }
            if (Boolean.getBoolean("useSystemXMPPConfig") || !config.canRead()) {
                this.props.putAll(System.getProperties());
            } else {
                FileInputStream fis = null;
                try {
                    fis = new FileInputStream(config);
                    this.props.load(fis);
                } catch (FileNotFoundException e) {
                    LOG.warn("unable to load {}", config, e);
                } catch (IOException e) {
                    LOG.warn("unable to load {}", config, e);
                } finally {
                    IOUtils.closeQuietly(fis);
                }
            }

            xmppServer = this.props.getProperty("xmpp.server");
            String xmppServiceName = this.props.getProperty("xmpp.servicename", xmppServer);
            xmppUser = this.props.getProperty("xmpp.user");
            xmppPassword = this.props.getProperty("xmpp.pass");
            xmppPort = Integer.valueOf(this.props.getProperty("xmpp.port", XMPP_PORT));

            ConnectionConfiguration xmppConfig = new ConnectionConfiguration(xmppServer, xmppPort, xmppServiceName);

            boolean debuggerEnabled = Boolean.parseBoolean(props.getProperty("xmpp.debuggerEnabled"));
            xmppConfig.setDebuggerEnabled(debuggerEnabled);

            if (Boolean.parseBoolean(props.getProperty("xmpp.TLSEnabled"))) {
                xmppConfig.setSecurityMode(SecurityMode.enabled);
            } else {
                xmppConfig.setSecurityMode(SecurityMode.disabled);
            }

            if (Boolean.parseBoolean(props.getProperty("xmpp.selfSignedCertificateEnabled"))) {
                try {
                    SSLContext ctx = SSLContext.getInstance("TLS");
                    ctx.init(null, new TrustManager[] { new LocalSSLTrustManager() }, null);
                    xmppConfig.setCustomSSLContext(ctx);
                } catch (NoSuchAlgorithmException | KeyManagementException e) {
                    LOG.error("Failed to create a custom SSL context", e);
                }
            }

            // Remove all SALS mechanisms when SASL is disabled
            // There is currently no option to do this on a per-connection basis
            // so we must do it globally.
            if (!Boolean.parseBoolean(props.getProperty("xmpp.SASLEnabled", "true"))) {
                LOG.info("Removing all SALS mechanisms from Smack.");
                SmackConfiguration.removeSaslMechs(SmackConfiguration.getSaslMechs());
            }

            LOG.debug("XMPP Manager connection config: {}", xmppConfig);

            xmpp = new XMPPTCPConnection(xmppConfig);

            // Connect to xmpp server
            connectToServer();
        } finally {
            if (mdc != null) {
                Logging.setContextMap(mdc);
            }
        }
    }

    private void connectToServer() {
        try {
            LOG.debug("Attempting vanilla XMPP Connection to {}:{}", xmppServer, xmppPort);
            xmpp.connect();
            if (xmpp.isConnected()) {
                LOG.debug("XMPP Manager successfully connected");
                // Following requires a later version of the library
                if (xmpp.isSecureConnection())
                    LOG.debug("XMPP Manager successfully negotiated a secure connection");
                if (xmpp.isUsingTLS())
                    LOG.debug("XMPP Manager successfully negotiated a TLS connection");
                LOG.debug("XMPP Manager Connected");
                login();
                // Add connection listener
                xmpp.addConnectionListener(conlistener);
            } else {
                LOG.debug("XMPP Manager Not Connected");
            }
        } catch (Throwable e) {
            LOG.error("XMPP Manager unable to connect", e);
        }
    }

    /**
     * Check if manager is logged in to xmpp server.
     * 
     * @return true if logged in, false otherwise
     */

    private void login() {
        try {
            if (xmpp.isConnected()) {
                LOG.debug("XMPP Manager logging in");
                xmpp.login(xmppUser, xmppPassword, XMPP_RESOURCE);
                rooms.clear();
            } else {
                LOG.debug("XMPP Manager unable to login: Not connected to XMPP server");
            }
        } catch (Throwable e) {
            LOG.error("XMPP Manager unable to login: ", e);
        }
    }

    /**
     * get an instance of the XMPPNotificationManager
     *
     * @return instance of XMPPNotificationManager
     */
    public static synchronized XMPPNotificationManager getInstance() {

        if (instance == null) {
            instance = new XMPPNotificationManager();
        }

        return instance;

    }

    /**
     * <p>isLoggedIn</p>
     *
     * @return a boolean.
     */
    public boolean isLoggedIn() {
        return (xmpp.isAuthenticated());
    }

    /**
     * send an xmpp message to a specified recipient.
     * 
     * @param xmppTo
     *            recipient of the xmpp message
     * @param xmppMessage
     *            text to be sent in the body of the message
     * @return true if message is sent, false otherwise
     */

    private static class NullMessageListener implements MessageListener {
        @Override
        public void processMessage(Chat chat, Message message) {
        }
    }

    /**
     * <p>sendMessage</p>
     *
     * @param xmppTo a {@link java.lang.String} object.
     * @param xmppMessage a {@link java.lang.String} object.
     * @return a boolean.
     */
    public boolean sendMessage(String xmppTo, String xmppMessage) {
        if (!isLoggedIn()) {
            connectToServer();
        }
        try {
            ChatManager cm = ChatManager.getInstanceFor(xmpp);
            cm.createChat(xmppTo, new NullMessageListener()).sendMessage(xmppMessage);
            LOG.debug("XMPP Manager sent message to: {}", xmppTo);
        } catch (XMPPException | NotConnectedException e) {
            LOG.error("XMPP Exception Sending message ", e);
            return false;
        }

        return true;

    }

    /**
     * send an xmpp message to a specified Chat Room.
     *
     * @param xmppChatRoom
     *            room to send message to.
     * @param xmppMessage
     *            text to be sent in the body of the message
     * @return true if message is sent, false otherwise
     */
    public boolean sendGroupChat(String xmppChatRoom, String xmppMessage) {

        MultiUserChat groupChat;

        if (rooms.containsKey(xmppChatRoom)) {
            groupChat = rooms.get(xmppChatRoom);
        } else {
            LOG.debug("Adding room: {}", xmppChatRoom);
            groupChat = new MultiUserChat(xmpp, xmppChatRoom);
            rooms.put(xmppChatRoom, groupChat);
        }

        if (!groupChat.isJoined()) {
            LOG.debug("Joining room: {}", xmppChatRoom);
            try {
                groupChat.join(xmppUser);
            } catch (XMPPException | NoResponseException | NotConnectedException e) {
                LOG.error("XMPP Exception joining chat room ", e);
                return false;
            }
        }

        try {
            groupChat.sendMessage(xmppMessage);
            LOG.debug("XMPP Manager sent message to: {}", xmppChatRoom);
        } catch (XMPPException | NotConnectedException e) {
            LOG.error("XMPP Exception sending message to Chat room", e);
            return false;
        }

        return true;
    }

    /**
     * Trusts everything.
     */
    private static class LocalSSLTrustManager implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            // pass
        }

        @Override
        public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            // pass
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }
}