As part of Google Cloud Messaging (GCM), Google provides an XMPP endpoint and that is called the Google Cloud Connection Server (CCS). It provides support for persistent asynchronous and most importantly bidirectional connection. When we use HTTP server, we can send only downstream messages to Android devices using Google GCM server. With Google CCS, we can send upstream message from an Android device to server and subsequently to another Android device.
In the Android device, the same connection that is used to receive the message is reused while sending an upstream message and this is an advantage in terms of resource usage. This is the best choice for implementing an Android chat application. I will develop a sample Android chat application and post it in the next tutorial. In this tutorial, we will see about how a device can send message to a XMMP server application via the GCM CCS and vice versa.
If you are new to Google Cloud Messaging, I recommend you to go through the introductory tutorial which discusses about notifications in Android using Google cloud messaging. This linked introductory tutorial explains the Google GCM concept with an example and it covers the prerequisite for using the Google Cloud infrastructure. You will need the same prerequisite for running the example given in this tutorial.
For 1 and 2, please refer the notifications in Android using Google cloud messaging tutorial.
For Google Approval you need to sign up using this form: https://services.google.com/fb/forms/gcm/
It took three weeks for me to get approval and it may vary depending on the request queue.
Server part is completely different than in the previous examples. We need to provide and implementation for XMPP server. Doing that from ground up is difficult and so let us use the smack library. Google also uses the same library in its examples.
For this implementation I have borrowed the Google sample code and made some customizations to it.
This example handles three cases.
/* * Most part of this class is copyright Google. * It is from https://developer.android.com/google/gcm/ccs.html */ package com.javapapers.java.gcm; 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.io.IOException; 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. * * For illustration purposes only. */ public class SmackCcsClient { static final String MESSAGE_KEY = "SERVER_MESSAGE"; 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 @Override public String toXML() { StringBuilder buf = new StringBuilder(); buf.append(""); buf.append(GcmPacketExtension.this.toXML()); buf.append(" "); 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. * * 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. * * 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(MapjsonObject) { 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 payload = (Map ) jsonObject .get("data"); String action = payload.get("ACTION"); if ("ECHO".equals(action)) { String clientMessage = payload.get("CLIENT_MESSAGE"); payload.put(MESSAGE_KEY, "ECHO: " + clientMessage); // Send an ECHO response back String echo = createJsonMessage(from, getRandomMessageId(), payload, collapseKey, null, false); send(echo); } else if ("REGISTER".equals(action)) { try { RegIdManager.writeToFile(from); } catch (IOException e) { e.printStackTrace(); } } } /** * Handles an ACK. * * By default, it only logs a INFO message, but subclasses could override it * to properly handle ACKS. */ public void handleAckReceipt(Map 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. * * By default, it only logs a INFO message, but subclasses could override it * to properly handle NACKS. */ public void handleNackReceipt(Map 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 payload, String collapseKey, Long timeToLive, Boolean delayWhileIdle) { Map message = new HashMap (); 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 message = new HashMap (); 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 jsonObject = (Map ) 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 sendMessage(String userName, final String GOOGLE_SERVER_KEY, String toDeviceRegId, String message) { SmackCcsClient ccsClient = new SmackCcsClient(); try { ccsClient.connect(userName, GOOGLE_SERVER_KEY); } catch (XMPPException e) { e.printStackTrace(); } String messageId = ccsClient.getRandomMessageId(); Map payload = new HashMap (); payload.put(MESSAGE_KEY, message); payload.put("EmbeddedMessageId", messageId); String collapseKey = "sample"; Long timeToLive = 10000L; Boolean delayWhileIdle = true; ccsClient.send(createJsonMessage(toDeviceRegId, messageId, payload, collapseKey, timeToLive, delayWhileIdle)); } }
package com.javapapers.java.gcm; import java.io.IOException; import java.util.Set; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/GCMNotification") public class GCMNotification extends HttpServlet { private static final long serialVersionUID = 1L; // Put your Google API Server Key here private static final String GOOGLE_SERVER_KEY = "AIzaSyA9DQTcggUtABVC9lnV_Xb5VEQ8iKBEaP4"; // Put your Google Project number here final String GOOGLE_USERNAME = "512212818580" + "@gcm.googleapis.com"; public GCMNotification() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { String userMessage = request.getParameter("message"); SetregIdSet = RegIdManager.readFromFile(); String toDeviceRegId = (String) (regIdSet.toArray())[0]; SmackCcsClient.sendMessage(GOOGLE_USERNAME, GOOGLE_SERVER_KEY, toDeviceRegId,userMessage); request.setAttribute("pushStatus", "Message Sent."); } catch (IOException ioe) { ioe.printStackTrace(); request.setAttribute("pushStatus", "RegId required: " + ioe.toString()); } catch (Exception e) { e.printStackTrace(); request.setAttribute("pushStatus", e.toString()); } request.getRequestDispatcher("index.jsp").forward(request, response); } }
package com.javapapers.java.gcm; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.HashSet; import java.util.Set; public class RegIdManager { static final String REG_ID_STORE = "GCMRegId.txt"; public static void writeToFile(String regId) throws IOException { SetregIdSet = readFromFile(); if (!regIdSet.contains(regId)) { PrintWriter out = new PrintWriter(new BufferedWriter( new FileWriter(REG_ID_STORE, true))); out.println(regId); out.close(); } } public static Set readFromFile() throws IOException { BufferedReader br = new BufferedReader(new FileReader(REG_ID_STORE)); String regId = ""; Set regIdSet = new HashSet (); while ((regId = br.readLine()) != null) { regIdSet.add(regId); } br.close(); return regIdSet; } }
The Android Google GCM Client application is no different from previous HTTP based clients. I have made some minor adjustments in order to incorporate the example communication flow.
package com.javapapers.android.gcm.multiple; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.os.AsyncTask; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.google.android.gms.gcm.GoogleCloudMessaging; public class RegisterActivity extends Activity { Button btnGCMRegister; Button btnXmppRegiser; Button btnSendMessage; GoogleCloudMessaging gcm; Context context; String regId; AsyncTasksendTask; AtomicInteger ccsMsgId = new AtomicInteger(); static final String GOOGLE_PROJECT_ID = "512212818580"; public static final String REG_ID = "regId"; private static final String APP_VERSION = "appVersion"; static final String TAG = "Register Activity"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_register); context = getApplicationContext(); btnGCMRegister = (Button) findViewById(R.id.btnGCMRegister); btnGCMRegister.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { if (TextUtils.isEmpty(regId)) { regId = registerGCM(); Log.d("RegisterActivity", "GCM RegId: " + regId); } else { Toast.makeText(getApplicationContext(), "Already Registered with GCM Server!", Toast.LENGTH_LONG).show(); } } }); btnXmppRegiser = (Button) findViewById(R.id.btnXmppRegiser); btnXmppRegiser.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { if (TextUtils.isEmpty(regId)) { Toast.makeText(getApplicationContext(), "RegId is empty!", Toast.LENGTH_LONG).show(); } else { sendMessage("REGISTER"); } } }); btnSendMessage = (Button) findViewById(R.id.btnSendMessage); btnSendMessage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { if (TextUtils.isEmpty(regId)) { Toast.makeText(getApplicationContext(), "RegId is empty!", Toast.LENGTH_LONG).show(); } else { sendMessage("ECHO"); } } }); } private void sendMessage(final String action) { sendTask = new AsyncTask () { @Override protected String doInBackground(Void... params) { Bundle data = new Bundle(); data.putString("ACTION", action); data.putString("CLIENT_MESSAGE", "Hello GCM CCS XMPP!"); String id = Integer.toString(ccsMsgId.incrementAndGet()); try { Log.d("RegisterActivity", "messageid: " + id); gcm.send(GOOGLE_PROJECT_ID + "@gcm.googleapis.com", id, data); Log.d("RegisterActivity", "After gcm.send successful."); } catch (IOException e) { Log.d("RegisterActivity", "Exception: " + e); e.printStackTrace(); } return "Sent message."; } @Override protected void onPostExecute(String result) { sendTask = null; Toast.makeText(getApplicationContext(), result, Toast.LENGTH_LONG).show(); } }; sendTask.execute(null, null, null); } public String registerGCM() { gcm = GoogleCloudMessaging.getInstance(this); regId = getRegistrationId(context); if (TextUtils.isEmpty(regId)) { registerInBackground(); Log.d("RegisterActivity", "registerGCM - successfully registered with GCM server - regId: " + regId); } else { Toast.makeText(getApplicationContext(), "RegId already available. RegId: " + regId, Toast.LENGTH_LONG).show(); } return regId; } private String getRegistrationId(Context context) { final SharedPreferences prefs = getSharedPreferences( RegisterActivity.class.getSimpleName(), Context.MODE_PRIVATE); String registrationId = prefs.getString(REG_ID, ""); if (registrationId.isEmpty()) { Log.i(TAG, "Registration not found."); return ""; } int registeredVersion = prefs.getInt(APP_VERSION, Integer.MIN_VALUE); int currentVersion = getAppVersion(context); if (registeredVersion != currentVersion) { Log.i(TAG, "App version changed."); return ""; } return registrationId; } private static int getAppVersion(Context context) { try { PackageInfo packageInfo = context.getPackageManager() .getPackageInfo(context.getPackageName(), 0); return packageInfo.versionCode; } catch (NameNotFoundException e) { Log.d("RegisterActivity", "I never expected this! Going down, going down!" + e); throw new RuntimeException(e); } } private void registerInBackground() { new AsyncTask () { @Override protected String doInBackground(Void... params) { String msg = ""; try { if (gcm == null) { gcm = GoogleCloudMessaging.getInstance(context); } regId = gcm.register(GOOGLE_PROJECT_ID); Log.d("RegisterActivity", "registerInBackground - regId: " + regId); msg = "Device registered, registration ID=" + regId; storeRegistrationId(context, regId); } catch (IOException ex) { msg = "Error :" + ex.getMessage(); Log.d("RegisterActivity", "Error: " + msg); } Log.d("RegisterActivity", "AsyncTask completed: " + msg); return msg; } @Override protected void onPostExecute(String msg) { Toast.makeText(getApplicationContext(), "Registered with GCM Server." + msg, Toast.LENGTH_LONG) .show(); } }.execute(null, null, null); } private void storeRegistrationId(Context context, String regId) { final SharedPreferences prefs = getSharedPreferences( RegisterActivity.class.getSimpleName(), Context.MODE_PRIVATE); int appVersion = getAppVersion(context); Log.i(TAG, "Saving regId on app version " + appVersion); SharedPreferences.Editor editor = prefs.edit(); editor.putString(REG_ID, regId); editor.putInt(APP_VERSION, appVersion); editor.commit(); } }
package com.javapapers.android.gcm.multiple; import android.app.IntentService; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.NotificationCompat; import android.util.Log; import com.google.android.gms.gcm.GoogleCloudMessaging; public class GCMNotificationIntentService extends IntentService { public static final int NOTIFICATION_ID = 1; private NotificationManager mNotificationManager; NotificationCompat.Builder builder; public GCMNotificationIntentService() { super("GcmIntentService"); } public static final String TAG = "GCMNotificationIntentService"; @Override protected void onHandleIntent(Intent intent) { Bundle extras = intent.getExtras(); GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this); String messageType = gcm.getMessageType(intent); if (!extras.isEmpty()) { if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR .equals(messageType)) { sendNotification("Send error: " + extras.toString()); } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED .equals(messageType)) { sendNotification("Deleted messages on server: " + extras.toString()); } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE .equals(messageType)) { sendNotification("SERVER_MESSAGE: " + extras.get("SERVER_MESSAGE")); Log.i(TAG, "SERVER_MESSAGE: " + extras.toString()); } } GcmBroadcastReceiver.completeWakefulIntent(intent); } private void sendNotification(String msg) { Log.d(TAG, "Preparing to send notification...: " + msg); mNotificationManager = (NotificationManager) this .getSystemService(Context.NOTIFICATION_SERVICE); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, RegisterActivity.class), 0); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder( this).setSmallIcon(R.drawable.gcm_cloud) .setContentTitle("GCM XMPP Message") .setStyle(new NotificationCompat.BigTextStyle().bigText(msg)) .setContentText(msg); mBuilder.setContentIntent(contentIntent); mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build()); Log.d(TAG, "Notification sent successfully."); } }
package com.javapapers.android.gcm.multiple; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.support.v4.content.WakefulBroadcastReceiver; public class GcmBroadcastReceiver extends WakefulBroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { ComponentName comp = new ComponentName(context.getPackageName(), GCMNotificationIntentService.class.getName()); startWakefulService(context, (intent.setComponent(comp))); setResultCode(Activity.RESULT_OK); } }
Comments are closed for "Google Cloud Messaging GCM CCS with XMPP".
Awesome!! I have been waiting for this tutorial for so long. Thanks for giving the time to explain and do the tutorials. You are a a legend.
Thanks Ousama.
Presently I am working on a chat application with XMPP and will post it in a couple of days and that will be interesting too.
Thanks joe
Hi Joe, when will you publish the chat application with XMPP? Eagerly waiting for it
Hi, thanks for the tutorial. i’m new in programming… I can’t run the server. What should I do?
I will surely publish it next week :-)
Hey Joe,
I wanted to ask a question about the server application. You’re java application SmackCcsClient.java is nearly identical to the Google provided version on develop.android.com, but you don’t have a main. You instead use sendMessage() which is called by GCMNotification.java. How does the program run without a main? I can’t compile it in eclipse.
Is there something I’m missing?
Great tutorial..
Hi joe,
I interest to make my App use this tutorial, i’ve created chat app using GCM only,sometimes i can send message from android device to another android device,but sometimes failed. till now i dont understand with my App why messages cant received..
I learn to make chat app and last time i read your article about this, i want to ask.
1. if users active more than 1 Million? is possible make chat app just use GCM?
2. are other chat app (whatsapp,line, etc) use xmpp & ccs ?
thanks so much joe..
I bookmark this link to guide my app to be better.
can you help me fix this bug? I implement follow this tutorial, but i met this bug.
…………………………
type Exception report
message Servlet execution threw an exception
description The server encountered an internal error that prevented it from fulfilling this request.
exception
javax.servlet.ServletException: Servlet execution threw an exception
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root cause
java.lang.ExceptionInInitializerError
org.jivesoftware.smack.ConnectionConfiguration.(ConnectionConfiguration.java:66)
com.tunha.gcm.SmackCcsClient.connect(SmackCcsClient.java:193)
com.tunha.gcm.GCMNotification.doPost(GCMNotification.java:48)
javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root cause
java.lang.IllegalStateException: org.xmlpull.v1.XmlPullParserException: caused by: org.xmlpull.v1.XmlPullParserException: resource not found: /META-INF/services/org.xmlpull.v1.XmlPullParserFactory make sure that parser implementing XmlPull API is available
org.jivesoftware.smack.SmackConfiguration.(SmackConfiguration.java:158)
org.jivesoftware.smack.ConnectionConfiguration.(ConnectionConfiguration.java:66)
com.tunha.gcm.SmackCcsClient.connect(SmackCcsClient.java:193)
com.tunha.gcm.GCMNotification.doPost(GCMNotification.java:48)
javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root cause
org.xmlpull.v1.XmlPullParserException: caused by: org.xmlpull.v1.XmlPullParserException: resource not found: /META-INF/services/org.xmlpull.v1.XmlPullParserFactory make sure that parser implementing XmlPull API is available
org.xmlpull.v1.XmlPullParserFactory.newInstance(XmlPullParserFactory.java:294)
org.xmlpull.v1.XmlPullParserFactory.newInstance(XmlPullParserFactory.java:259)
org.jivesoftware.smack.SmackConfiguration.processConfigFile(SmackConfiguration.java:352)
org.jivesoftware.smack.SmackConfiguration.processConfigFile(SmackConfiguration.java:347)
org.jivesoftware.smack.SmackConfiguration.(SmackConfiguration.java:155)
org.jivesoftware.smack.ConnectionConfiguration.(ConnectionConfiguration.java:66)
com.tunha.gcm.SmackCcsClient.connect(SmackCcsClient.java:193)
com.tunha.gcm.GCMNotification.doPost(GCMNotification.java:48)
javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
Thanks verymuch. I download them then import and run but why it is not as you demo. Message type is always send_event
I need for your help
Hey Joe,
Thanks for tutorial!
I have a query for follwing :
Google CCS XMMP Upstream Messaging Prerequisites
1.Project in Google Developers Console
2.Server API Key
3.Google Approval for API access
My question is “What is step 3 signifying here?” Which API access you are talking about for Google approval?
Hi joe,
Thanks for the tutorial,
This blog is only contains latest tutorial in internet for android chat app, thank for this additionally.
i can understand the coding flow in client and XMPP server part, but i dont have any idea to Make XMPP server.
whether i want to run SmackClass in comment prompt as you mentioned (if yes i’m getting no class exception for configuration) or can i keep any apache tomcat server. for future i want to customize server code for user online status options.
Pls give your reply ASAP.
Hi Joe,
Thanks for the tutorial!!
I have a query.. Can we have a java program which calls GCM server which executes on a local machine rather than having a web server. The java program is designed so that it calls GCM server with regid and apikey to send messages. If possible can you share idea please..
Thank You.
Thanks for the beautiful tutorial.
Pls how do I send notification base on geofence?
how i add smack lib to my project and how cloud do connection with server side php pls reply
Hi Joe,
I implemented your first GCM HTTP Push Notification tutorial successfully, and now I’m attempting to implement the XMPP Server. I’m having issues, however, importing the project into eclipse. I just downloaded the most recent version of Eclipse (IDE for Java Developers, Mars Release 4.5.0) and imported the project as a java project. I’m showing 19 errors, mostly of which are related to the “HttpServlet” and “HttpServletRequest” and the JSON values cannot be resolved. Does this need to be imported as a Java Web app. Any insight you can provide would be greatly appreciated.
Thanks!
Please help i am unable to code using xmpp
Hello sir. The server is good but i want to do implementation with php . Could you post the code with php ?