سؤال

Bit of an odd one, but I'm having issues with monitoring the connection status of an XMPP connection. I'm using Smack (3.4.1) and Openfire (3.8.2)

Openfire reports that the client has disconnected. As does my remote client BOT, which is responsible for middle-man server requests. However, the devices themselves still believe they're connected. So something has abruptly killed the connection.

I can replicate the scenario, by closing the client connection manually via Openfire admin console. After doing this, the client still reports that it's connected, with the following details:

04-15 09:48:23.349: D/BXC(18166): Checking Xmpp connection status
04-15 09:48:23.349: I/BXC(18166): Xmpp Connection still valid
04-15 09:48:23.349: D/BXC(18166): isNull=false
04-15 09:48:23.349: D/BXC(18166): isConnected()=true
04-15 09:48:23.349: D/BXC(18166): isAuthenticated()=true
04-15 09:48:23.349: D/BXC(18166): xmppConnectorRunning=true
04-15 09:48:23.349: D/BXC(18166): socketIsClosed()=false
04-15 09:48:23.349: D/BXC(18166): Are we logged in?
04-15 09:48:23.349: I/BXC(18166): Yes - Logged in.

From the code:

Log.d("BXC", "isNull=" + (xConnection == null));
Log.d("BXC", "isConnected()=" + xConnection.isConnected());
Log.d("BXC", "isAuthenticated()=" + xConnection.isAuthenticated());
Log.d("BXC", "xmppConnectorRunning=" + Globals.backgroundXmppConnectorRunning);
Log.d("BXC", "socketIsClosed()=" + xConnection.isSocketClosed());

The trouble is, that the connection will remain in this state until I restart the app. What is the best value/data/variable/method call to use to check the actual status of an XMPP connection?

Here's my BackgroundXmppConnector Service code (I've quite possibly borked something along the way, but I can't see it):

package com.goosesys.gaggle.services;

import java.util.Collection;

import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.Roster.SubscriptionMode;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;

import com.google.gson.Gson;
import com.goosesys.gaggle.Globals;
import com.goosesys.gaggle.application.AppSettings;
import com.goosesys.gaggle.application.Utility;

import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;

public class BackgroundXmppConnector extends Service
{
    private ConnectionConfiguration acc;
    private XMPPConnection xConnection;
    private final IBinder mBinder = new XmppBinder();
    private final Handler mHandler = new Handler();
    private static int mInterval = (1 * 60 * 1000);
    private static boolean bConnecting = false;
    private static final Object connectLock = new Object(); 

    private final Runnable checkConnection = new Runnable()
    {
        @Override
        public void run()
        {
            checkConnectionStatus();    
        }
    };

    @Override
    public void onCreate()
    {   
        Log.i("BXC", "BackgroundXmppConnector Service has been created");

        // Checks the connection state every 1 minute //
        mHandler.postDelayed(checkConnection, mInterval);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
//      SmackAndroid.init(getApplicationContext());

        Log.d("BXC", "Xmpp Connector Started");
        if(xConnection == null)
        {
            checkConnectionStatus();
        }

        return Service.START_STICKY;
    }

    private void setupConnection()
    {       
        try 
        {
            acc = new ConnectionConfiguration(AppSettings.XMPP_SERVER_HOST,
                    AppSettings.XMPP_SERVER_PORT);
            acc.setSecurityMode(SecurityMode.disabled);
            acc.setSASLAuthenticationEnabled(false);
            acc.setReconnectionAllowed(true);

            xConnection = new XMPPConnection(acc);

            xConnection.addConnectionListener(new ConnectionListener()
            {
                @Override
                public void connectionClosed() 
                {
                    Log.e("BXC", "Xmpp connection closed");
                    Globals.backgroundXmppConnectorRunning = false;
                    bConnecting = false;
                }

                @Override
                public void connectionClosedOnError(Exception e) 
                {
                    Log.e("BXC", "Xmpp connection closed with error: " + e);
                    Globals.backgroundXmppConnectorRunning = false;
                    bConnecting = false;
                }

                @Override
                public void reconnectingIn(int seconds) 
                {
                    Log.i("BXC", "Xmpp connection, reconnecting in " + seconds + " seconds");
                    bConnecting = true;
                }

                @Override
                public void reconnectionFailed(Exception e) 
                {
                    Log.e("BXC", "Xmpp reconnection failed: " + e);
                    Globals.backgroundXmppConnectorRunning = false;
                    bConnecting = false;
                }

                @Override
                public void reconnectionSuccessful() 
                {
                    Log.i("BXC", "Xmpp reconnected successfully");
                    Globals.backgroundXmppConnectorRunning = true;
                    bConnecting = false;
                }           
            });                     

        } 
        catch (Exception e)
        {
            Log.e("BXC", e.getMessage());
        }   
        finally
        {
            // Schedule another check in 1 minute //
            mHandler.postDelayed(checkConnection, mInterval);               
        }

        if(xConnection.isAuthenticated() == false)
            new LoginTask().execute();
    }

    public boolean sendMessage(Intent intent)
    {
        if(xConnection != null && xConnection.isConnected())
        {
            String jsonObject;
            Bundle extras = intent.getExtras();
            if(extras != null)
            {
                jsonObject = extras.getString("MESSAGEDATA");
                Message m = new Gson().fromJson(jsonObject, Message.class);
                if(m != null)
                {
                    sendMessage(m);
                }
                else
                {
                    Log.e("BXC", "Message to send was/is null. Can't send.");
                }

                m = null;
                jsonObject = null;
                extras = null;
            }

            Log.i("BXC", "Sending Xmpp Packet");
            return true;
        }

        return false;
    }

    /*
     * Sends message to xmpp server - message packet in form of
     * 
     * --------------------MESSAGE PACKET-------------------------
     * TO
     * -----------------------
     * FROM
     * -----------------------
     * BODY
     *      TRANSACTION-------------------------------------------
     *          MessageType
     *          --------------------------------------------------
     *          TransactionObject
     */
    private void sendMessage(Message m)
    {
        try
        {
            Log.d("BXC", "Sending transaction message to Xmpp Server");
            xConnection.sendPacket(m);          
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }

    public boolean isConnected()
    {
        return xConnection.isConnected();
    }

    private void checkConnectionStatus()
    {

        Log.d("BXC", "Checking Xmpp connection status");

        if(!bConnecting)
        {

            // if connection object is null - re-create
            if(xConnection == null) //  || xConnection.isConnected() == false
            {
                // The connection has stalled for some reason - attempt a reconnect
                Log.e("BXC", "No connection. Attempting to connect.");
                setupConnection();          
            }
            else
            {
                Log.i("BXC", "Xmpp Connection still valid");
            }

            Log.d("BXC", "isNull=" + (xConnection == null));
            Log.d("BXC", "isConnected()=" + xConnection.isConnected());
            Log.d("BXC", "isAuthenticated()=" + xConnection.isAuthenticated());
            Log.d("BXC", "xmppConnectorRunning=" + Globals.backgroundXmppConnectorRunning);
            Log.d("BXC", "socketIsClosed()=" + xConnection.isSocketClosed());

            if(xConnection != null && xConnection.isConnected() == true)
            {
                Log.d("BXC", "Are we logged in?");

                if(xConnection.isAuthenticated() == false)
                {
                    Log.e("BXC", "Nope. Logging in...");
                    new LoginTask().execute();          
                }
                else
                {
                    Log.i("BXC", "Yes - Logged in.");
                }               
            }
            else if(xConnection == null || xConnection.isConnected() == false || xConnection.isAuthenticated() == false)
            {
                Log.e("BXC", "Disconnected. Attempting reconnect");
                new LoginTask().execute();
            }
            else
            {
                Log.e("BXC", "Couldn't log in because connection failed.");
            }


        }
        else
        {
            Log.e("BXC", "Already checking.");
        }


        // Schedule again for 5 minutes. 
        mHandler.postDelayed(checkConnection, mInterval);
    }

    // BINDER ////////////////////////////////////////////////////////////////////////////////
    @Override
    public IBinder onBind(Intent intent) 
    {
        return mBinder;
    }


    // INTERNAL CLASSES //////////////////////////////////////////////////////////////////////
    public class XmppBinder extends Binder
    {
        public BackgroundXmppConnector getService(){
            return BackgroundXmppConnector.this;
        }
    }

    private class LoginTask extends AsyncTask<Void, Void, Void>
    {
        @Override
        protected Void doInBackground(Void... params)
        {
            try
            {   
                bConnecting = true;
                synchronized(connectLock)
                {
                    if(xConnection != null && (xConnection.isSocketClosed() || !xConnection.isConnected()))
                    {                   
                        xConnection.connect();
                        Log.i("BXC", "Login Credentials: " + Utility.getAndroidID(getApplicationContext()) + " / " + AppSettings.XMPP_KEYSTORE_PASSWORD);
                        xConnection.login(Utility.getAndroidID(getApplicationContext()), AppSettings.XMPP_KEYSTORE_PASSWORD);                   

                        xConnection.getChatManager().addChatListener(new ChatManagerListener(){
                            @Override
                            public void chatCreated(final Chat chat, boolean createdLocally)
                            {
                                if(!createdLocally)
                                {
                                    // add chat listener //
                                    chat.addMessageListener(new BackgroundMessageListener(getApplicationContext()));
                                }
                            }

                        });                 

                        Presence p = new Presence(Presence.Type.subscribe);
                        p.setStatus("Out and About");
                        xConnection.sendPacket(p);

                        Roster r = xConnection.getRoster();                 
                        r.setSubscriptionMode(SubscriptionMode.accept_all);
                        r.createEntry(AppSettings.BOT_NAME, "AbleBot", null);
                        r.addRosterListener(new RosterListener(){

                            @Override
                            public void entriesAdded(Collection<String> addresses) 
                            {               
                                for(String s : addresses)
                                {
                                    Log.d("BXC", "Entries Added: " + s);
                                }
                            }

                            @Override
                            public void entriesDeleted(Collection<String> addresses) 
                            {
                                for(String s : addresses)
                                {
                                    Log.d("BXC", "Entries Deleted: " + s);
                                }                           
                            }

                            @Override
                            public void entriesUpdated(Collection<String> addresses) 
                            {   
                                for(String s : addresses)
                                {
                                    Log.d("BXC", "Entries updated: " + s);
                                }                           
                            }

                            @Override
                            public void presenceChanged(Presence presence) 
                            {   
                                Log.d("BXC", "PresenceChanged: " + presence.getFrom());
                            }                       
                        });
                    }
                }           
            }
            catch(IllegalStateException ex)
            {
                Log.e("BXC", "IllegalStateException -->");
                Globals.backgroundXmppConnectorRunning = false;
                ex.printStackTrace();
            }
            catch(XMPPException ex)
            {
                Log.e("BXC", "XMPPException -->");
                Globals.backgroundXmppConnectorRunning = false;
                ex.printStackTrace();
            }
            catch(NullPointerException ex)
            {
                Log.e("BXC", "NullPointerException -->");
                Globals.backgroundXmppConnectorRunning = false;
                ex.printStackTrace();
            }
            catch(Exception ex)
            {
                Log.e("BXC", "Exception -->");
                Globals.backgroundXmppConnectorRunning = false;
                ex.printStackTrace();
            }

            return null;                    
            //}         
        }

        @Override
        protected void onPostExecute(Void ignored)
        {
            if(xConnection != null)
            {
                if(xConnection.isConnected() && (!xConnection.isSocketClosed()))
                {
                    Log.i("BXC", "Logged in to XMPP Server");
                    Globals.backgroundXmppConnectorRunning = true;
                    bConnecting = false;
                }
                else
                {
                    Log.e("BXC", "Unable to log into XMPP Server.");    
                    Globals.backgroundXmppConnectorRunning = false;
                    bConnecting = false;
                }
            }
            else
            {
                Log.e("BXC", "Xmpp Connection object is null");
                Globals.backgroundXmppConnectorRunning = false;
                bConnecting = false;
            }
        }
    }

}
هل كانت مفيدة؟

المحلول

There was no actual way around this as the internal variables/members within the XMPPConnector class, are always true (except for initial startup), regardless of connection state.

How I got around this was to create a global static variable, which I set true/false, depending on what the connection status is, defined by the callback methods, such as:

connectionClosed, connectionClosedOnError etc.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top