/*

* Most part of this class is copyright Google.
* It is from https://developer.android.com/google/gcm/ccs.html
*/
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

org.jivesoftware.smack.ConnectionConfiguration;
org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
org.jivesoftware.smack.ConnectionListener;
org.jivesoftware.smack.PacketInterceptor;
org.jivesoftware.smack.PacketListener;
org.jivesoftware.smack.XMPPConnection;
org.jivesoftware.smack.XMPPException;
org.jivesoftware.smack.filter.PacketTypeFilter;
org.jivesoftware.smack.packet.DefaultPacketExtension;
org.jivesoftware.smack.packet.Message;
org.jivesoftware.smack.packet.Packet;
org.jivesoftware.smack.packet.PacketExtension;
org.jivesoftware.smack.provider.PacketExtensionProvider;
org.jivesoftware.smack.provider.ProviderManager;
org.jivesoftware.smack.util.StringUtils;
org.json.simple.JSONValue;
org.json.simple.parser.ParseException;
org.xmlpull.v1.XmlPullParser;

import
import
import
import
import
import
import

java.util.HashMap;
java.util.Map;
java.util.Random;
java.util.logging.Level;
java.util.logging.Logger;
java.io.*;
javax.net.ssl.SSLSocketFactory;

/**
* Sample Smack implementation of a client for GCM Cloud Connection Server.
*
* For illustration purposes only.
*/
public class SmackCcsClient {
static final String REG_ID_STORE = "gcmchat.txt";
static final String MESSAGE_KEY = "SM";
Logger logger = Logger.getLogger("SmackCcsClient");
public static final String GCM_SERVER = "gcm.googleapis.com";
public static final int GCM_PORT = 5235;
public static final String GCM_ELEMENT_NAME = "gcm";
public static final String GCM_NAMESPACE = "google:mobile:data";
static Random random = new Random();
XMPPConnection connection;
ConnectionConfiguration config;
/**
* XMPP Packet Extension for GCM Cloud Connection Server.
*/
class GcmPacketExtension extends DefaultPacketExtension {
String json;

append(" to=###BOT_TEXT###quot;") . GCM_NAMESPACE). GCM_ELE MENT_NAME.append(" id=###BOT_TEXT###quot;").append("###BOT_TEXT###quot;"). } public String getJson() { return json. GCM_ELEMENT_NAME). buf. } if (getLanguage() != null) { buf.toString(). } @Override public String toXML() { return String.append(" from=###BOT_TEXT###quot;") . if (getXmlns() != null) { buf. } buf.public GcmPacketExtension(String json) { super(GCM_ELEMENT_NAME.appen d(getLanguage()) . buf.json = json. json.append(" xmlns=###BOT_TEXT###quot;"). buf. } if (getTo() != null) { buf. } }.append("###BOT_TEXT###quot;"). return buf.append(StringUt ils. } if (getFrom() != null) { buf.append(" xml:lang=###BOT_TEXT###quot;").append("###BOT_TEXT###quot;").this.append(GcmPacketExtension.append("###BOT_TEXT###quot;").append("<message").escapeForXML(getFrom())) . this.append(getP acketID()). GCM_NAMESPACE.append("###BOT_TEXT###quot;").append(">").toXML ()).escapeForXML(getTo())) .append(g etXmlns()). } } . } if (getPacketID() != null) { buf.append(StringUt ils.append("</message>"). } @SuppressWarnings("unused") public Packet toPacket() { return new Message() { // Must override toXML() because it includes a < body> @Override public String toXML() { StringBuilder buf = new StringBuilder().format("<%s xmlns=###BOT_TEXT###quot;%s###BOT_TEXT###quot;>%s</%s>".

sendPacket(request). . */ public void send(String jsonRequest) { Packet request = new GcmPacketExtension(jsonRequest). } /** * Returns a random message id to uniquely identify a message. * Subclasses should override this method to process an upstream message .nextLong()).toString(). } }). * */ public String getRandomMessageId() { return "m-" + Long. and is not guaranteed to be unique.toPacket(). */ public void handleIncomingDataMessage(Map<String.get("from"). * * <p> * This sample echo server sends an echo message back to the device. connection. GcmPacketExtension packet = new GcmPacketExtension(json). Object> jsonObject) { String from = jsonObject.getInstance(). } /** * Handles an upstream data message from a device application.get("category"). String category = jsonObject. return packet.public SmackCcsClient() { // Add GcmPacketExtension ProviderManager. GCM_NAMESPACE. * * <p> * Note: This is generated by a pseudo random number generator for * illustration purpose. new PacketExtensionProvider() { @Override public PacketExtension parseExtension(Xm lPullParser parser) throws Exception { String json = parser.toString().addExtensionProvider(GCM_ELEMENT_N AME.nextText(). } /** * Sends a downstream GCM message.get("data"). // PackageName of the application that sent this message.toString(random. @SuppressWarnings("unchecked") Map<String. // Use the packageName as the collapseKey in the echo packet String collapseKey = "echo:CollapseKey". String> payload = (Map<String. String>) jsonObject .

get("CLIENT_MESSAGE"). getRando mMessageId(). String toUser = payload.equals(action)) { String clientMessage = payload.put(MESSAGE_KEY. } catch (IOException e) { e. } payload. collapseKey.getKey() + ":". String message = createJsonMessage(from.printStackTrace(). collapseKey. but subclasses could overrid e it * to properly handle ACKS.get("message_id").get("TOUSER"). "USERLIST"). writeToFile(userName.toString(). payload. payload. payload. String> regIdMap = readFromFile(). Object> jsonObject) { String messageId = jsonObject.put(MESSAGE_KEY. // Send an ECHO response back String echo = createJsonMessage(from. false). payload.toString(). */ public void handleAckReceipt(Map<String.equals(action)) { try { String userName = payload. String toUserRegid = regIdMap.Entry<String. payload.String action = payload. * * <p> * By default. } } /** * Handles an ACK.equals(action)) { Map<String.put(MESSAGE_KEY. from). String users = "". false).put("USERLIST". . collapseKey. null. } else if ("SIGNUP".users). for (Map.entrySet ()) { users = users + entry. } else if ("CHAT". String from = jsonObject. String message = createJsonMessage(toUserRegid.get("USER_NAME"). if ("ECHO".get("from").equals(action)) { Map<String. send(message). null. getRandomMessag eId(). "CHAT"). it only logs a INFO message. String> regIdMap = readFromFile(). String> entry : regIdMap. false). payload.get(toUser). send(echo).get("ACTION"). getRandomMessageId (). null. } } else if ("USERLIST". "ECHO: " + clientMessage). send(message).

get("message_id"). messageId: " + messageId). * @param timeToLive * GCM time_to_live parameter (Optional). * @param collapseKey * GCM collapse_key parameter (Optional). Object> jsonObject) { String messageId = jsonObject. String> payload.put("message_id".log(Level. * @param delayWhileIdle * GCM delay_while_idle parameter (Optional). it only logs a INFO message.put("time_to_live". message. messageId). Object> message = new HashMap<String. * * @param to * RegistrationId of the target device (Required). return JSONValue. "handleNackReceipt() from: " + from + ". } /** * Creates a JSON encoded ACK message for an upstream message received f rom .toJSONString(message). timeToLive). but subclasses could overrid e it * to properly handle NACKS. */ public void handleNackReceipt(Map<String. } if (timeToLive != null) { message.logger. } message.toString(). } if (delayWhileIdle != null && delayWhileIdle) { message. * @param messageId * Unique messageId for which CCS will send an "ack/nack" * (Required).put("data".INFO. String messageId. logger. String from = jsonObject. (Optional). String collapseKey. Map<String.put("delay_while_idle". * @param payload * Message content intended for the application. messageId: " + messageId). message.put("to".get("from"). Object>().toString(). Boolean delayWhileIdle) { Map<String. } /** * Handles a NACK. * @return JSON encoded GCM message. } /** * Creates a JSON encoded GCM message.log(Level. if (collapseKey != null) { message. Long ti meToLive.put("collapse_key". * * <p> * By default. true). "handleAckReceipt() from: " + from + ".INFO. payload). */ public static String createJsonMessage(String to. collapseKey). to).

String password) throws XMPPExcepti on { config = new ConnectionConfiguration(GCM_SERVER.googleapis. Object> message = new HashMap<String.info("Reconnecting.DEBUG_ENABLED = true..setDebuggerEnabled(true).addConnectionListener(new ConnectionListener() { @Override public void reconnectionSuccessful() { logger. message. config.* an application. * @param messageId * messageId of the upstream message to be acknowledged to CC S. } . connection.setReconnectionAllowed(true). return JSONValue. e). messageId). connection = new XMPPConnection(config). */ public static String createJsonAck(String to.getDefault()). ".").com * @param password * API Key * @throws XMPPException */ public void connect(String username. config. } @Override public void reconnectionFailed(Exception e) { logger.debugEnabled=true XMPPConnection.setSecurityMode(SecurityMode. "ack"). GCM_PORT).connect().setRosterLoadedAtLogin(false). "Reconnection failed. connection. } /** * Connects to GCM Cloud Connection Server using the supplied credential s.log(Level. config. String messageId) { Map<String. Object>().toJSONString(message). to). * * @param username * GCM_SENDER_ID@gcm. * @return JSON encoded ack. message.put("message_type".INFO.put("to".setSocketFactory(SSLSocketFactory. config.setSendPresence(false). // NOTE: Set to true to launch a window with information about p ackets // sent and received config. // -Dsmack..enabled). * * @param to * RegistrationId of the device who sent the upstream message . message. config.put("message_id".

").toString().getJson(). messageId). Object>) JSONValue . // Handle incoming packets connection. // present for "ack"/"nack".parseWithException(json ). "Received: " + packet.toSt ring())) { // Process Ack .getExtension(GCM_NAMESPACE).ge t("message_id") . "Reconnecting in %d secs" . String from = jsonObject. try { @SuppressWarnings("unchecked") Map<String. // Send ACK to CCS String messageId = jsonObject.log(Level. send(ack).log(Level. "Connection closed on err or.log(Level.toX ML()).addPacketListener(new PacketListener() { @Override public void processPacket(Packet packet) { logger. Message incomingMessage = (Message) packet. Object> jsonObject = (Map<St ring."). String ack = createJsonAck(from.INFO. } @Override public void connectionClosedOnError(Exception e) { logger. if (messageType == null) { // Normal upstream data message handleIncomingDataMessage(jsonOb ject). GcmPacketExtension gcmPacket = (GcmPacketExtensi on) incomingMessage . seconds).info("Connection closed. } }).equals(messageType. } else if ("ack".toString(). null otherw ise Object messageType = jsonObject.INFO.INFO. } @Override public void connectionClosed() { logger. String json = gcmPacket.get("mes sage_type").@Override public void reconnectingIn(int seconds) { logger.get("fr om").

close().log(Level. } } }.class)).").log(Level. for (Map. } else { logger.addPacketInterceptor(new PacketInterceptor() { @Override public void interceptPacket(Packet packet) { logger.println(name + ". password). String> entry : regIdMap. connection. } br. String>(). } out.class)).toS tring())) { // Process Nack handleNackReceipt(jsonObject).". String> readFromFile() { Map<String. new PacketTypeFilter(Message. regIdMap.split(". } catch (Exception e) { logger.log(Level. "Sent: {0}". out.close(). try { BufferedReader br = new BufferedReader(new FileReader(REG_ID_STO RE)). String regIdLine = "". } }.put(regArr[0].WARNING. regId). new PacketTypeFilter(Message. PrintWriter out = new PrintWriter(new BufferedWriter(new FileWri ter( REG_ID_STORE. e). } else if ("nack".toSt ring()).handleAckReceipt(jsonObject).log(Level. regIdMap = new HashMap<String." + regId). packet. while ((regIdLine = br. "Unrecognized me ssage type (%s)". String regId) throws IOException { Map<String. } } catch (ParseException e) { logger.println(entry. false))).getKey() + ".entrySet()) { out." + entry. String> regIdMap = readFromFile(). String> regIdMap = null.getValue()).put(name.toXML ()).equals(messageType.SEVERE. regArr[1]). // Log all outgoing packets connection.INFO.login(username. messageType. } public void writeToFile(String name. .readLine()) != null) { String[] regArr = regIdLine.Entry<String. e).SEVERE. regIdMap. "Couldn't send echo. "Error parsing JSON " + json. } public Map<String.

try { ccsClient.googleapis. password).printStackTrace(). SmackCcsClient ccsClient = new SmackCcsClient(). } catch (XMPPException e) { e. } } } . } public static void main(String [] args) { final String userName = "512218038480" + "@gcm.connect(userName.} catch(IOException ioe) { } return regIdMap.com". final String password = "AIzaSyA9DQTcggUtfqOG9lnV_Xb5VEQ8iKBEaP4".