So, Google used to have a nice page where they gave code sample on how to implement a GCM Xmpp server on this page:
https://developers.google.com/cloud-messaging/ccs
I had bookmarked this answer anytime I wanted to view the implementation
Confused about Google GCM XMPP
But everything linked from in that answer is gone. Google deleted them.
But now, they have removed everything, and added a page that just explains what it does. Sure, but the sample java code would be nice. I tried to go to the sample site on github that they link to
https://github.com/google/gcm
but it only provides samples of HTTP GCM server version.
Can somebody point me to a working code sample please? Or Is there a library out there that would work with c# at all? if not, I would just settle with the java version too.
Thanks.
Here is one I wrote for a class example (with some non-XMPP parts deleted to keep it small enough so I can post the code, but have the full server at http://people.eku.edu/styere/GcmPushServer.java):
// derived from https://developer.android.com/google/gcm/ccs.html
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
//import javax.swing.JScrollPane;
//import javax.swing.SwingUtilities;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketInterceptor;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.DefaultPacketExtension;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.util.StringUtils;
import org.json.simple.JSONValue;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;
import org.xmlpull.v1.XmlPullParser;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Random;
import java.net.*;
import java.io.*;
import java.util.*;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.HttpsURLConnection;
// list of all users and associated devices
class UserList {
// (--deleted--)
// constructor/read existing data from file
public UserList() {
// (--deleted--)
}
// add a new device to an existing user (called from networking thread)
// duplicate IDs are quietly accepted
public synchronized void addDevice( String uname, String newRegToken ) {
// (--deleted--)
}
// generate a group address/notification key
// !!!!!!!!!!!!!!! W A R N I N G !!!!!!!!!!!!!!!
// If the group addr/notification key is lost, it currently CANNOT be recovered/rebuilt
public String createNotificationKey( String nKeyName, String addr ) {
String[] idset = new String[1];
idset[0] = addr;
String newGroupAddr;
try {
// create a https connection to create the notification_key
URL url = new URL( "https://android.googleapis.com/gcm/notification" );
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
// send as a POST request
conn.setRequestMethod("POST");
// add request parameters
conn.addRequestProperty( "project_id", ""+GcmPushServer.senderId );
conn.addRequestProperty( "Authorization", "key="+GcmPushServer.password );
// use "set" since this may already exist
conn.setRequestProperty( "Content-Type", "application/json" );
// create data to send with request
Map<String,Object> sendData = new HashMap<>();
// create a new notification key
sendData.put( "operation", "create" );
sendData.put( "notification_key_name", (--deleted--) );
sendData.put( "registration_ids", (--deleted--) );
String strData = JSONValue.toJSONString(sendData);
// Send post request
conn.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
wr.writeBytes( strData );
wr.flush();
wr.close();
// successful?
int rc = conn.getResponseCode();
//System.out.println("ResponseCode = " + rc );
// read the response
Scanner input = new Scanner( conn.getInputStream() );
String resp="";
// read/display lines
while( input.hasNextLine() ) {
String s = input.nextLine();
resp += s;
}
//System.out.println("Response content: " + resp);
if ( rc == HttpsURLConnection.HTTP_OK ) {
JSONObject obj = (JSONObject) JSONValue.parse(resp);
newGroupAddr = (String) obj.get( "notification_key" );
//System.out.println(" new notification_key: " + newGroupAddr );
return newGroupAddr;
} else {
}
} catch ( Exception e ) {
//System.out.println("-- Exception: " + e.getMessage() );
}
return null;
}
// add/delete a single address to/from a notification key
// (add if doAdd is true, delete otherwise)
// removing final address will quietly delete the key
public static void modifyNotificationKey( String nKeyName, String nKey,
String addr, boolean doAdd ) {
String[] idset = new String[1];
idset[0] = addr;
try {
// create a https connection to create the notification_key
URL url = new URL( "https://android.googleapis.com/gcm/notification" );
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
// send as a POST request
conn.setRequestMethod("POST");
// add request parameters
conn.addRequestProperty( "project_id", ""+GcmPushServer.senderId );
conn.addRequestProperty( "Authorization", "key="+GcmPushServer.password );
// use "set" since this may already exist
conn.setRequestProperty( "Content-Type", "application/json" );
// create data to send with request
Map<String,Object> sendData = new HashMap<>();
// create a new notification key
sendData.put( "operation", doAdd? "add" : "remove" ); // add or delete key?
sendData.put( "notification_key_name", (--deleted--) );
sendData.put( "notification_key", (--deleted--) );
sendData.put( "registration_ids", (--deleted--) );
String strData = JSONValue.toJSONString(sendData);
//System.out.println("genGroupAddress POST data: " + strData );
// Send post request
conn.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
wr.writeBytes( strData );
wr.flush();
wr.close();
//conn.openConnection();
// successful?
int rc = conn.getResponseCode();
//System.out.println("ResponseCode = " + rc );
Scanner input = new Scanner( conn.getInputStream() );
// read/display lines
while( input.hasNextLine() ) {
System.out.println("Response content:");
String s = input.nextLine();
System.out.println( s );
}
} catch ( Exception e ) {
// do nothing
}
}
}
// text area for log messages
class LogTextArea extends JTextArea {
// update the "log" with the specified message
public void logMessage( String msg ) {
// (--deleted--)
}
}
class CcsServer {
private static final String GCM_SERVER = "gcm.googleapis.com";
private static final int GCM_PORT = 5235;
private static final String GCM_ELEMENT_NAME = "gcm";
private static final String GCM_NAMESPACE = "google:mobile:data";
// display/log area
LogTextArea logArea;
UserList users;
CcsServer( LogTextArea lta, UserList u ) {
logArea = lta;
users = u;
}
static {
ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE,
new PacketExtensionProvider() {
#Override
public PacketExtension parseExtension(XmlPullParser parser) throws
Exception {
String json = parser.nextText();
return new GcmPacketExtension(json);
}
});
}
private XMPPConnection connection;
/**
* Indicates whether the connection is in draining state, which means that it
* will not accept any new downstream messages.
*/
protected volatile boolean connectionDraining = false;
/**
* Sends a downstream message to GCM.
*
* #return true if the message has been successfully sent.
*/
public boolean sendDownstreamMessage(String jsonRequest) throws
NotConnectedException {
if (!connectionDraining) {
send(jsonRequest);
return true;
}
logArea.logMessage("Dropping downstream message since the connection is draining");
return false;
}
/**
* Returns a random message id to uniquely identify a message.
*
* <p>Note: This is generated by a pseudo random number generator for
* illustration purpose, and is not guaranteed to be unique.
*/
public String nextMessageId() {
return "m-" + UUID.randomUUID().toString();
}
/**
* Sends a packet with contents provided.
*/
protected void send(String jsonRequest) throws NotConnectedException {
Packet request = new GcmPacketExtension(jsonRequest).toPacket();
connection.sendPacket(request);
}
/**
* Handles an upstream data message from a device application.
*
* <p>This sample echo server sends an echo message back to the device.
* Subclasses should override this method to properly process upstream messages.
*/
protected void handleUpstreamMessage(Map<String, Object> jsonObject) {
// PackageName of the application that sent this message.
String category = (String) jsonObject.get("category");
String from = (String) jsonObject.get("from");
#SuppressWarnings("unchecked")
Map<String, String> payload = (Map<String, String>) jsonObject.get("data");
// what type of upstream message is this?
if ( ! payload.containsKey( "my_action" ) )
{
// Hmmm - this shouldn't happen!
logArea.logMessage( "handleUpstreamMessage - incoming message is missing my_action" );
// just ignore the message
return;
}
// what action do they want?
String my_action = (String) payload.get( "my_action" );
if ( my_action.equals( "edu.eku.styere.gcmpushclient.REGISTER" ) ) {
// registration request
String username = (String) payload.get( "username" );
logArea.logMessage( "Registration request: user=" + username + ", ID/Token=" + from );
// save the information
users.addDevice( username, from );
return;
} else {
// take default action of echoing the message
payload.put("ECHO", "Application: " + category);
// Send an ECHO response back
String echo = createJsonMessage(from, nextMessageId(), payload,
"echo:CollapseKey", null, false);
try {
sendDownstreamMessage(echo);
} catch (NotConnectedException e) {
logArea.logMessage( "Not connected anymore, echo message is not sent: " + e.getMessage() );
}
}
}
/**
* Handles an ACK.
*
* <p>Logs a INFO message, but subclasses could override it to
* properly handle ACKs.
*/
protected void handleAckReceipt(Map<String, Object> jsonObject) {
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
logArea.logMessage( "handleAckReceipt() from: " + from + ", messageId: " + messageId );
}
/**
* Handles a NACK.
*
* <p>Logs a INFO message, but subclasses could override it to
* properly handle NACKs.
*/
protected void handleNackReceipt(Map<String, Object> jsonObject) {
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
logArea.logMessage( "handleNackReceipt() from: " + from + ", messageId: " + messageId );
}
protected void handleControlMessage(Map<String, Object> jsonObject) {
logArea.logMessage( "handleControlMessage(): " + jsonObject );
String controlType = (String) jsonObject.get("control_type");
if ("CONNECTION_DRAINING".equals(controlType)) {
connectionDraining = true;
} else {
logArea.logMessage( "Unrecognised control type: " + controlType + ". This could "+
"happen if new features are " + "added to the CCS protocol." );
}
}
/**
* Creates a JSON encoded GCM message.
*
* #param to RegistrationId of the target device (Required).
* #param messageId Unique messageId for which CCS sends an
* "ack/nack" (Required).
* #param payload Message content intended for the application. (Optional).
* #param collapseKey GCM collapse_key parameter (Optional).
* #param timeToLive GCM time_to_live parameter (Optional).
* #param delayWhileIdle GCM delay_while_idle parameter (Optional).
* #return JSON encoded GCM message.
*/
public static String createJsonMessage(String to, String messageId,
Map<String, String> payload, String collapseKey, Long timeToLive,
Boolean delayWhileIdle) {
Map<String, Object> message = new HashMap<String, Object>();
message.put("to", to);
if (collapseKey != null) {
message.put("collapse_key", collapseKey);
}
if (timeToLive != null) {
message.put("time_to_live", timeToLive);
}
if (delayWhileIdle != null && delayWhileIdle) {
message.put("delay_while_idle", true);
}
message.put("message_id", messageId);
message.put("data", payload);
return JSONValue.toJSONString(message);
}
/**
* Creates a JSON encoded ACK message for an upstream message received
* from an application.
*
* #param to RegistrationId of the device who sent the upstream message.
* #param messageId messageId of the upstream message to be acknowledged to CCS.
* #return JSON encoded ack.
*/
protected static String createJsonAck(String to, String messageId) {
Map<String, Object> message = new HashMap<String, Object>();
message.put("message_type", "ack");
message.put("to", to);
message.put("message_id", messageId);
return JSONValue.toJSONString(message);
}
/**
* Connects to GCM Cloud Connection Server using the supplied credentials.
*
* #param senderId Your GCM project number
* #param apiKey API Key of your project
*/
public void connect(long senderId, String apiKey)
throws XMPPException, IOException, SmackException {
ConnectionConfiguration config =
new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
config.setSecurityMode(SecurityMode.enabled);
config.setReconnectionAllowed(true);
config.setRosterLoadedAtLogin(false);
config.setSendPresence(false);
config.setSocketFactory(SSLSocketFactory.getDefault());
connection = new XMPPTCPConnection(config);
connection.connect();
connection.addConnectionListener( new LoggingConnectionListener() );
// Handle incoming packets
connection.addPacketListener(new PacketListener() {
#Override
public void processPacket(Packet packet) {
logArea.logMessage( "Received: " + packet.toXML() );
Message incomingMessage = (Message) packet;
GcmPacketExtension gcmPacket =
(GcmPacketExtension) incomingMessage.
getExtension(GCM_NAMESPACE);
String json = gcmPacket.getJson();
try {
#SuppressWarnings("unchecked")
Map<String, Object> jsonObject =
(Map<String, Object>) JSONValue.
parseWithException(json);
// present for "ack"/"nack", null otherwise
Object messageType = jsonObject.get("message_type");
if (messageType == null) {
// Normal upstream data message
handleUpstreamMessage(jsonObject);
// Send ACK to CCS
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
String ack = createJsonAck(from, messageId);
send(ack);
} else if ("ack".equals(messageType.toString())) {
// Process Ack
handleAckReceipt(jsonObject);
} else if ("nack".equals(messageType.toString())) {
// Process Nack
handleNackReceipt(jsonObject);
} else if ("control".equals(messageType.toString())) {
// Process control message
handleControlMessage(jsonObject);
} else {
logArea.logMessage( "Unrecognised message type: " +
messageType.toString() );
}
} catch (ParseException e) {
logArea.logMessage( "Error parsing JSON " + json );
} catch (Exception e) {
logArea.logMessage( "Failed to process packet" );
}
}
}, new PacketTypeFilter(Message.class));
// Log all outgoing packets
connection.addPacketInterceptor(new PacketInterceptor() {
#Override
public void interceptPacket(Packet packet) {
logArea.logMessage( "Sent: " + packet.toXML());
}
}, new PacketTypeFilter(Message.class));
connection.login(senderId + "#gcm.googleapis.com", apiKey);
}
//---------- support classes ----------
/**
* XMPP Packet Extension for GCM Cloud Connection Server.
*/
private static class GcmPacketExtension extends DefaultPacketExtension {
private final String json;
public GcmPacketExtension(String json) {
super( GCM_ELEMENT_NAME, GCM_NAMESPACE);
this.json = json;
}
public String getJson() {
return json;
}
#Override
public String toXML() {
return String.format("<%s xmlns=\"%s\">%s</%s>",
GCM_ELEMENT_NAME, GCM_NAMESPACE,
StringUtils.escapeForXML(json), GCM_ELEMENT_NAME);
}
public Packet toPacket() {
Message message = new Message();
message.addExtension(this);
return message;
}
}
class LoggingConnectionListener implements ConnectionListener {
#Override
public void connected(XMPPConnection xmppConnection) {
logArea.logMessage( "Connected." );
}
#Override
public void authenticated(XMPPConnection xmppConnection) {
logArea.logMessage( "Authenticated." );
}
#Override
public void reconnectionSuccessful() {
logArea.logMessage( "Reconnecting.." );
}
#Override
public void reconnectionFailed(Exception e) {
logArea.logMessage( "Reconnection failed.. " + e.getMessage() );
}
#Override
public void reconnectingIn(int seconds) {
logArea.logMessage( "Reconnecting in " + seconds + " secs" );
}
#Override
public void connectionClosedOnError(Exception e) {
logArea.logMessage( "Connection closed on error." );
}
#Override
public void connectionClosed() {
logArea.logMessage( "Connection closed." );
}
}
}
// a non-editable combo (drop-down) box for destination addresses
class DestComboBox extends JComboBox<String> {
// (deleted to save space)
}
// screen panel for sending a message
class SendMessagePanel extends JPanel implements ActionListener, ItemListener {
// Message Types
protected final int MSGTYPE_NOTIFICATION = 1;
protected final int MSGTYPE_NOTIFICATION_DATA = 2;
protected final int MSGTYPE_DATA_COLLAPSE = 3;
protected final int MSGTYPE_DATA_NONCOLLAPSE = 4;
// log window
LogTextArea msgs;
// server class
CcsServer ccsServer;
// constructor
SendMessagePanel( UserList u, LogTextArea m, CcsServer c ) {
// (deleted to save space)
}
// respond to the button
#Override
public void actionPerformed(ActionEvent e) {
String toAddr = "zzzzzz"; // destination address
// may be device, notification_key, or topic address
ttl = 2419200; // time-to-live in seconds
// what type of message?
int msgTypeIndex = (--deleted--);
// create the message
Map<String, Object> message = new HashMap<String, Object>();
message.put("to", toAddr);
if ( msgTypeIndex == MSGTYPE_NOTIFICATION ||
msgTypeIndex == MSGTYPE_NOTIFICATION_DATA ||
msgTypeIndex == MSGTYPE_DATA_COLLAPSE ) {
// create a collapse key
message.put("collapse_key", "ck"+msgTypeIndex );
}
message.put("time_to_live", ttl);
message.put("message_id", ccsServer.nextMessageId());
// notification included?
if ( msgTypeIndex == MSGTYPE_NOTIFICATION ||
msgTypeIndex == MSGTYPE_NOTIFICATION_DATA ) {
// create the notification payload
HashMap<String, String> notePayload = new HashMap<>();
notePayload.put( "title", "Gcm Push Message Example" );
notePayload.put( "body", (--deleted--) );
// identify which notifications should replace older versions
notePayload.put( "tag", "ntag" + msgTypeIndex );
notePayload.put( "icon", "#drawable/new_picture" ); // notification icon
// additional stuff if we also have data
if ( msgTypeIndex == MSGTYPE_NOTIFICATION_DATA ) {
// what to do when the user opens the notification
notePayload.put( "click_action", "OPEN_MAIN_ACTIVITY" );
}
message.put( "notification", notePayload );
}
// data included?
if ( msgTypeIndex == MSGTYPE_NOTIFICATION_DATA ||
msgTypeIndex == MSGTYPE_DATA_COLLAPSE ||
msgTypeIndex == MSGTYPE_DATA_NONCOLLAPSE ) {
HashMap<String, String> dataPayload = new HashMap<>();
dataPayload.put( "contents", (--deleted--) );
message.put("data", dataPayload);
}
// actually send the message
try {
ccsServer.sendDownstreamMessage( JSONValue.toJSONString(message) );
} catch (NotConnectedException enc ) {
msgs.logMessage( "Not connected anymore, echo message is not sent: " + enc.getMessage() );
}
}
}
class BorderPanel extends JPanel {
// (--deleted--)
}
public class GcmPushServer
{
// (--deleted--)
}
Google's example implementation of an XMPP server is in the friendly-ping sample. It has Java and Go versions.
A little bit late, but here's:
The direct link to the sample app server for ccs connection Friendly Ping java
Wolfram Rittmeyer also created a gcm server, in his An XMPP Server for Google Cloud Messaging post
Related
Question is quite basic . I am following a tutorial on creating a chat application using tomcat7 . Everything works fine on localhost but as i deploy it on openshift it doesn't seem to be creating a socket connection .
link to the uploaded website is here : http://qabchat-survey987.rhcloud.com
Java Script file is given bellow which is responsible for sending and receiving messages between client and server
var sessionId = '';
// name of the client
var name = '';
// socket connection url and port
var socket_url = 'qabchat-survey987.rhcloud.com';
var port = '8443';
$(document).ready(function() {
$("#form_submit, #form_send_message").submit(function(e) {
e.preventDefault();
join();
});
});
var webSocket;
/**
* Connecting to socket
*/
function join() {
// Checking person name
if ($('#input_name').val().trim().length <= 0) {
alert('Enter your name');
} else {
name = $('#input_name').val().trim();
$('#prompt_name_container').fadeOut(1000, function() {
// opening socket connection
openSocket();
});
}
return false;
}
/**
* Will open the socket connection
*/
function openSocket() {
// Ensures only one connection is open at a time
if (webSocket !== undefined && webSocket.readyState !== WebSocket.CLOSED) {
return;
}
// Create a new instance of the websocket
webSocket = new WebSocket("ws://" + socket_url + ":" + port
+ "/qabchat/chat1?name=" + name);
/**
* Binds functions to the listeners for the websocket.
*/
webSocket.onopen = function(event) {
$('#message_container').fadeIn();
if (event.data === undefined)
return;
};
webSocket.onmessage = function(event) {
// parsing the json data
parseMessage(event.data);
};
webSocket.onclose = function(event) {
alert('Error! Connection is closed. Try connecting again.');
};
}
/**
* Sending the chat message to server
*/
function send() {
var message = $('#input_message').val();
if (message.trim().length > 0) {
sendMessageToServer('message', message);
} else {
alert('Please enter message to send!');
}
}
/**
* Closing the socket connection
*/
function closeSocket() {
webSocket.close();
$('#message_container').fadeOut(600, function() {
$('#prompt_name_container').fadeIn();
// clearing the name and session id
sessionId = '';
name = '';
// clear the ul li messages
$('#messages').html('');
$('p.online_count').hide();
});
}
/**
* Parsing the json message. The type of message is identified by 'flag' node
* value flag can be self, new, message, exit
*/
function parseMessage(message) {
var jObj = $.parseJSON(message);
// if the flag is 'self' message contains the session id
if (jObj.flag == 'self') {
sessionId = jObj.sessionId;
} else if (jObj.flag == 'new') {
// if the flag is 'new', a client joined the chat room
var new_name = 'You';
// number of people online
var online_count = jObj.onlineCount;
$('p.online_count').html(
'Hello, <span class="green">' + name + '</span>. <b>'
+ online_count + '</b> people online right now')
.fadeIn();
if (jObj.sessionId != sessionId) {
new_name = jObj.name;
}
var li = '<li class="new"><span class="name">' + new_name + '</span> '
+ jObj.message + '</li>';
$('#messages').append(li);
$('#input_message').val('');
ai
} else if (jObj.flag == 'message') {
// if the json flag is 'message', it means somebody sent the chat
// message
var from_name = 'You';
if (jObj.sessionId != sessionId) {
from_name = jObj.name;
}
var li = '<li><span class="name">' + from_name + '</span> '
+ jObj.message + '</li>';
// appending the chat message to list
appendChatMessage(li);
$('#input_message').val('');
} else if (jObj.flag == 'exit') {
// if the json flag is 'exit', it means somebody left the chat room
var li = '<li class="exit"><span class="name red">' + jObj.name
+ '</span> ' + jObj.message + '</li>';
var online_count = jObj.onlineCount;
$('p.online_count').html(
'Hello, <span class="green">' + name + '</span>. <b>'
+ online_count + '</b> people online right now');
appendChatMessage(li);
}
}
/**
* Appending the chat message to list
*/
function appendChatMessage(li) {
$('#messages').append(li);
// scrolling the list to bottom so that new message will be visible
$('#messages').scrollTop($('#messages').height());
}
/**
* Sending message to socket server message will be in json format
*/
function sendMessageToServer(flag, message) {
var json = '{""}';
// preparing json object
var myObject = new Object();
myObject.sessionId = sessionId;
myObject.message = message;
myObject.flag = flag;
// converting json object to json string
json = JSON.stringify(myObject);
// sending message to server
webSocket.send(json);
}
I have tried using 8000 port as well as using wss: instead of ws for the instance of websocket . Java file is given bellow :
#ServerEndpoint("/chat1")
public class SocketServer {
// set to store all the live sessions
private static final Set<Session> sessions = Collections
.synchronizedSet(new HashSet<Session>());
// Mapping between session and person name
private static final HashMap<String, String> nameSessionPair = new HashMap<String, String>();
private static final HashMap<String, String> SessionNamePair=new HashMap<String,String>();
private JSONUtils jsonUtils = new JSONUtils();
// Getting query params
public static Map<String, String> getQueryMap(String query) {
Map<String, String> map = Maps.newHashMap();
if (query != null) {
String[] params = query.split("&");
for (String param : params) {
String[] nameval = param.split("=");
map.put(nameval[0], nameval[1]);
}
}
return map;
}
/**
* Called when a socket connection opened
* */
#OnOpen
public void onOpen(Session session) {
System.out.println(session.getId() + " has opened a connection");
Map<String, String> queryParams = getQueryMap(session.getQueryString());
String name = "";
if (queryParams.containsKey("name")) {
// Getting client name via query param
name = queryParams.get("name");
try {
name = URLDecoder.decode(name, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// Mapping client name and session id
nameSessionPair.put(session.getId(), name);
SessionNamePair.put(name, session.getId());
}
// Adding session to session list
sessions.add(session);
try {
// Sending session id to the client that just connected
session.getBasicRemote().sendText(
jsonUtils.getClientDetailsJson(session.getId(),
"Your session details"));
} catch (IOException e) {
e.printStackTrace();
}
// Notifying all the clients about new person joined
sendMessageToAll(session.getId(), name, " joined conversation!", true,
false);
}
/**
* method called when new message received from any client
*
* #param message
* JSON message from client
* */
#OnMessage
public void onMessage(String message, Session session) {
System.out.println("Message from " + session.getId() + ": " + message);
String msg = null;
// Parsing the json and getting message
try {
JSONObject jObj = new JSONObject(message);
msg = jObj.getString("message");
} catch (JSONException e) {
e.printStackTrace();
}
// Sending the message to all clients
sendMessageToAll(session.getId(), nameSessionPair.get(session.getId()),
msg, false, false);
}
/**
* Method called when a connection is closed
* */
#OnClose
public void onClose(Session session) {
System.out.println("Session " + session.getId() + " has ended");
// Getting the client name that exited
String name = nameSessionPair.get(session.getId());
// removing the session from sessions list
sessions.remove(session);
// Notifying all the clients about person exit
sendMessageToAll(session.getId(), name, " left conversation!", false,
true);
}
/**
* Method to send message to all clients
*
* #param sessionId
* #param message
* message to be sent to clients
* #param isNewClient
* flag to identify that message is about new person joined
* #param isExit
* flag to identify that a person left the conversation
* */
private void sendMessageToAll(String sessionId, String name,
String message, boolean isNewClient, boolean isExit) {
// Looping through all the sessions and sending the message individually
for (Session s : sessions) {
String json = null;
// Checking if the message is about new client joined
if (isNewClient) {
json = jsonUtils.getNewClientJson(sessionId, name, message,
sessions.size());
} else if (isExit) {
// Checking if the person left the conversation
json = jsonUtils.getClientExitJson(sessionId, name, message,
sessions.size());
} else {
// Normal chat conversation message
json = jsonUtils
.getSendAllMessageJson(sessionId, name, message);
}
try {
System.out.println("Sending Message To: " + sessionId + ", "
+ json + SessionNamePair.get(name));
s.getBasicRemote().sendText(json);
} catch (IOException e) {
System.out.println("error in sending. " + s.getId() + ", "
+ e.getMessage());
e.printStackTrace();
}
}
}
}
I can not seem to figure out why the code is not working on openshift while it works just fine on localhost . Name of the main root directory of the project in openshift is qabchat.
I ran a simple test found out that after the onopen() function in javascript file , onclose() function is being called .
From start I'am new to Java Developing... I've tried to use the folowing Java code on NetBeans IDE:
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketInterceptor;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.DefaultPacketExtension;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.util.StringUtils;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
import org.xmlpull.v1.XmlPullParser;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLSocketFactory;
/**
* Sample Smack implementation of a client for GCM Cloud Connection Server. This
* code can be run as a standalone CCS client.
*
* <p>For illustration purposes only.
*/
public class SmackCcsClient {
private static final Logger logger = Logger.getLogger("SmackCcsClient");
private static final String GCM_SERVER = "gcm.googleapis.com";
private static final int GCM_PORT = 5235;
private static final String GCM_ELEMENT_NAME = "gcm";
private static final String GCM_NAMESPACE = "google:mobile:data";
static {
ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE,
new PacketExtensionProvider() {
#Override
public PacketExtension parseExtension(XmlPullParser parser) throws
Exception {
String json = parser.nextText();
return new GcmPacketExtension(json);
}
});
}
private XMPPConnection connection;
/**
* Indicates whether the connection is in draining state, which means that it
* will not accept any new downstream messages.
*/
protected volatile boolean connectionDraining = false;
/**
* Sends a downstream message to GCM.
*
* #return true if the message has been successfully sent.
*/
public boolean sendDownstreamMessage(String jsonRequest) throws
NotConnectedException {
if (!connectionDraining) {
send(jsonRequest);
return true;
}
logger.info("Dropping downstream message since the connection is draining");
return false;
}
/**
* Returns a random message id to uniquely identify a message.
*
* <p>Note: This is generated by a pseudo random number generator for
* illustration purpose, and is not guaranteed to be unique.
*/
public String nextMessageId() {
return "m-" + UUID.randomUUID().toString();
}
/**
* Sends a packet with contents provided.
*/
protected void send(String jsonRequest) throws NotConnectedException {
Packet request = new GcmPacketExtension(jsonRequest).toPacket();
connection.sendPacket(request);
}
/**
* Handles an upstream data message from a device application.
*
* <p>This sample echo server sends an echo message back to the device.
* Subclasses should override this method to properly process upstream messages.
*/
protected void handleUpstreamMessage(Map<String, Object> jsonObject) {
// PackageName of the application that sent this message.
String category = (String) jsonObject.get("category");
String from = (String) jsonObject.get("from");
#SuppressWarnings("unchecked")
Map<String, String> payload = (Map<String, String>) jsonObject.get("data");
payload.put("ECHO", "Application: " + category);
// Send an ECHO response back
String echo = createJsonMessage(from, nextMessageId(), payload,
"echo:CollapseKey", null, false);
try {
sendDownstreamMessage(echo);
} catch (NotConnectedException e) {
logger.log(Level.WARNING, "Not connected anymore, echo message is
not sent", e);
}
}
/**
* Handles an ACK.
*
* <p>Logs a INFO message, but subclasses could override it to
* properly handle ACKs.
*/
protected void handleAckReceipt(Map<String, Object> jsonObject) {
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
logger.log(Level.INFO, "handleAckReceipt() from: " + from + ",
messageId: " + messageId);
}
/**
* Handles a NACK.
*
* <p>Logs a INFO message, but subclasses could override it to
* properly handle NACKs.
*/
protected void handleNackReceipt(Map<String, Object> jsonObject) {
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
logger.log(Level.INFO, "handleNackReceipt() from: " + from + ",
messageId: " + messageId);
}
protected void handleControlMessage(Map<String, Object> jsonObject) {
logger.log(Level.INFO, "handleControlMessage(): " + jsonObject);
String controlType = (String) jsonObject.get("control_type");
if ("CONNECTION_DRAINING".equals(controlType)) {
connectionDraining = true;
} else {
logger.log(Level.INFO, "Unrecognized control type: %s. This could
happen if new features are " + "added to the CCS protocol.",
controlType);
}
}
/**
* Creates a JSON encoded GCM message.
*
* #param to RegistrationId of the target device (Required).
* #param messageId Unique messageId for which CCS sends an
* "ack/nack" (Required).
* #param payload Message content intended for the application. (Optional).
* #param collapseKey GCM collapse_key parameter (Optional).
* #param timeToLive GCM time_to_live parameter (Optional).
* #param delayWhileIdle GCM delay_while_idle parameter (Optional).
* #return JSON encoded GCM message.
*/
public static String createJsonMessage(String to, String messageId,
Map<String, String> payload, String collapseKey, Long timeToLive,
Boolean delayWhileIdle) {
Map<String, Object> message = new HashMap<String, Object>();
message.put("to", to);
if (collapseKey != null) {
message.put("collapse_key", collapseKey);
}
if (timeToLive != null) {
message.put("time_to_live", timeToLive);
}
if (delayWhileIdle != null && delayWhileIdle) {
message.put("delay_while_idle", true);
}
message.put("message_id", messageId);
message.put("data", payload);
return JSONValue.toJSONString(message);
}
/**
* Creates a JSON encoded ACK message for an upstream message received
* from an application.
*
* #param to RegistrationId of the device who sent the upstream message.
* #param messageId messageId of the upstream message to be acknowledged to CCS.
* #return JSON encoded ack.
*/
protected static String createJsonAck(String to, String messageId) {
Map<String, Object> message = new HashMap<String, Object>();
message.put("message_type", "ack");
message.put("to", to);
message.put("message_id", messageId);
return JSONValue.toJSONString(message);
}
/**
* Connects to GCM Cloud Connection Server using the supplied credentials.
*
* #param senderId Your GCM project number
* #param apiKey API Key of your project
*/
public void connect(long senderId, String apiKey)
throws XMPPException, IOException, SmackException {
ConnectionConfiguration config =
new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
config.setSecurityMode(SecurityMode.enabled);
config.setReconnectionAllowed(true);
config.setRosterLoadedAtLogin(false);
config.setSendPresence(false);
config.setSocketFactory(SSLSocketFactory.getDefault());
connection = new XMPPTCPConnection(config);
connection.connect();
connection.addConnectionListener(new LoggingConnectionListener());
// Handle incoming packets
connection.addPacketListener(new PacketListener() {
#Override
public void processPacket(Packet packet) {
logger.log(Level.INFO, "Received: " + packet.toXML());
Message incomingMessage = (Message) packet;
GcmPacketExtension gcmPacket =
(GcmPacketExtension) incomingMessage.
getExtension(GCM_NAMESPACE);
String json = gcmPacket.getJson();
try {
#SuppressWarnings("unchecked")
Map<String, Object> jsonObject =
(Map<String, Object>) JSONValue.
parseWithException(json);
// present for "ack"/"nack", null otherwise
Object messageType = jsonObject.get("message_type");
if (messageType == null) {
// Normal upstream data message
handleUpstreamMessage(jsonObject);
// Send ACK to CCS
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
String ack = createJsonAck(from, messageId);
send(ack);
} else if ("ack".equals(messageType.toString())) {
// Process Ack
handleAckReceipt(jsonObject);
} else if ("nack".equals(messageType.toString())) {
// Process Nack
handleNackReceipt(jsonObject);
} else if ("control".equals(messageType.toString())) {
// Process control message
handleControlMessage(jsonObject);
} else {
logger.log(Level.WARNING,
"Unrecognized message type (%s)",
messageType.toString());
}
} catch (ParseException e) {
logger.log(Level.SEVERE, "Error parsing JSON " + json, e);
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to process packet", e);
}
}
}, new PacketTypeFilter(Message.class));
// Log all outgoing packets
connection.addPacketInterceptor(new PacketInterceptor() {
#Override
public void interceptPacket(Packet packet) {
logger.log(Level.INFO, "Sent: {0}", packet.toXML());
}
}, new PacketTypeFilter(Message.class));
connection.login(senderId + "#gcm.googleapis.com", apiKey);
}
public static void main(String[] args) throws Exception {
final long senderId = 1234567890L; // your GCM sender id
final String password = "Your API key";
SmackCcsClient ccsClient = new SmackCcsClient();
ccsClient.connect(senderId, password);
// Send a sample hello downstream message to a device.
String toRegId = "RegistrationIdOfTheTargetDevice";
String messageId = ccsClient.nextMessageId();
Map<String, String> payload = new HashMap<String, String>();
payload.put("Hello", "World");
payload.put("CCS", "Dummy Message");
payload.put("EmbeddedMessageId", messageId);
String collapseKey = "sample";
Long timeToLive = 10000L;
String message = createJsonMessage(toRegId, messageId, payload,
collapseKey, timeToLive, true);
ccsClient.sendDownstreamMessage(message);
}
/**
* XMPP Packet Extension for GCM Cloud Connection Server.
*/
private static final class GcmPacketExtension extends DefaultPacketExtension {
private final String json;
public GcmPacketExtension(String json) {
super(GCM_ELEMENT_NAME, GCM_NAMESPACE);
this.json = json;
}
public String getJson() {
return json;
}
#Override
public String toXML() {
return String.format("<%s xmlns=\"%s\">%s</%s>",
GCM_ELEMENT_NAME, GCM_NAMESPACE,
StringUtils.escapeForXML(json), GCM_ELEMENT_NAME);
}
public Packet toPacket() {
Message message = new Message();
message.addExtension(this);
return message;
}
}
private static final class LoggingConnectionListener
implements ConnectionListener {
#Override
public void connected(XMPPConnection xmppConnection) {
logger.info("Connected.");
}
#Override
public void authenticated(XMPPConnection xmppConnection) {
logger.info("Authenticated.");
}
#Override
public void reconnectionSuccessful() {
logger.info("Reconnecting..");
}
#Override
public void reconnectionFailed(Exception e) {
logger.log(Level.INFO, "Reconnection failed.. ", e);
}
#Override
public void reconnectingIn(int seconds) {
logger.log(Level.INFO, "Reconnecting in %d secs", seconds);
}
#Override
public void connectionClosedOnError(Exception e) {
logger.info("Connection closed on error.");
}
#Override
public void connectionClosed() {
logger.info("Connection closed.");
}
}
}
Source code I found on following link:
http://developer.android.com/google/gcm/ccs.html#implement
I imported already Smack 4.1.1 on it and also json-simple-1.1.1, xmlpull-1.1.3.1, junit...
Problem lyies on Smack library, iam getting a lot of errors, even i already imported all jar files from it... I understand that Smack library is having a BIG history during development... still i would like to understand what I'am doing... Found a possible solution on following link:
GCM XMPP Server using Smack 4.1.0
Maybe I can use it, maybe not... Are there any Tutorials? Maybe some better examples? Is every version on Smack having it's own Documentation?
build.gradle(Module: app)
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.example.sqltest2"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.1.1'
compile 'com.google.android.gms:play-services:7.3.0'
compile "org.igniterealtime.smack:smack-android:4.1.0-rc1"
compile "org.igniterealtime.smack:smack-tcp:4.1.0-rc1"
compile "org.igniterealtime.smack:smack-extensions:4.1.0-rc1"
compile "org.igniterealtime.smack:smack-im:4.1.0-rc1"
}
I am working on a project to build a group chat app on socket server!
but when I start the server it get started and displays the following error:
HTTP Status 404- /GroupChat/
type Status report
message /GroupChat/
description The requested resource is not available.
I am using a J2EE eclipse where I have added a tomcat 7 server.
Below are my two files:
JSONUtils.java
package com.groupchat;
import org.json.JSONException;
import org.json.JSONObject;
public class JSONUtils {
// flags to identify the kind of json response on client side
private static final String FLAG_SELF = "self", FLAG_NEW = "new",
FLAG_MESSAGE = "message", FLAG_EXIT = "exit";
public JSONUtils() {
}
/**
* Json when client needs it's own session details
* */
public String getClientDetailsJson(String sessionId, String message) {
String json = null;
try {
JSONObject jObj = new JSONObject();
jObj.put("flag", FLAG_SELF);
jObj.put("sessionId", sessionId);
jObj.put("message", message);
json = jObj.toString();
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
/**
* Json to notify all the clients about new person joined
* */
public String getNewClientJson(String sessionId, String name,
String message, int onlineCount) {
String json = null;
try {
JSONObject jObj = new JSONObject();
jObj.put("flag", FLAG_NEW);
jObj.put("name", name);
jObj.put("sessionId", sessionId);
jObj.put("message", message);
jObj.put("onlineCount", onlineCount);
json = jObj.toString();
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
/**
* Json when the client exits the socket connection
* */
public String getClientExitJson(String sessionId, String name,
String message, int onlineCount) {
String json = null;
try {
JSONObject jObj = new JSONObject();
jObj.put("flag", FLAG_EXIT);
jObj.put("name", name);
jObj.put("sessionId", sessionId);
jObj.put("message", message);
jObj.put("onlineCount", onlineCount);
json = jObj.toString();
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
/**
* JSON when message needs to be sent to all the clients
* */
public String getSendAllMessageJson(String sessionId, String fromName,
String message) {
String json = null;
try {
JSONObject jObj = new JSONObject();
jObj.put("flag", FLAG_MESSAGE);
jObj.put("sessionId", sessionId);
jObj.put("name", fromName);
jObj.put("message", message);
json = jObj.toString();
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
}
SocketServer.java
#ServerEndpoint("/chat")
public class SocketServer {
// set to store all the live sessions
private static final Set<Session> sessions = Collections
.synchronizedSet(new HashSet<Session>());
// Mapping between session and person name
private static final HashMap<String, String> nameSessionPair = new HashMap<String, String>();
private JSONUtils jsonUtils = new JSONUtils();
// Getting query params
public static Map<String, String> getQueryMap(String query) {
Map<String, String> map = Maps.newHashMap();
if (query != null) {
String[] params = query.split("&");
for (String param : params) {
String[] nameval = param.split("=");
map.put(nameval[0], nameval[1]);
}
}
return map;
}
/**
* Called when a socket connection opened
* */
#OnOpen
public void onOpen(Session session) {
System.out.println(session.getId() + " has opened a connection");
Map<String, String> queryParams = getQueryMap(session.getQueryString());
String name = "";
if (queryParams.containsKey("name")) {
// Getting client name via query param
name = queryParams.get("name");
try {
name = URLDecoder.decode(name, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// Mapping client name and session id
nameSessionPair.put(session.getId(), name);
}
// Adding session to session list
sessions.add(session);
try {
// Sending session id to the client that just connected
session.getBasicRemote().sendText(
jsonUtils.getClientDetailsJson(session.getId(),
"Your session details"));
} catch (IOException e) {
e.printStackTrace();
}
// Notifying all the clients about new person joined
sendMessageToAll(session.getId(), name, " joined conversation!", true, false);
}
/**
* method called when new message received from any client
*
* #param message
* JSON message from client
* */
#OnMessage
public void onMessage(String message, Session session) {
System.out.println("Message from " + session.getId() + ": " + message);
String msg = null;
// Parsing the json and getting message
try {
JSONObject jObj = new JSONObject(message);
msg = jObj.getString("message");
} catch (JSONException e) {
e.printStackTrace();
}
// Sending the message to all clients
sendMessageToAll(session.getId(), nameSessionPair.get(session.getId()),
msg, false, false);
}
/**
* Method called when a connection is closed
* */
#OnClose
public void onClose(Session session) {
System.out.println("Session " + session.getId() + " has ended");
// Getting the client name that exited
String name = nameSessionPair.get(session.getId());
// removing the session from sessions list
sessions.remove(session);
// Notifying all the clients about person exit
sendMessageToAll(session.getId(), name, " left conversation!", false, true);
}
/**
* Method to send message to all clients
*
* #param sessionId
* #param message
* message to be sent to clients
* #param isNewClient
* flag to identify that message is about new person joined
* #param isExit
* flag to identify that a person left the conversation
* */
private void sendMessageToAll(String sessionId, String name,
String message, boolean isNewClient, boolean isExit) {
// Looping through all the sessions and sending the message individually
for (Session s : sessions) {
String json = null;
// Checking if the message is about new client joined
if (isNewClient) {
json = jsonUtils.getNewClientJson(sessionId, name, message,
sessions.size());
} else if (isExit) {
// Checking if the person left the conversation
json = jsonUtils.getClientExitJson(sessionId, name, message, sessions.size());
} else {
// Normal chat conversation message
json = jsonUtils
.getSendAllMessageJson(sessionId, name, message);
}
try {
System.out.println("Sending Message To: " + sessionId + ", " + json);
s.getBasicRemote().sendText(json);
} catch (IOException e) {
System.out.println("error in sending. " + s.getId() + ", "
+ e.getMessage());
e.printStackTrace();
}
}
}
}
main.js
#ServerEndpoint("/chat")
public class SocketServer {
// set to store all the live sessions
private static final Set<Session> sessions = Collections
.synchronizedSet(new HashSet<Session>());
// Mapping between session and person name
private static final HashMap<String, String> nameSessionPair = new HashMap<String, String>();
private JSONUtils jsonUtils = new JSONUtils();
// Getting query params
public static Map<String, String> getQueryMap(String query) {
Map<String, String> map = Maps.newHashMap();
if (query != null) {
String[] params = query.split("&");
for (String param : params) {
String[] nameval = param.split("=");
map.put(nameval[0], nameval[1]);
}
}
return map;
}
/**
* Called when a socket connection opened
* */
#OnOpen
public void onOpen(Session session) {
System.out.println(session.getId() + " has opened a connection");
Map<String, String> queryParams = getQueryMap(session.getQueryString());
String name = "";
if (queryParams.containsKey("name")) {
// Getting client name via query param
name = queryParams.get("name");
try {
name = URLDecoder.decode(name, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// Mapping client name and session id
nameSessionPair.put(session.getId(), name);
}
// Adding session to session list
sessions.add(session);
try {
// Sending session id to the client that just connected
session.getBasicRemote().sendText(
jsonUtils.getClientDetailsJson(session.getId(),
"Your session details"));
} catch (IOException e) {
e.printStackTrace();
}
// Notifying all the clients about new person joined
sendMessageToAll(session.getId(), name, " joined conversation!", true, false);
}
/**
* method called when new message received from any client
*
* #param message
* JSON message from client
* */
#OnMessage
public void onMessage(String message, Session session) {
System.out.println("Message from " + session.getId() + ": " + message);
String msg = null;
// Parsing the json and getting message
try {
JSONObject jObj = new JSONObject(message);
msg = jObj.getString("message");
} catch (JSONException e) {
e.printStackTrace();
}
// Sending the message to all clients
sendMessageToAll(session.getId(), nameSessionPair.get(session.getId()), msg, false, false);
}
/**
* Method called when a connection is closed
* */
#OnClose
public void onClose(Session session) {
System.out.println("Session " + session.getId() + " has ended");
// Getting the client name that exited
String name = nameSessionPair.get(session.getId());
// removing the session from sessions list
sessions.remove(session);
// Notifying all the clients about person exit
sendMessageToAll(session.getId(), name, " left conversation!", false, true);
}
/**
* Method to send message to all clients
*
* #param sessionId
* #param message
* message to be sent to clients
* #param isNewClient
* flag to identify that message is about new person joined
* #param isExit
* flag to identify that a person left the conversation
* */
private void sendMessageToAll(String sessionId, String name,
String message, boolean isNewClient, boolean isExit) {
// Looping through all the sessions and sending the message individually
for (Session s : sessions) {
String json = null;
// Checking if the message is about new client joined
if (isNewClient) {
json = jsonUtils.getNewClientJson(sessionId, name, message,
sessions.size());
} else if (isExit) {
// Checking if the person left the conversation
json = jsonUtils.getClientExitJson(sessionId, name, message, sessions.size());
} else {
// Normal chat conversation message
json = jsonUtils
.getSendAllMessageJson(sessionId, name, message);
}
try {
System.out.println("Sending Message To: " + sessionId + ", " + json);
s.getBasicRemote().sendText(json);
} catch (IOException e) {
System.out.println("error in sending. " + s.getId() + ", "
+ e.getMessage());
e.printStackTrace();
}
}
}
}
Firstly, the 404 code indicates that the element doesn't exist, then maybe your problem is the annotation #ServerEndpoint("/chat"), the HTTP request is looking for a /GroupChat/ element. Now, also you must review your settings files (web.xml, context.xml, application.xml, etc) because you could have an context attribute or display-name attribute misconfigured.
I hope this information helps you.
Good Luck.
I am somewhat new to Google Cloud Messaging. We have been working with it for a couple of months but just recently we have been getting "Connection Draining" messages. When this happens all communication stops.
Google says: https://developer.android.com/google/gcm/ccs.html#response
When you receive a CONNECTION_DRAINING message, you should
immediately begin sending messages to another CCS connection, opening
a new connection if necessary. You should, however, keep the original
connection open and continue receiving messages that may come over the
connection (and ACKing them)—CCS will handle initiating a connection
close when it is ready.
My question is
If I open a new connection manually, how does it know what connection to use if I don't close the existing connection?
If 6 messages are sent concurrently how do I stop the method from opening 6 connections? Or am I confused on this?
Why does connection draining happen?
I am surprised this isn't already put into play in their example code. It seems like its pretty much everything you need. Is it already done for me in the code and I am missing it?
I don't have a main method in my code, I user servlets as triggers instead. My connection is initailized like this
#PostConstruct
public void init() throws Exception{
try {
smackCcsClient.connect(Long.parseLong(env.getProperty("gcm.api")), env.getProperty("gcm.key"));
}catch (IOException e ){
e.printStackTrace();
}catch(SmackException e){
e.printStackTrace();
}catch(XMPPException e){
e.printStackTrace();
}
}
however after this I never touch the connection again. Am I handling this wrong, is the connection something I should be touching more frequently or something I need to keep track of?
_______________________ADDED AFTER THE QUESTION_________________________
I added a connection inside of their example code to try to reinitialize a connection. It looks like this:
if ("CONNECTION_DRAINING".equals(controlType)) {
connectionDraining = true;
//Open new connection because old connection will be closing or is already closed.
try {
connect(Long.parseLong(env.getProperty("gcm.api")), env.getProperty("gcm.key"));
} catch (XMPPException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SmackException e) {
e.printStackTrace();
}
} else {
logger.log(Level.INFO, "Unrecognized control type: %s. This could happen if new features are " + "added to the CCS protocol.",
controlType);
}
I have written a code for handling such cases(basically diverting new downstream messages to a new connection)... not thoroughly tested...
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedDeque;
import javax.net.ssl.SSLSocketFactory;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketInterceptor;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.DefaultPacketExtension;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmlpull.v1.XmlPullParser;
import com.fasterxml.jackson.core.type.TypeReference;
/**
* Based on https://developer.android.com/google/gcm/ccs.html#smack
*
* #author Abhinav.Dwivedi
*
*/
public class SmackCcsClient implements CcsClient {
private static final Logger logger = LoggerFactory.getLogger(SmackCcsClient.class);
private static final String GCM_SERVER = "gcm.googleapis.com";
private static final int GCM_PORT = 5235;
private static final String GCM_ELEMENT_NAME = "gcm";
private static final String GCM_NAMESPACE = "google:mobile:data";
private static volatile SmackCcsClient instance;
static {
ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE, new PacketExtensionProvider() {
#Override
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
String json = parser.nextText();
return new GcmPacketExtension(json);
}
});
}
private final Deque<Channel> channels;
public static SmackCcsClient instance() {
if (instance == null) {
synchronized (SmackCcsClient.class) {
if (instance == null) {
instance = new SmackCcsClient();
}
}
}
return instance;
}
private SmackCcsClient() {
channels = new ConcurrentLinkedDeque<Channel>();
channels.addFirst(connect());
}
private class Channel {
private XMPPConnection connection;
/**
* Indicates whether the connection is in draining state, which means that it will not accept any new downstream
* messages.
*/
private volatile boolean connectionDraining = false;
/**
* Sends a packet with contents provided.
*/
private void send(String jsonRequest) throws NotConnectedException {
Packet request = new GcmPacketExtension(jsonRequest).toPacket();
connection.sendPacket(request);
}
private void handleControlMessage(Map<String, Object> jsonObject) {
logger.debug("handleControlMessage(): {}", jsonObject);
String controlType = (String) jsonObject.get("control_type");
if ("CONNECTION_DRAINING".equals(controlType)) {
connectionDraining = true;
} else {
logger.info("Unrecognized control type: {}. This could happen if new features are "
+ "added to the CCS protocol.", controlType);
}
}
}
/**
* Sends a downstream message to GCM.
*
*/
#Override
public void sendDownstreamMessage(String message) throws Exception {
Channel channel = channels.peekFirst();
if (channel.connectionDraining) {
synchronized (channels) {
channel = channels.peekFirst();
if (channel.connectionDraining) {
channels.addFirst(connect());
channel = channels.peekFirst();
}
}
}
channel.send(message);
logger.debug("Message Sent via CSS: ({})", message);
}
/**
* Handles an upstream data message from a device application.
*
*/
protected void handleUpstreamMessage(Map<String, Object> jsonObject) {
// PackageName of the application that sent this message.
String category = (String) jsonObject.get("category");
String from = (String) jsonObject.get("from");
#SuppressWarnings("unchecked")
Map<String, String> payload = (Map<String, String>) jsonObject.get("data");
logger.info("Message received from device: category ({}), from ({}), payload: ({})", category, from,
JsonUtil.toJson(payload));
}
/**
* Handles an ACK.
*
* <p>
* Logs a INFO message, but subclasses could override it to properly handle ACKs.
*/
public void handleAckReceipt(Map<String, Object> jsonObject) {
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
logger.debug("handleAckReceipt() from: {}, messageId: {}", from, messageId);
}
/**
* Handles a NACK.
*
* <p>
* Logs a INFO message, but subclasses could override it to properly handle NACKs.
*/
protected void handleNackReceipt(Map<String, Object> jsonObject) {
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
logger.debug("handleNackReceipt() from: {}, messageId: ", from, messageId);
}
/**
* Creates a JSON encoded ACK message for an upstream message received from an application.
*
* #param to
* RegistrationId of the device who sent the upstream message.
* #param messageId
* messageId of the upstream message to be acknowledged to CCS.
* #return JSON encoded ack.
*/
protected static String createJsonAck(String to, String messageId) {
Map<String, Object> message = new HashMap<String, Object>();
message.put("message_type", "ack");
message.put("to", to);
message.put("message_id", messageId);
return JsonUtil.toJson(message);
}
/**
* Connects to GCM Cloud Connection Server using the supplied credentials.
*
* #return
*/
#Override
public Channel connect() {
try {
Channel channel = new Channel();
ConnectionConfiguration config = new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
config.setSecurityMode(SecurityMode.enabled);
config.setReconnectionAllowed(true);
config.setRosterLoadedAtLogin(false);
config.setSendPresence(false);
config.setSocketFactory(SSLSocketFactory.getDefault());
channel.connection = new XMPPTCPConnection(config);
channel.connection.connect();
channel.connection.addConnectionListener(new LoggingConnectionListener());
// Handle incoming packets
channel.connection.addPacketListener(new PacketListener() {
#Override
public void processPacket(Packet packet) {
logger.debug("Received: ({})", packet.toXML());
Message incomingMessage = (Message) packet;
GcmPacketExtension gcmPacket = (GcmPacketExtension) incomingMessage.getExtension(GCM_NAMESPACE);
String json = gcmPacket.getJson();
try {
Map<String, Object> jsonObject = JacksonUtil.DEFAULT.mapper().readValue(json,
new TypeReference<Map<String, Object>>() {});
// present for ack, nack and control, null otherwise
Object messageType = jsonObject.get("message_type");
if (messageType == null) {
// Normal upstream data message
handleUpstreamMessage(jsonObject);
// Send ACK to CCS
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
String ack = createJsonAck(from, messageId);
channel.send(ack);
} else if ("ack".equals(messageType.toString())) {
// Process Ack
handleAckReceipt(jsonObject);
} else if ("nack".equals(messageType.toString())) {
// Process Nack
handleNackReceipt(jsonObject);
} else if ("control".equals(messageType.toString())) {
// Process control message
channel.handleControlMessage(jsonObject);
} else {
logger.error("Unrecognized message type ({})", messageType.toString());
}
} catch (Exception e) {
logger.error("Failed to process packet ({})", packet.toXML(), e);
}
}
}, new PacketTypeFilter(Message.class));
// Log all outgoing packets
channel.connection.addPacketInterceptor(new PacketInterceptor() {
#Override
public void interceptPacket(Packet packet) {
logger.debug("Sent: {}", packet.toXML());
}
}, new PacketTypeFilter(Message.class));
channel.connection.login(ExternalConfig.gcmSenderId() + "#gcm.googleapis.com", ExternalConfig.gcmApiKey());
return channel;
} catch (Exception e) {
logger.error(Logging.FATAL, "Error in creating channel for GCM communication", e);
throw new RuntimeException(e);
}
}
/**
* XMPP Packet Extension for GCM Cloud Connection Server.
*/
private static final class GcmPacketExtension extends DefaultPacketExtension {
private final String json;
public GcmPacketExtension(String json) {
super(GCM_ELEMENT_NAME, GCM_NAMESPACE);
this.json = json;
}
public String getJson() {
return json;
}
#Override
public String toXML() {
return String.format("<%s xmlns=\"%s\">%s</%s>", GCM_ELEMENT_NAME, GCM_NAMESPACE,
StringUtils.escapeForXML(json), GCM_ELEMENT_NAME);
}
public Packet toPacket() {
Message message = new Message();
message.addExtension(this);
return message;
}
}
private static final class LoggingConnectionListener implements ConnectionListener {
#Override
public void connected(XMPPConnection xmppConnection) {
logger.info("Connected.");
}
#Override
public void authenticated(XMPPConnection xmppConnection) {
logger.info("Authenticated.");
}
#Override
public void reconnectionSuccessful() {
logger.info("Reconnecting..");
}
#Override
public void reconnectionFailed(Exception e) {
logger.error("Reconnection failed.. ", e);
}
#Override
public void reconnectingIn(int seconds) {
logger.info("Reconnecting in {} secs", seconds);
}
#Override
public void connectionClosedOnError(Exception e) {
logger.info("Connection closed on error.");
}
#Override
public void connectionClosed() {
logger.info("Connection closed.");
}
}
}
I'm also new in GCM and facing the same problem...I solved it by creating new SmackCcsClient() on CONNECTION_DRAINING message. Older connection should still exists and receiving messages, but not sending because:
protected volatile boolean connectionDraining = true;
Google says that connection will be closed by CCS:
CCS will handle initiating a connection close when it is ready.
Until connection is closed by CCS you will be able to receive messages from both connections, but able to send messages with just new one. When old connection is closed it should be destroyed, I'm not sure if garbage collector is called or not...trying to solve this issue
P.S.: I'm not 100% sure with this answer, but maybe it will open more space for discussion.
I just pushed the code for the FCM Connection Draining to my FCM XMPP Server example.
Project:
XMPP Connection Server for FCM using the latest version of the Smack library (4.2.2) + Connection Draining Implementation.
GitHub link: https://github.com/carlosCharz/fcmxmppserverv2
Youtube link: https://youtu.be/KVKEj6PeLTc
If you had some problems check my troubleshooting section. Hope you can find it useful. Greetings!
I am trying to make third party server so that I can implement CCS server for my chat client. I copied the below code from below android developer link, I included Smack library and JSON library as prescribed, however I am getting compilation error.
CCS Link
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketInterceptor;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.DefaultPacketExtension;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.util.StringUtils;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
import org.xmlpull.v1.XmlPullParser;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLSocketFactory;
Sample implementation:
/**
* Sample Smack implementation of a client for GCM Cloud Connection Server. This
* code can be run as a standalone CCS client.
*
* <p>For illustration purposes only.
*/
public class SmackCcsClient {
private static final Logger logger = Logger.getLogger("SmackCcsClient");
private static final String GCM_SERVER = "gcm.googleapis.com";
private static final int GCM_PORT = 5235;
private static final String GCM_ELEMENT_NAME = "gcm";
private static final String GCM_NAMESPACE = "google:mobile:data";
static {
ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE,
new PacketExtensionProvider() {
#Override
public PacketExtension parseExtension(XmlPullParser parser) throws
Exception {
String json = parser.nextText();
return new GcmPacketExtension(json);
}
});
}
private XMPPConnection connection;
/**
* Indicates whether the connection is in draining state, which means that it
* will not accept any new downstream messages.
*/
protected volatile boolean connectionDraining = false;
/**
* Sends a downstream message to GCM.
*
* #return true if the message has been successfully sent.
*/
public boolean sendDownstreamMessage(String jsonRequest) throws
NotConnectedException {
if (!connectionDraining) {
send(jsonRequest);
return true;
}
logger.info("Dropping downstream message since the connection is draining");
return false;
}
/**
* Returns a random message id to uniquely identify a message.
*
* <p>Note: This is generated by a pseudo random number generator for
* illustration purpose, and is not guaranteed to be unique.
*/
public String nextMessageId() {
return "m-" + UUID.randomUUID().toString();
}
/**
* Sends a packet with contents provided.
*/
protected void send(String jsonRequest) throws NotConnectedException {
Packet request = new GcmPacketExtension(jsonRequest).toPacket();
connection.sendPacket(request);
}
/**
* Handles an upstream data message from a device application.
*
* <p>This sample echo server sends an echo message back to the device.
* Subclasses should override this method to properly process upstream messages.
*/
protected void handleUpstreamMessage(Map<String, Object> jsonObject) {
// PackageName of the application that sent this message.
String category = (String) jsonObject.get("category");
String from = (String) jsonObject.get("from");
#SuppressWarnings("unchecked")
Map<String, String> payload = (Map<String, String>) jsonObject.get("data");
payload.put("ECHO", "Application: " + category);
// Send an ECHO response back
String echo = createJsonMessage(from, nextMessageId(), payload,
"echo:CollapseKey", null, false);
try {
sendDownstreamMessage(echo);
} catch (NotConnectedException e) {
logger.log(Level.WARNING, "Not connected anymore, echo message is not sent", e);
}
}
/**
* Handles an ACK.
*
* <p>Logs a INFO message, but subclasses could override it to
* properly handle ACKs.
*/
protected void handleAckReceipt(Map<String, Object> jsonObject) {
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
logger.log(Level.INFO, "handleAckReceipt() from: " + from + ",messageId: " + messageId);
}
/**
* Handles a NACK.
*
* <p>Logs a INFO message, but subclasses could override it to
* properly handle NACKs.
*/
protected void handleNackReceipt(Map<String, Object> jsonObject) {
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
logger.log(Level.INFO, "handleNackReceipt() from: " + from + ",messageId: " + messageId);
}
protected void handleControlMessage(Map<String, Object> jsonObject) {
logger.log(Level.INFO, "handleControlMessage(): " + jsonObject);
String controlType = (String) jsonObject.get("control_type");
if ("CONNECTION_DRAINING".equals(controlType)) {
connectionDraining = true;
} else {
logger.log(Level.INFO, "Unrecognized control type: %s. This could happen if new features are " + "added to the CCS protocol.",controlType);
}
}
/**
* Creates a JSON encoded GCM message.
*
* #param to RegistrationId of the target device (Required).
* #param messageId Unique messageId for which CCS will send an
* "ack/nack" (Required).
* #param payload Message content intended for the application. (Optional).
* #param collapseKey GCM collapse_key parameter (Optional).
* #param timeToLive GCM time_to_live parameter (Optional).
* #param delayWhileIdle GCM delay_while_idle parameter (Optional).
* #return JSON encoded GCM message.
*/
public static String createJsonMessage(String to, String messageId,
Map<String, String> payload, String collapseKey, Long timeToLive,
Boolean delayWhileIdle) {
Map<String, Object> message = new HashMap<String, Object>();
message.put("to", to);
if (collapseKey != null) {
message.put("collapse_key", collapseKey);
}
if (timeToLive != null) {
message.put("time_to_live", timeToLive);
}
if (delayWhileIdle != null && delayWhileIdle) {
message.put("delay_while_idle", true);
}
message.put("message_id", messageId);
message.put("data", payload);
return JSONValue.toJSONString(message);
}
/**
* Creates a JSON encoded ACK message for an upstream message received
* from an application.
*
* #param to RegistrationId of the device who sent the upstream message.
* #param messageId messageId of the upstream message to be acknowledged to CCS.
* #return JSON encoded ack.
*/
protected static String createJsonAck(String to, String messageId) {
Map<String, Object> message = new HashMap<String, Object>();
message.put("message_type", "ack");
message.put("to", to);
message.put("message_id", messageId);
return JSONValue.toJSONString(message);
}
/**
* Connects to GCM Cloud Connection Server using the supplied credentials.
*
* #param senderId Your GCM project number
* #param apiKey API Key of your project
*/
public void connect(long senderId, String apiKey)
throws XMPPException, IOException, SmackException {
ConnectionConfiguration config =
new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
config.setSecurityMode(SecurityMode.enabled);
config.setReconnectionAllowed(true);
config.setRosterLoadedAtLogin(false);
config.setSendPresence(false);
config.setSocketFactory(SSLSocketFactory.getDefault());
connection = new XMPPTCPConnection(config);
connection.connect();
connection.addConnectionListener(new LoggingConnectionListener());
// Handle incoming packets
connection.addPacketListener(new PacketListener() {
#Override
public void processPacket(Packet packet) {
logger.log(Level.INFO, "Received: " + packet.toXML());
Message incomingMessage = (Message) packet;
GcmPacketExtension gcmPacket =
(GcmPacketExtension) incomingMessage.
getExtension(GCM_NAMESPACE);
String json = gcmPacket.getJson();
try {
#SuppressWarnings("unchecked")
Map<String, Object> jsonObject =
(Map<String, Object>) JSONValue.
parseWithException(json);
// present for "ack"/"nack", null otherwise
Object messageType = jsonObject.get("message_type");
if (messageType == null) {
// Normal upstream data message
handleUpstreamMessage(jsonObject);
// Send ACK to CCS
String messageId = (String) jsonObject.get("message_id");
String from = (String) jsonObject.get("from");
String ack = createJsonAck(from, messageId);
send(ack);
} else if ("ack".equals(messageType.toString())) {
// Process Ack
handleAckReceipt(jsonObject);
} else if ("nack".equals(messageType.toString())) {
// Process Nack
handleNackReceipt(jsonObject);
} else if ("control".equals(messageType.toString())) {
// Process control message
handleControlMessage(jsonObject);
} else {
logger.log(Level.WARNING,
"Unrecognized message type (%s)",
messageType.toString());
}
} catch (ParseException e) {
logger.log(Level.SEVERE, "Error parsing JSON " + json, e);
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to process packet", e);
}
}
}, new PacketTypeFilter(Message.class));
// Log all outgoing packets
connection.addPacketInterceptor(new PacketInterceptor() {
#Override
public void interceptPacket(Packet packet) {
logger.log(Level.INFO, "Sent: {0}", packet.toXML());
}
}, new PacketTypeFilter(Message.class));
connection.login(senderId + "#gcm.googleapis.com", apiKey);
}
public static void main(String[] args) throws Exception {
final long senderId = 1234567890L; // your GCM sender id
final String password = "Your API key";
SmackCcsClient ccsClient = new SmackCcsClient();
ccsClient.connect(senderId, password);
// Send a sample hello downstream message to a device.
String toRegId = "RegistrationIdOfTheTargetDevice";
String messageId = ccsClient.nextMessageId();
Map<String, String> payload = new HashMap<String, String>();
payload.put("Hello", "World");
payload.put("CCS", "Dummy Message");
payload.put("EmbeddedMessageId", messageId);
String collapseKey = "sample";
Long timeToLive = 10000L;
String message = createJsonMessage(toRegId, messageId, payload,
collapseKey, timeToLive, true);
ccsClient.sendDownstreamMessage(message);
}
/**
* XMPP Packet Extension for GCM Cloud Connection Server.
*/
private static final class GcmPacketExtension extends DefaultPacketExtension {
private final String json;
public GcmPacketExtension(String json) {
super(GCM_ELEMENT_NAME, GCM_NAMESPACE);
this.json = json;
}
public String getJson() {
return json;
}
public String toXML() {
return String.format("<%s xmlns=\"%s\">%s</%s>",
GCM_ELEMENT_NAME, GCM_NAMESPACE,
StringUtils.escapeForXML(json), GCM_ELEMENT_NAME);
}
public Packet toPacket() {
Message message = new Message();
message.addExtension(this);
return message;
}
}
private static final class LoggingConnectionListener
implements ConnectionListener {
public void connected(XMPPConnection xmppConnection) {
logger.info("Connected.");
}
public void authenticated(XMPPConnection xmppConnection) {
logger.info("Authenticated.");
}
public void reconnectionSuccessful() {
logger.info("Reconnecting..");
}
public void reconnectionFailed(Exception e) {
logger.log(Level.INFO, "Reconnection failed.. ", e);
}
public void reconnectingIn(int seconds) {
logger.log(Level.INFO, "Reconnecting in %d secs", seconds);
}
public void connectionClosedOnError(Exception e) {
logger.info("Connection closed on error.");
}
public void connectionClosed() {
logger.info("Connection closed.");
}
}
}
Once I try to run it with using the below command
java SmackCcsClient
it gives me the below error stack.
Exception in thread "main" java.lang.NoClassDefFoundError: org/jivesoftware/smack/SmackException$NotConnectedException
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2531)
at java.lang.Class.getMethod0(Class.java:2774)
at java.lang.Class.getMethod(Class.java:1663)
at sun.launcher.LauncherHelper.getMainMethod(LauncherHelper.java:494)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:486)
Caused by: java.lang.ClassNotFoundException: org.jivesoftware.smack.SmackException$NotConnectedException
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
... 6 more