diff options
Diffstat (limited to 'docs/html/google/gcm/ccs.jd')
| -rw-r--r-- | docs/html/google/gcm/ccs.jd | 769 |
1 files changed, 79 insertions, 690 deletions
diff --git a/docs/html/google/gcm/ccs.jd b/docs/html/google/gcm/ccs.jd index 244278e..0cadbd2 100644 --- a/docs/html/google/gcm/ccs.jd +++ b/docs/html/google/gcm/ccs.jd @@ -1,96 +1,93 @@ -page.title=GCM Cloud Connection Server (XMPP) +page.title=GCM Cloud Connection Server @jd:body <div id="qv-wrapper"> <div id="qv"> +<h2>Quickview</h2> + +<ul> +<li>Get an introduction to key CCS terms and concepts.</li> +<li>Learn how to send and receive both upstream and downstream messages in CCS.</li> +</ul> + <h2>In this document</h2> <ol class="toc"> + <li><a href="#gcm">CCS vs. GCM HTTP</a> </li> <li><a href="#usage">How to Use CCS</a> - <ol class="toc"> - <li><a href="#auth">Authentication</a></li> - </ol> - </li> - <li><a href="#format">Message Format</a> - <ol class="toc"> - <li><a href="#request">Request format</a></li> - <li><a href="#response">Response format</a></li> - </ol> - </li> - <li><a href="#upstream">Upstream Messages</a> </li> - <li><a href="#flow">Flow Control</a> </li> - <li><a href="#implement">Implementing an XMPP-based App Server</a> - <ol class="toc"> - <li><a href="#smack">Java sample using the Smack library</a></li> - <li><a href="#python">Python sample</a></li> + <ol> + <li><a href="#send_msg">Sending Messages</a></li> + <li><a href="#format">Message Format</a></li> + <li><a href="#msg_examples">Message Examples</a></li> </ol> </li> + <li><a href="#flow">Flow Control</a> </li> </ol> <h2>See Also</h2> <ol class="toc"> -<li><a href="{@docRoot}google/play-services/gcm/http.html">HTTP</a></li> <li><a href="{@docRoot}google/play-services/gcm/gs.html">Getting Started</a></li> -<li><a href="{@docRoot}google/play-services/gcm/server.html">Implementing GCM Server</a></li> -<li><a href="{@docRoot}google/play-services/gcm/client.html">Implementing GCM Client</a></li> -<li><a href="https://services.google.com/fb/forms/gcm/" class="external-link" -target="_android">CCS and User Notifications Signup Form</a></li> +<li><a href="https://services.google.com/fb/forms/gcm/" class="external-link" target="_android">CCS and User Notifications Signup Form</a></li> </ol> </div> </div> -<p class="note"><strong>Note:</strong> To try out this feature, sign up using -<a href="https://services.google.com/fb/forms/gcm/">this form</a>.</p> +<p class="note"><strong>Note:</strong> To try out this feature, sign up using <a href="https://services.google.com/fb/forms/gcm/">this form</a>.</p> -<p>The GCM Cloud Connection Server (CCS) is a connection server based on XMPP. -CCS allows 3rd-party app servers (which you're -responsible for implementing) to communicate -with Android devices by establishing a persistent TCP connection with Google -servers using the XMPP protocol. This communication is asynchronous and bidirectional.</p> -<p>You can continue to use the HTTP request mechanism to send messages to GCM -servers, side-by-side with CCS which uses XMPP. Some of the benefits of CCS include:</p> +<p>The GCM Cloud Connection Server (CCS) allows third party servers to communicate with Android devices by establishing a persistent TCP connection with Google servers using the XMPP protocol. This communication is asynchronous and bidirectional.</p> +<p>You can continue to use the HTTP request mechanism to send messages to GCM servers, side-by-side with CCS which uses XMPP. Some of the benefits of CCS include:</p> <ul> - <li>The asynchronous nature of XMPP allows you to send more messages with fewer -resources.</li> - <li>Communication is bidirectional—not only can the server send messages -to the device, but the device can send messages back to the server.</li> -<li>You can send messages back using the same connection used for receiving, -thereby improving battery life.</li> + <li>The asynchronous nature of XMPP allows you to send more messages with fewer resources.</li> + <li>Communication is bidirectional—not only can the server send messages to the device, but the device can send messages back to the server.</li> +<li>You can send messages back using the same connection used for receiving, thereby improving battery life.</li> </ul> -<p>The upstream messaging (device-to-cloud) feature of CCS is part of the Google -Play services platform. Upstream messaging is available through the -<a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html"> -{@code GoogleCloudMessaging}</a> -APIs. For examples, see -<a href="#implement">Implementing an XMPP-based App Server</a>.</p> +<p>The upstream messaging (device-to-cloud) feature of CCS is part of the Google Play services platform. Upstream messaging is available through the <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">{@code GoogleCloudMessaging}</a> APIs. To use upstream messaging and the new streamlined registration process, you must <a href="{@docRoot}google/play-services/setup.html">set up</a> the Google Play services SDK.</p> -<p class="note"><strong>Note:</strong> See -<a href="server.html#params">Implementing GCM Server</a> for a list of all the message -parameters and which connection server(s) supports them.</p> +<p class="note"><strong>Note:</strong> For an example of an XMPP server, see <a href="server.html#xmpp">GCM Server</a>. + +<h2 id="gcm">CCS vs. GCM HTTP</h2> + +<p>CCS messaging differs from GCM HTTP messaging in the following ways:</p> +<ul> + <li>Upstream/Downstream messages + <ul> + <li>GCM HTTP: Downstream only: cloud-to-device. </li> + <li>CCS: Upstream and downstream (device-to-cloud, cloud-to-device). </li> + </ul> + </li> + <li>Asynchronous messaging + <ul> + <li>GCM HTTP: 3rd-party servers send messages as HTTP POST requests and wait for a response. This mechanism is synchronous and causes the sender to block before sending another message.</li> + <li>CCS: 3rd-party servers connect to Google infrastructure using a persistent XMPP connection and send/receive messages to/from all their devices at full line speed. CCS sends acknowledgements or failure notifications (in the form of special ACK and NACK JSON-encoded XMPP messages) asynchronously.</li> + </ul> + </li> + <li>JSON + <ul> + <li>GCM HTTP: JSON messages sent as HTTP POST.</li> + <li>CCS: JSON messages encapsulated in XMPP messages.</li> + </ul> + </li> +</ul> +<p>This document describes how to use CCS. For general concepts and information on how to use GCM HTTP, see the <a href="gcm.html">GCM Architectural Overview</a>.</p> <h2 id="usage">How to Use CCS</h2> -<p>GCM Cloud Connection Server (CCS) is an XMPP endpoint, running on -{@code http://gcm.googleapis.com} port 5235.</p> +<p>GCM Cloud Connection Server (CCS) is an XMPP endpoint, running on {@code http://gcm.googleapis.com} port 5235.</p> -<p>CCS requires a Transport Layer Security (TLS) connection. That means the XMPP -client must initiate a TLS connection. -For example in Java, you would call {@code setSocketFactory(SSLSocketFactory)}.</p> +<p>CCS requires a Transport Layer Security (TLS) connection. That means the XMPP client must initiate a TLS connection. +For example in smack, you would call {@code setSocketFactory(SSLSocketFactory)}, similar to “old style SSL” XMPP connections and https.</p> -<p>CCS requires a SASL PLAIN authentication mechanism using -{@code <your_GCM_Sender_Id>@gcm.googleapis.com} (GCM sender ID) and the -API key as the password, where the sender ID and API key are the same as described -in <a href="gs.html">Getting Started</a>.</p> +<p>CCS requires a SASL PLAIN authentication mechanism using {@code <your_GCM_Sender_Id>@gcm.googleapis.com} (GCM sender ID) and the API key as the password, where the sender ID and API key are the same as described in <a href="gs.html">Getting Started</a>.</p> <p> You can use most XMPP libraries to interact with CCS.</p> -<h3 id="auth">Authentication</h3> +<h3 id="send_msg">Sending messages</h3> <p>The following snippets illustrate how to perform authentication in CCS.</p> <h4>Client</h4> @@ -111,13 +108,13 @@ in <a href="gs.html">Getting Started</a>.</p> <h4>Client</h4> <pre><auth mechanism="PLAIN" xmlns="urn:ietf:params:xml:ns:xmpp-sasl">MTI2MjAwMzQ3OTMzQHByb2plY3RzLmdjbS5hb +mRyb2lkLmNvbQAxMjYyMDAzNDc5FzNAcHJvamVjdHMtZ2EtLmFuZHJvaWQuY29tAEFJe mFTeUIzcmNaTmtmbnFLZEZiOW1oekNCaVlwT1JEQTJKV1d0dw==</auth> </pre> - <h4>Server</h4> <pre><success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/></pre> -<h2 id="format">Message Format</h2> +<h3 id="format">Message Format</h3> <p>CCS uses normal XMPP <code><message></code> stanzas. The body of the message must be: </p> <pre> @@ -126,42 +123,25 @@ mFTeUIzcmNaTmtmbnFLZEZiOW1oekNCaVlwT1JEQTJKV1d0dw==</auth> </gcm> </pre> -<p>The JSON payload for server-to-device is similar to what the GCM http endpoint -uses, with these exceptions:</p> +<p>The JSON payload for server-to-device is similar to what the GCM http endpoint uses, with these exceptions:</p> <ul> <li>There is no support for multiple recipients.</li> <li>{@code to} is used instead of {@code registration_ids}.</li> - <li>CCS adds the field {@code message_id}, which is required. This ID uniquely -identifies the message in an XMPP connection. The ACK or NACK from CCS uses the -{@code message_id} to identify a message sent from 3rd-party app servers to CCS. -Therefore, it's important that this {@code message_id} not only be unique, but -always present.</li> + <li>CCS adds the field {@code message_id}, which is required. This ID uniquely identifies the message in an XMPP connection. The ACK or NACK from CCS uses the {@code message_id} to identify a message sent from 3rd-party servers to CCS. Therefore, it's important that this {@code message_id} not only be unique, but always present.</li> -<li>For ACK/NACK messages that are special control messages, you also need to -include a {@code message_type} field in the JSON message. The value can be either -'ack' or 'nack'. For example: + <li>For ACK/NACK messages that are special control messages, you also need to include a {@code message_type} field in the JSON message. For example: -<pre>message_type = ('ack');</pre> +<pre>message_type = ('ack' OR 'nack');</pre> </li> </ul> -<p>For each device message your app server receives from CCS, it needs to send -an ACK message. -It never needs to send a NACK message. If you don't send an ACK for a message, -CCS will just resend it. +<p>For each message a device sends to the server, you need to send an ACK message. You never need to send a NACK message. If you don't send an ACK for a message, CCS will just resend it. </p> -<p>CCS also sends an ACK or NACK for each server-to-device message. If you do not -receive either, it means that the TCP connection was closed in the middle of the -operation and your server needs to resend the messages. See -<a href="#flow">Flow Control</a> for details. +<p>CCS also sends an ACK or NACK for each server-to-device message. If you do not receive either, it means that the TCP connection was closed in the middle of the operation and your server needs to resend the messages. </p> -<p class="note"><strong>Note:</strong> See -<a href="server.html#params">Implementing GCM Server</a> for a list of all the message -parameters and which connection server(s) supports them.</p> +<h3 id="msg_examples">Message Examples</h3> -<h3 id="request">Request format</h3> - -<p>Here is an XMPP stanza containing the JSON message from a 3rd-party app server to CCS: +<p>Here is an XMPP stanza containing the JSON message from a 3rd-party server to CCS: </p> <pre><message id=""> @@ -180,15 +160,7 @@ parameters and which connection server(s) supports them.</p> </message> </pre> -<h3 id="response">Response format</h3> - -<p>A CCS response can have 3 possible forms. The first one is a regular 'ack' -message. But when the response contains an error, there are 2 -different forms the message can take, described below.</p> - -<h4 id="ack">ACK message</h4> - -<p>Here is an XMPP stanza containing the ACK/NACK message from CCS to 3rd-party app server: +<p>Here is an XMPP stanza containing the ACK/NACK message from CCS to 3rd-party server: </p> <pre><message id=""> <gcm xmlns="google:mobile:data"> @@ -199,138 +171,24 @@ different forms the message can take, described below.</p> } </gcm> </message> -</pre> - -<h4 id="nack">NACK message</h4> - -<p>A NACK error is a regular XMPP message in which the {@code message_type} status -message is "nack". A NACK message contains:</p> -<ul> -<li>Nack error code.</li> -<li>Nack error description.</li> -</ul> - -<p>Below are some examples.</p> - -<p>Bad registration:</p> -<pre><message> - <data:gcm xmlns:data="google:mobile:data"> - { - "error":"BAD_REGISTRATION", // error code - "message_id":"msgId1", - "from":"PA91bHFOtaQGSwupt5l1og", - "message_type":"nack" - } - </data:gcm> -</message></pre> - -<p>Invalid "time to live":</p> - -<pre><message> - <data:gcm xmlns:data="google:mobile:data"> - { - "error":"InvalidJson : INVALID_TTL : Invalid value (-1) for \"time_to_live\": must be between 0 and \"2419200\"\n", - "message_id":"msgId1", - "from":"APA91bHFOtaQGSwupt5l1og", - "message_type":"nack" - } - </data:gcm> -</message></pre> -<p>JSON type error:</p> - -<pre><message> - <data:gcm xmlns:data="google:mobile:data"> +<message id=""> + <gcm xmlns="google:mobile:data"> { - "error":"InvalidJson : JSON_TYPE_ERROR : Field \"delay_while_idle\" must be a JSON java.lang.Boolean: not-boolean-user-supplied-value\n", - "message_id":"msgId1", - "from":"APA91bHFOtaQGSwupt5l1og", - "message_type":"nack" + "from":"REGID", + "message_id":"m-1366082849205" + "error": ERROR_CODE, + "message_type":"nack" } - </data:gcm> -</message></pre> - - -<p>The following table lists some of the more common NACK error codes.</p> - -<p class="table-caption" id="table1"> - <strong>Table 1.</strong> NACK error codes.</p> - -<table border="1"> -<tr> -<th>Error Code</th> -<th>Description</th> -</tr> -<tr> -<td>{@code BAD_REGISTRATION}</td> -<td>The device has a registration ID, but it's invalid.</td> -</tr> -<tr> -<td>{@code DEVICE_UNREGISTERED}</td> -<td>The device is not registered.</td> -</tr> -<tr> -<td>{@code INTERNAL_SERVER_ERROR}</td> -<td>The server encountered an error while trying to process the request.</td> -</tr> -<tr> -<td>{@code SERVICE_UNAVAILABLE}</td> -<td>The CCS connection server is temporarily unavailable, try again later -(using exponential backoff, etc.).</td> -</tr> -<tr> -<td>{@code BAD_ACK}</td> -<td>The ACK message is improperly formed.</td> -</tr> -<tr> -<td>{@code AUTHENTICATION_FAILED}</td> -<td>This is a 401 error indicating that there was an error authenticating the sender account.</td> -</tr> -<tr> -<td>{@code INVALID_TTL}</td> -<td>There was an error in the supplied "time to live" value.</td> -</tr> -<tr> -<td>{@code JSON_TYPE_ERROR}</td> -<td>There was an error in the supplied JSON data type.</td> -</tr> -</table> - -<h4 id="stanza">Stanza error</h4> - -<p>You can also get a stanza error in certain cases. -A stanza error contains:</p> -<ul> -<li>Stanza error code.</li> -<li>Stanza error description (free text).</li> -</ul> -<p>For example:</p> - -<pre><message id="3" type="error" to="123456789@gcm.googleapis.com/ABC"> - <gcm xmlns="google:mobile:data"> - {"random": "text"} </gcm> - <error code="400" type="modify"> - <bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/> - <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"> - InvalidJson: JSON_PARSING_ERROR : Missing Required Field: message_id\n - </text> - </error> </message> </pre> +<h4>Upstream Messages</h4> -<h2 id="upstream">Upstream Messages</h2> +<p>Using CCS and the <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">GoogleCloudMessaging</a> API, you can send messages from a user's device to the cloud.</p> -<p>Using CCS and the -<a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html"> -{@code GoogleCloudMessaging}</a> -API, you can send messages from a user's device to the cloud.</p> - -<p>Here is how you send an upstream message using the -<a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html"> -{@code GoogleCloudMessaging}</a> -API. For a complete example, see <a href="client.html">Implementing GCM Client</a>:</p> +<p>Here is how you send an upstream message using the <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">GoogleCloudMessaging</a> API. For a complete example, see <a href="gs.html#gs_example">Getting Started</a>:</p> <pre>GoogleCloudMessaging gcm = GoogleCloudMessaging.get(context); String GCM_SENDER_ID = "Your-Sender-ID"; @@ -340,15 +198,12 @@ Bundle data = new Bundle(); // Bundle data consists of a key-value pair data.putString("hello", "world"); // "time to live" parameter -// This is optional. It specifies a value in seconds up to 4 weeks. int ttl = [0 seconds, 4 weeks] gcm.send(GCM_SENDER_ID + "@gcm.googleapis.com", id, ttl, data); </pre> -<p>This call generates the necessary XMPP stanza for sending the upstream message. -The message goes from the app on the device to CCS to the 3rd-party app server. -The stanza has the following format:</p> +<p>This call generates the necessary XMPP stanza for sending the upstream message. The message goes from the app on the device to CCS to the 3rd-party server. The stanza has the following format:</p> <pre><message id=""> <gcm xmlns="google:mobile:data"> @@ -364,8 +219,7 @@ The stanza has the following format:</p> </gcm> </message></pre> -<p>Here is the format of the ACK expected by CCS from 3rd-party app servers in -response to the above message:</p> +<p>Here is the format of the ACK expected by CCS from 3rd-party servers in response to the above message:</p> <pre><message id=""> <gcm xmlns="google:mobile:data"> @@ -377,478 +231,13 @@ response to the above message:</p> </gcm> </message></pre> -<h2 id="flow">Flow Control</h2> -<p>Every message sent to CCS receives either an ACK or a NACK response. Messages -that haven't received one of these responses are considered pending. If the pending -message count reaches 1000, the 3rd-party app server should stop sending new messages -and wait for CCS to acknowledge some of the existing pending messages as illustrated in -figure 1:</p> +<h2 id="flow">Flow Control</h2> -<img src="{@docRoot}images/gcm/CCS-ack.png"> +<p>Every message sent to CCS receives either an ACK or a NACK response. Messages that haven't received one of these responses are considered pending. If the pending message count reaches 1000, the 3rd-party server should stop sending new messages and wait for CCS to acknowledge some of the existing pending messages.</p> -<p class="img-caption"> - <strong>Figure 1.</strong> Message/ack flow. -</p> +<p>Conversely, to avoid overloading the 3rd-party server, CCS will stop sending if there are too many unacknowledged messages. Therefore, the 3rd-party server should "ACK" received messages as soon as possible to maintain a constant flow of incoming messages. The aforementioned pending message limit doesn't apply to these ACKs. Even if the pending message count reaches 1000, the 3rd-party server should continue sending ACKs to avoid blocking delivery of new messages.</p> -<p>Conversely, to avoid overloading the 3rd-party app server, CCS will stop sending -if there are too many unacknowledged messages. Therefore, the 3rd-party app server -should "ACK" upstream messages, received from the client application via CCS, as soon as possible -to maintain a constant flow of incoming messages. The aforementioned pending message limit doesn't -apply to these ACKs. Even if the pending message count reaches 1000, the 3rd-party app server -should continue sending ACKs for messages received from CCS to avoid blocking delivery of new -upstream messages.</p> - -<p>ACKs are only valid within the context of one connection. If the connection is -closed before a message can be ACKed, the 3rd-party app server should wait for CCS -to resend the upstream message before ACKing it again. Similarly, all pending messages for which an -ACK/NACK was not received from CCS before the connection was closed should be sent again. +<p>ACKs are only valid within the context of one connection. If the connection is closed before a message can be ACKed, the 3rd-party server should wait for CCS to resend the message before ACKing it again. </p> -<h2 id="implement">Implementing an XMPP-based App Server</h2> - -<p>This section gives examples of implementing an app server that works with CCS. -Note that a full GCM implementation requires a client-side implementation, in -addition to the server. For more information, see <a href="client.html"> -Implementing GCM Client</a>.</a> - -<h3 id="smack">Java sample using the Smack library</h3> - -<p>Here is a sample app server written in Java, using the -<a href="http://www.igniterealtime.org/projects/smack/">Smack</a> library.</p> - -<pre>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.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.util.StringUtils; -import org.json.simple.JSONValue; -import org.json.simple.parser.ParseException; -import org.xmlpull.v1.XmlPullParser; - -import java.util.HashMap; -import java.util.Map; -import java.util.Random; -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. - * - * <p>For illustration purposes only. - */ -public class SmackCcsClient { - - 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; - - 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, json, GCM_ELEMENT_NAME); - } - - @SuppressWarnings("unused") - public Packet toPacket() { - return new Message() { - // Must override toXML() because it includes a <body> - @Override - public String toXML() { - - StringBuilder buf = new StringBuilder(); - buf.append("<message"); - if (getXmlns() != null) { - buf.append(" xmlns=\"").append(getXmlns()).append("\""); - } - if (getLanguage() != null) { - buf.append(" xml:lang=\"").append(getLanguage()).append("\""); - } - if (getPacketID() != null) { - buf.append(" id=\"").append(getPacketID()).append("\""); - } - if (getTo() != null) { - buf.append(" to=\"").append(StringUtils.escapeForXML(getTo())).append("\""); - } - if (getFrom() != null) { - buf.append(" from=\"").append(StringUtils.escapeForXML(getFrom())).append("\""); - } - buf.append(">"); - buf.append(GcmPacketExtension.this.toXML()); - buf.append("</message>"); - return buf.toString(); - } - }; - } - } - - public SmackCcsClient() { - // Add GcmPacketExtension - ProviderManager.getInstance().addExtensionProvider(GCM_ELEMENT_NAME, - GCM_NAMESPACE, new PacketExtensionProvider() { - - @Override - public PacketExtension parseExtension(XmlPullParser parser) - throws Exception { - String json = parser.nextText(); - GcmPacketExtension packet = new GcmPacketExtension(json); - return packet; - } - }); - } - - /** - * 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 getRandomMessageId() { - return "m-" + Long.toString(random.nextLong()); - } - - /** - * Sends a downstream GCM message. - */ - public void send(String jsonRequest) { - 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 process an upstream message. - */ - public void handleIncomingDataMessage(Map<String, Object> jsonObject) { - String from = jsonObject.get("from").toString(); - - // PackageName of the application that sent this message. - String category = jsonObject.get("category").toString(); - - // Use the packageName as the collapseKey in the echo packet - String collapseKey = "echo:CollapseKey"; - @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, getRandomMessageId(), payload, collapseKey, null, false); - send(echo); - } - - /** - * Handles an ACK. - * - * <p>By default, it only logs a {@code INFO} message, but subclasses could override it to - * properly handle ACKS. - */ - public void handleAckReceipt(Map<String, Object> jsonObject) { - String messageId = jsonObject.get("message_id").toString(); - String from = jsonObject.get("from").toString(); - logger.log(Level.INFO, "handleAckReceipt() from: " + from + ", messageId: " + messageId); - } - - /** - * Handles a NACK. - * - * <p>By default, it only logs a {@code INFO} message, but subclasses could override it to - * properly handle NACKS. - */ - public void handleNackReceipt(Map<String, Object> jsonObject) { - String messageId = jsonObject.get("message_id").toString(); - String from = jsonObject.get("from").toString(); - logger.log(Level.INFO, "handleNackReceipt() from: " + from + ", messageId: " + messageId); - } - - /** - * 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. - */ - public 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 username GCM_SENDER_ID@gcm.googleapis.com - * @param password API Key - * @throws XMPPException - */ - public void connect(String username, String password) throws XMPPException { - config = new ConnectionConfiguration(GCM_SERVER, GCM_PORT); - config.setSecurityMode(SecurityMode.enabled); - config.setReconnectionAllowed(true); - config.setRosterLoadedAtLogin(false); - config.setSendPresence(false); - config.setSocketFactory(SSLSocketFactory.getDefault()); - - // NOTE: Set to true to launch a window with information about packets sent and received - config.setDebuggerEnabled(true); - - // -Dsmack.debugEnabled=true - XMPPConnection.DEBUG_ENABLED = true; - - connection = new XMPPConnection(config); - connection.connect(); - - connection.addConnectionListener(new ConnectionListener() { - - @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.log(Level.INFO, "Connection closed on error."); - } - - @Override - public void connectionClosed() { - logger.info("Connection closed."); - } - }); - - // 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 - handleIncomingDataMessage(jsonObject); - - // Send ACK to CCS - String messageId = jsonObject.get("message_id").toString(); - String from = jsonObject.get("from").toString(); - 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 { - 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, "Couldn't send echo.", 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(username, password); - } - - public static void main(String [] args) { - final String userName = "Your GCM Sender Id" + "@gcm.googleapis.com"; - final String password = "API Key"; - - SmackCcsClient ccsClient = new SmackCcsClient(); - - try { - ccsClient.connect(userName, password); - } catch (XMPPException e) { - e.printStackTrace(); - } - - // Send a sample hello downstream message to a device. - String toRegId = "RegistrationIdOfTheTargetDevice"; - String messageId = ccsClient.getRandomMessageId(); - 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; - Boolean delayWhileIdle = true; - ccsClient.send(createJsonMessage(toRegId, messageId, payload, collapseKey, - timeToLive, delayWhileIdle)); - } -}</pre> -<h3 id="python">Python sample</h3> - -<p>Here is an example of a CCS app server written in Python. This sample echo -server sends an initial message, and for every upstream message received, it sends -a dummy response back to the application that sent the upstream message. This -example illustrates how to connect, send, and receive GCM messages using XMPP. It -shouldn't be used as-is on a production deployment.</p> - -<pre> -#!/usr/bin/python -import sys, json, xmpp, random, string - -SERVER = 'gcm.googleapis.com' -PORT = 5235 -USERNAME = "Your GCM Sender Id" -PASSWORD = "API Key" -REGISTRATION_ID = "Registration Id of the target device" - -unacked_messages_quota = 1000 -send_queue = [] - -# Return a random alphanumerical id -def random_id(): - rid = '' - for x in range(8): rid += random.choice(string.ascii_letters + string.digits) - return rid - -def message_callback(session, message): - global unacked_messages_quota - gcm = message.getTags('gcm') - if gcm: - gcm_json = gcm[0].getData() - msg = json.loads(gcm_json) - if not msg.has_key('message_type'): - # Acknowledge the incoming message immediately. - send({'to': msg['from'], - 'message_type': 'ack', - 'message_id': msg['message_id']}) - # Queue a response back to the server. - if msg.has_key('from'): - # Send a dummy echo response back to the app that sent the upstream message. - send_queue.append({'to': msg['from'], - 'message_id': random_id(), - 'data': {'pong': 1}}) - elif msg['message_type'] == 'ack' or msg['message_type'] == 'nack': - unacked_messages_quota += 1 - -def send(json_dict): - template = ("<message><gcm xmlns='google:mobile:data'>{1}</gcm></message>") - client.send(xmpp.protocol.Message( - node=template.format(client.Bind.bound[0], json.dumps(json_dict)))) - -def flush_queued_messages(): - global unacked_messages_quota - while len(send_queue) and unacked_messages_quota > 0: - send(send_queue.pop(0)) - unacked_messages_quota -= 1 - -client = xmpp.Client('gcm.googleapis.com', debug=['socket']) -client.connect(server=(SERVER,PORT), secure=1, use_srv=False) -auth = client.auth(USERNAME, PASSWORD) -if not auth: - print 'Authentication failed!' - sys.exit(1) - -client.RegisterHandler('message', message_callback) - -send_queue.append({'to': REGISTRATION_ID, - 'message_id': 'reg_id', - 'data': {'message_destination': 'RegId', - 'message_id': random_id()}}) - -while True: - client.Process(1) - flush_queued_messages()</pre> |
