/*
* $Id$
*
* Copyright 2006, The jCoderZ.org Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
* * Neither the name of the jCoderZ.org Project nor the names of
* its contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jcoderz.phoenix.jabber;
import java.net.InetAddress;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smack.GroupChat;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
/**
* Simply sends a jabber message in a jabber group.
*
* <p>To setup different than the default connection call
* <code>JabberConnection.getInstance().setup(...)</code> prior the
* first call to say().</p>
*
* <p>This implementation tries to establish a static connection to a
* group chat and re-use this connection for further messages.</p>
*
* <p>By design there is only one connection per VM/classloader possible.</p>
*
* @author Andreas Mandel
*/
public final class Jabber
{
private static final int JABBER_DEFAULT_PORT = 5222;
private static final String CLASSNAME = Jabber.class.getName();
private static final Logger logger = Logger.getLogger(CLASSNAME);
private Jabber ()
{
// main class - no instances allowed.
}
/**
* Shortcut to send message directly from the command line.
* TODO: Allow different chat server.
* @param args the message to send.
*/
public static void main (String[] args)
{
JabberConnection.getInstance().say(args[0]);
}
/**
* Sends the given text message to the GroupChat.
* @param msg the message to send.
* @throws RuntimeException if sending fails (even after retry).
*/
public static void say (String msg)
{
JabberConnection.getInstance().say(msg);
}
private static class JabberConnection
{
private static final JabberConnection INSTANCE = new JabberConnection();
private static final int GRACEFUL_PERIOD = 1000;
private final String mHostname;
private String mJabberUserName = "cc";
private String mJabberUserPassword = "cc42";
private String mJabberServerName = "jabber.org";
private String mJabberHostAddress = "jabber.org";
private int mJabberHostPort = JABBER_DEFAULT_PORT;
private String mJabberMucName = "jcoderz@conference.jabber.org";
private final String mJabberMucAlias;
private XMPPConnection mConnection;
private GroupChat mGroupChat;
JabberConnection ()
{
String localhost;
try
{
localhost = InetAddress.getLocalHost().getHostName();
}
catch (Exception ex)
{
localhost = "localhost";
}
mHostname = localhost;
mJabberMucAlias
= System.getProperty("user.name", "cruise.control")
+ "@" + mHostname;
}
/**
* Returns the one and only instance of the jabber connection.
* @return the one and only instance of the jabber connection.
*/
public static JabberConnection getInstance ()
{
return INSTANCE;
}
/**
* Setup the connection parameters to be used.
* If there was already a connection to a group chat, this
* connection is closed.
* The connection is established after setting the bew parameters.
* Be prepared to catch a runtime exception if the connection fails.
* @param jabberHostName The host name of the JabberServer to connect to
* might also be it's numeric ip address.
* @param jabberHostPort The port to connect to (most likely 5222)
* @param jabberServerName The "virtual" server name of the jabber
* server (the part after the @ in the JIDs)
* @param jabberUserName The user name to connect as. This is
* the part before the @ in the JID.
* @param jabberUserPassword The password to use.
* @param jabberMucName The full qualified name of the MUC.
* (eg. talk@conf.jabber.org)
*/
public synchronized void setup (String jabberHostName,
int jabberHostPort, String jabberServerName, String jabberUserName,
String jabberUserPassword, String jabberMucName)
{
clear();
mJabberHostAddress = jabberHostName;
mJabberHostPort = jabberHostPort;
mJabberServerName = jabberServerName;
mJabberUserName = jabberUserName;
mJabberUserPassword = jabberUserPassword;
mJabberMucName = jabberMucName;
checkConnection();
}
/**
* Sends the given text message to the GroupChat
* @param message the message to send.
* @throws RuntimeException if sending fails (even after retry).
*/
public synchronized void say (String message)
{
try
{
checkConnection();
mGroupChat.sendMessage(message);
}
catch (Exception ex)
{
logger.log(Level.WARNING, "Exception sending message."
+ " Will retry", ex);
clear();
try
{
checkConnection();
mGroupChat.sendMessage(message);
}
catch (Exception e)
{
clear();
throw new RuntimeException("Jabber send message failed fatal!",
e);
}
}
}
/**
* Checks and ensures that a connection is established.
* @throws RuntimeException if connecting fails (even after retry).
*/
public synchronized void checkConnection ()
throws RuntimeException
{
try
{
checkConnectionRaw();
}
catch (Exception ex)
{
logger.log(Level.WARNING, "Exception while reconnecting (1).", ex);
mGroupChat = null;
}
try
{
checkConnectionRaw();
}
catch (Exception ex)
{
logger.log(Level.WARNING, "Exception while reconnecting (2).",
ex);
mConnection = null;
mGroupChat = null;
}
try
{
checkConnectionRaw();
}
catch (Exception ex)
{
mConnection = null;
mGroupChat = null;
throw new RuntimeException("Jabber connect failed fatal!", ex);
}
}
protected void finalize ()
throws Throwable
{
clear();
super.finalize();
}
private void checkConnectionRaw ()
throws XMPPException, InterruptedException
{
if (mConnection == null || !mConnection.isConnected())
{
clear();
mConnection = new XMPPConnection (// CHECKME: new SSLXMPPConnection(
mJabberHostAddress, mJabberHostPort, mJabberServerName);
mGroupChat = null;
logger.fine("New connection to jabber server. TLS: "
+ mConnection.isUsingTLS() + " secure: "
+ mConnection.isSecureConnection());
if (!mConnection.isConnected())
{
Thread.sleep(GRACEFUL_PERIOD);
}
}
if (!mConnection.isAuthenticated())
{
mConnection.login(mJabberUserName, mJabberUserPassword, mHostname);
mGroupChat = null;
logger.fine("Login to jabber server. ("
+ mConnection.isAuthenticated() + ").");
}
if (mGroupChat == null)
{
mGroupChat = mConnection.createGroupChat(mJabberMucName);
logger.fine("New group chat generated.");
}
if (!mGroupChat.isJoined())
{
mGroupChat.join(mJabberMucAlias);
if (!mGroupChat.isJoined())
{
Thread.sleep(GRACEFUL_PERIOD);
}
logger.fine("Joined to group chat. (" + mGroupChat.isJoined()
+ ").");
}
}
private void clear ()
{
try
{
if (mGroupChat != null)
{
mGroupChat.leave();
}
}
catch (Exception ex)
{
logger.log(Level.FINEST, "Exception while leaving groupchat.", ex);
}
mGroupChat = null;
try
{
if (mConnection != null)
{
mConnection.close();
}
}
catch (Exception ex)
{
logger.log(Level.FINEST,
"Exception while closing jabber connection.", ex);
}
mConnection = null;
}
}
}