Smack 4.5.0-rc1: BoshClient throws .LinkedList.isEmpty() on a null object reference (exhanges)

With aTalk ported to use smack 4.5.0-rc1, found the following problem with BOSH connection whenever threre is change in the network connection. The problem does not happen in aTalk with smack-4.4.8 release.

From the debug info, the BoshClient always execute #dispose() followed by #disconnect() whenever there is a detection of the network disconnection. the ‘exchanges’ was set to null in #dispose(), hence when #send() (called by disconnect) checks for exchanges.isEmpty(), it throws the exception.

11:04:23.897  E  java.lang.Exception: Bosh dispose
                 	at org.igniterealtime.jbosh.BOSHClient.dispose(BOSHClient.java:690)
                 	at org.igniterealtime.jbosh.BOSHClient.processExchange(BOSHClient.java:1142)
                 	at org.igniterealtime.jbosh.BOSHClient.processMessages(BOSHClient.java:1003)
                 	at org.igniterealtime.jbosh.BOSHClient.-$$Nest$mprocessMessages(Unknown Source:0)
                 	at org.igniterealtime.jbosh.BOSHClient$RequestProcessor.run(BOSHClient.java:1732)
                 	at java.lang.Thread.run(Thread.java:920)
11:04:23.904  E  java.lang.Exception: Bosh disconnect
                 	at org.igniterealtime.jbosh.BOSHClient.disconnect(BOSHClient.java:569)
                 	at org.jivesoftware.smack.bosh.XMPPBOSHConnection.instantShutdown(XMPPBOSHConnection.java:344)
                 	at org.jivesoftware.smack.AbstractXMPPConnection.notifyConnectionError(AbstractXMPPConnection.java:1011)
                 	at org.jivesoftware.smack.bosh.XMPPBOSHConnection.access$500(XMPPBOSHConnection.java:72)
                 	at org.jivesoftware.smack.bosh.XMPPBOSHConnection$BOSHConnectionListener.connectionEvent(XMPPBOSHConnection.java:539)
                 	at org.igniterealtime.jbosh.BOSHClient.fireConnectionClosedOnError(BOSHClient.java:1688)
                 	at org.igniterealtime.jbosh.BOSHClient.dispose(BOSHClient.java:717)
                 	at org.igniterealtime.jbosh.BOSHClient.processExchange(BOSHClient.java:1142)
                 	at org.igniterealtime.jbosh.BOSHClient.processMessages(BOSHClient.java:1003)
                 	at org.igniterealtime.jbosh.BOSHClient.-$$Nest$mprocessMessages(Unknown Source:0)
                 	at org.igniterealtime.jbosh.BOSHClient$RequestProcessor.run(BOSHClient.java:1732)
                 	at java.lang.Thread.run(Thread.java:920)
11:04:23.923  W  [2154] org.jivesoftware.smack.bosh.XMPPBOSHConnection.instantShutdown() shutdown
                 java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.util.LinkedList.isEmpty()' on a null object reference
                 	at org.igniterealtime.jbosh.BOSHClient.send(BOSHClient.java:496)
                 	at org.igniterealtime.jbosh.BOSHClient.disconnect(BOSHClient.java:589)
                 	at org.igniterealtime.jbosh.BOSHClient.disconnect(BOSHClient.java:570)
                 	at org.jivesoftware.smack.bosh.XMPPBOSHConnection.instantShutdown(XMPPBOSHConnection.java:344)
                 	at org.jivesoftware.smack.AbstractXMPPConnection.notifyConnectionError(AbstractXMPPConnection.java:1011)
                 	at org.jivesoftware.smack.bosh.XMPPBOSHConnection.access$500(XMPPBOSHConnection.java:72)
                 	at org.jivesoftware.smack.bosh.XMPPBOSHConnection$BOSHConnectionListener.connectionEvent(XMPPBOSHConnection.java:539)
                 	at org.igniterealtime.jbosh.BOSHClient.fireConnectionClosedOnError(BOSHClient.java:1688)
                 	at org.igniterealtime.jbosh.BOSHClient.dispose(BOSHClient.java:717)
                 	at org.igniterealtime.jbosh.BOSHClient.processExchange(BOSHClient.java:1142)
                 	at org.igniterealtime.jbosh.BOSHClient.processMessages(BOSHClient.java:1003)
                 	at org.igniterealtime.jbosh.BOSHClient.-$$Nest$mprocessMessages(Unknown Source:0)
                 	at org.igniterealtime.jbosh.BOSHClient$RequestProcessor.run(BOSHClient.java:1732)
                 	at java.lang.Thread.run(Thread.java:920)
11:04:23.955  W  [2179] org.jivesoftware.smack.AbstractXMPPConnection.callConnectionClosedOnErrorListener() Connection XMPPBOSHConnection[swordfish@atalk.sytes.net/atalk-17kg4on] (0) closed with error
                 org.igniterealtime.jbosh.BOSHException: Could not obtain response
                 	at org.igniterealtime.jbosh.ApacheHTTPResponse.awaitResponse(ApacheHTTPResponse.java:251)
                 	at org.igniterealtime.jbosh.ApacheHTTPResponse.getBody(ApacheHTTPResponse.java:192)
                 	at org.igniterealtime.jbosh.BOSHClient.processExchange(BOSHClient.java:1127)
                 	at org.igniterealtime.jbosh.BOSHClient.processMessages(BOSHClient.java:1003)
                 	at org.igniterealtime.jbosh.BOSHClient.-$$Nest$mprocessMessages(Unknown Source:0)
                 	at org.igniterealtime.jbosh.BOSHClient$RequestProcessor.run(BOSHClient.java:1732)
                 	at java.lang.Thread.run(Thread.java:920)
                 Caused by: java.net.SocketException: Software caused connection abort
                 	at java.net.SocketInputStream.socketRead0(Native Method)
                 	at java.net.SocketInputStream.socketRead(SocketInputStream.java:119)
                 	at java.net.SocketInputStream.read(SocketInputStream.java:176)
                 	at java.net.SocketInputStream.read(SocketInputStream.java:144)
                 	at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.readFromSocket(ConscryptEngineSocket.java:945)
                 	at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.processDataFromSocket(ConscryptEngineSocket.java:909)
                 	at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.readUntilDataAvailable(ConscryptEngineSocket.java:824)
                 	at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.read(ConscryptEngineSocket.java:797)
                 	at org.apache.http.impl.io.AbstractSessionInputBuffer.fillBuffer(AbstractSessionInputBuffer.java:108)
                 	at org.apache.http.impl.io.AbstractSessionInputBuffer.readLine(AbstractSessionInputBuffer.java:196)
                 	at org.apache.http.impl.conn.DefaultResponseParser.parseHead(DefaultResponseParser.java:88)
                 	at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:179)
                 	at org.apache.http.impl.AbstractHttpClientConnection.receiveResponseHeader(AbstractHttpClientConnection.java:185)
                 	at org.apache.http.impl.conn.DefaultClientConnection.receiveResponseHeader(DefaultClientConnection.java:240)
                 	at org.apache.http.impl.conn.AbstractClientConnAdapter.receiveResponseHeader(AbstractClientConnAdapter.java:264)
                 	at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:284)
                 	at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:126)
                 	at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:442)
                 	at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:560)
                 	at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:492)
                 	at org.igniterealtime.jbosh.ApacheHTTPResponse.awaitResponse(ApacheHTTPResponse.java:235)
                 	at org.igniterealtime.jbosh.ApacheHTTPResponse.getBody(ApacheHTTPResponse.java:192) 
                 	at org.igniterealtime.jbosh.BOSHClient.processExchange(BOSHClient.java:1127) 
                 	at org.igniterealtime.jbosh.BOSHClient.processMessages(BOSHClient.java:1003) 
                 	at org.igniterealtime.jbosh.BOSHClient.-$$Nest$mprocessMessages(Unknown Source:0) 
                 	at org.igniterealtime.jbosh.BOSHClient$RequestProcessor.run(BOSHClient.java:1732) 
                 	at java.lang.Thread.run(Thread.java:920)
.....
11:04:24.157  D  Received system activity event: net.java.sip.communicator.service.sysactivity.event.SystemActivityEvent[eventID=9]
11:04:25.010  D  XMPPConnection (XMPPBOSHConnection[swordfish@atalk.sytes.net/atalk-17kg4on] (0)) will reconnect in 2
11:04:26.012  D  XMPPConnection (XMPPBOSHConnection[swordfish@atalk.sytes.net/atalk-17kg4on] (0)) will reconnect in 1
11:04:27.014  D  XMPPConnection (XMPPBOSHConnection[swordfish@atalk.sytes.net/atalk-17kg4on] (0)) will reconnect in 0
11:04:27.015  D  XMPPConnection (XMPPBOSHConnection[swordfish@atalk.sytes.net/atalk-17kg4on] (0)) will reconnect in 0           

aTalk modifies and splits shutdown() and instantShutdown() source so it is similar to smack-4.4.8 code. I also changed all the local call to instantShutdown() to shutdown(). The above reported problem is resolved.

Any reason why the ‘client.disconnected’, the cause of the problem, is moved to instantShutdown(). Actually shutdown() is called via AbstractXMPPConnection#disconnect(); and

instantShutdown() is called via AbstractXMPPConnection#notifyConnectionError(), so calling to client.disconnect() will not have any effect. May be just move the client=null into instantShutdown().


    @Override
    protected void shutdown() {
        if (client != null) {
            try {
                client.disconnect();
            } catch (Exception e) {
                LOGGER.log(Level.WARNING, "shutdown", e);
            }
        }
        instantShutdown();
    }

    @Override
    public void instantShutdown() {
        outgoingQueue.shutdown();

        try {
            boolean writerThreadTerminated = waitFor(() -> !writerThreadRunning);
            if (!writerThreadTerminated) {
                LOGGER.severe("Writer thread of " + this + " did not terminate timely");
            }
        } catch (InterruptedException e) {
            LOGGER.log(Level.FINE, "Interrupted while waiting for writer thread to terminate", e);
        }

        setWasAuthenticated();
        client = null;
        sessionID = null;
        done = true;
        authenticated = false;
        connected = false;
        isFirstInitialization = true;

        // Close down the readers and writers.
        CloseableUtil.maybeClose(readerPipe, LOGGER);
        CloseableUtil.maybeClose(reader, LOGGER);
        CloseableUtil.maybeClose(writer, LOGGER);

        // set readerConsumer = null before reader to avoid NPE reference
        readerConsumer = null;
        readerPipe = null;
        reader = null;
        writer = null;
    }