I've implemented the new GCM CCS for bi-directional messages between an Android application and a web server. Downstream messages (web-device) work perfectly. Unfortunately, upstream messages (device-web) are not received on the server. They do appear to be sent on the client side (see Android app log message below), but nothing is being received on the server.
D/GCM﹕ GcmService start Intent { act=com.google.android.gcm.intent.SEND flg=0x10 pkg=com.google.android.gms cmp=com.google.android.gms/.gcm.GcmService (has extras) } com.google.android.gcm.intent.SEND
I presume nothing is wrong on the Android side, but rather on the server side. The thing is, I cannot manage to figure out what is wrong, because the connection is still alive and I do receive some messages from GCM server, like ACKs. So why are normal messages not received ? Does anybody have any idea ?
Some other details worth mentioning are that the web server being used is Glassfish and that I start the XMPP connection inside a Servlet. Some snippets below.
EDIT: As I've stated in the answer, the major problem that prevented any message being received on the server has been resolved. However, quite a lot of messages are still not being received (approx. 50%).
For example, I'm sending 2 messages immediately one after another on a background thread, every time a user makes a change in the Android app (presses a button, so there's a minimum couple of seconds between every batch of 2). Sometimes I receive both messages on the server, sometimes I receive only 1 of them, sometimes nothing even happens... This is a serious problem, especially for apps that rely on this technology at their core. Can anybody be of further assistance in order to troubleshoot this down ?
More info: I'm pretty sure this is not client related, as every message is being sent, like you see in the logcat log above and I also receive the "event:sent" GCM broadcast after a while (not immediately though, maybe like 5 mins). So it must be something GCM-based or server-based.
public class CcsServlet extends HttpServlet
{
private static Logger logger = Logger.getLogger(CcsServlet.class.getName());
public void init(ServletConfig config) throws ServletException
{
CcsManager ccsManager = CcsManager.getInstance();
try
{
ccsManager.connect();
}
catch (Exception e)
{
logger.warning("Cannot connect to CCS server.");
e.printStackTrace();
}
}
}
public class CcsManager
{
private static XMPPConnection connection;
private static Logger logger = Logger.getLogger(CcsManager.class.getName());
public static final String GCM_SERVER = "gcm.googleapis.com";
public static final int GCM_PORT = 5235;
private static CcsManager sInstance = null;
private static final String USERNAME = "xxxxxxxxxx" + "@gcm.googleapis.com";
private static final String PASSWORD = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
public CcsManager()
{
// Add GcmPacketExtension
ProviderManager.getInstance().addExtensionProvider(
GcmPacketExtension.GCM_ELEMENT_NAME,
GcmPacketExtension.GCM_NAMESPACE, new PacketExtensionProvider()
{
public PacketExtension parseExtension(XmlPullParser parser) throws Exception
{
String json = parser.nextText();
return new GcmPacketExtension(json);
}
});
}
public static CcsManager getInstance()
{
if (sInstance == null)
sInstance = new CcsManager();
return sInstance;
}
/**
* Connects to GCM Cloud Connection Server
*/
public void connect() throws IOException, XMPPException
{
ConnectionConfiguration config = new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
config.setSecurityMode(ConnectionConfiguration.SecurityMode.enabled);
config.setReconnectionAllowed(true);
config.setRosterLoadedAtLogin(false);
config.setSendPresence(false);
config.setSocketFactory(SSLSocketFactory.getDefault());
config.setDebuggerEnabled(false);
connection = new XMPPConnection(config);
connection.connect();
connection.addConnectionListener(new ConnectionListener()
{
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 %s secs", seconds);
}
public void connectionClosedOnError(Exception e)
{
logger.info("Connection closed on error.");
}
public void connectionClosed()
{
logger.info("Connection closed.");
}
});
// Handle incoming packets
connection.addPacketListener(new PacketListener()
{
public void processPacket(Packet packet)
{
logger.log(Level.INFO, "Received: " + packet.toXML());
Message incomingMessage = (Message) packet;
GcmPacketExtension gcmPacket =
(GcmPacketExtension) incomingMessage.getExtension(GcmPacketExtension.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()
{
public void interceptPacket(Packet packet)
{
logger.log(Level.INFO, "Sent: {0}", packet.toXML());
}
}, new PacketTypeFilter(Message.class));
connection.login(USERNAME, PASSWORD);
}
}
It appears that the web application was being deployed twice on the server. This caused the servlet that creates the XMPP connection to be initialized once, then destroyed, then initialized again. This sequence probably wasn't a good thing for the connection to GCM (dho..).
Just make sure that the servlet is initialized only once. Check this by using loggers in place inside its init() and destroy() methods. If it is indeed called more than once, try assigning the web module to a specific virtual server for deployment. I believe this differs from web server to web server. For Glassfish, you need to do this from within the admin console (localhost:4848).
This solves the bigger problem. Another problem I encountered was that the upstream messaging was not reliable at all. Sometimes multiple consecutive upstream messages from the same device worked flawlessly, only to try another one that wasn't pushed at all to the server. Haven't figured out any pattern to this problem... I'll get back if I find anything else.
EDIT: Apparently there was an issue when using the implementation on a local server. Moved to a remote server (staging VPS) and the issue appears to be gone. Every message is received by the server. I'll be back if the issue persists, but I doubt it. I presume the local issue was due to either my ISP, or heck, even my local Wi-Fi connection. I don't have a complete answer as to what exactly caused this, but at least it works perfectly on the staging server.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With