Itachi Itachi - 6 months ago 100
Java Question

Smack 4.1.0 GCM CCS stops responding after a while

Have implemented gcm ccs for chat module and i am able to send and receive messages. Below is the main connection module,

config = XMPPTCPConnectionConfiguration.builder()
.setServiceName("gcm-pesu.googleapis.com")
.setPort(GCM_PORT)
.setHost(GCM_SERVER)
.setCompressionEnabled(false)
.setConnectTimeout(30000)
.setSecurityMode(SecurityMode.ifpossible)
.setSendPresence(false)
.setSocketFactory(SSLSocketFactory.getDefault())
.build();

connection = new XMPPTCPConnection(config);
connection.connect();

Roster roster = Roster.getInstanceFor(connection);
roster.setRosterLoadedAtLogin(false);

connection.addConnectionListener(new LoggingConnectionListener());

// Handle incoming packets
connection.addAsyncStanzaListener(new MyStanzaListener(), new MyStanzaFilter());

// Log all outgoing packets
connection.addPacketInterceptor(new MyStanzaInterceptor(), new MyStanzaFilter());

connection.login(mProjectId + "@gcm.googleapis.com", mApiKey);
logger.info("logged in: " + mProjectId);

PingManager pm = PingManager.getInstanceFor(connection);
pm.setPingInterval(300);
pm.pingMyServer();
pm.registerPingFailedListener(new PingFailedListener() {
@Override
public void pingFailed() {
connection.disconnect();
logger.error("GCM CCS, Ping failed !!");
}
});


The problem i am running into is not receiving any message from GCM, sent by client device after a while. Though, the heartbeat looks normal and i do get pong from GCM even in that case. Is it something to do with SSL ?

Have handled connection draining case as follows,

String controlType = (String) jsonObject.get("control_type");
volatile boolean connectionDraining = false;
if ("CONNECTION_DRAINING".equals(controlType)) {
connectionDraining = true;
try {
connection.disconnect();
connect();
connectionDraining = false;
} catch (Exception e) {
logger.error("Error establishing new connection after draining ", e);
}
}

Answer

Implemented queue of channels when one of it is draining.

            private Deque<Channel> channels;
            protected void handleControlMessage(Map<String, Object> jsonObject) {
            logger.info("Control message : " + jsonObject);
            String controlType = (String) jsonObject.get("control_type");
            if ("CONNECTION_DRAINING".equals(controlType)) {
                connectionDraining = true;
            } 
        }

Create new channel while sending message

        public void sendDownstreamMessage(String jsonRequest) {
        Channel channel = channels.peekFirst();
        try {
            if (channel.connectionDraining) {
                synchronized (channels) {
                    channel = channels.peekFirst();
                    if (channel.connectionDraining) {
                        channels.addFirst(connect());
                        channel = channels.peekFirst();
                    }
                }
            }
            channel.send(jsonRequest);
        } catch (Exception e) {
            logger.error("Message not sent. Error in connecting :", e);
        }
    }

GCM will take care of closing the other. This resolved the issue.

Comments