4
votes

I am trying to develop a XMPP chat client for Android (using Java connected to C#/Unity). I have got the Java -> Unity/C# connection working perfectly. I have also downloaded Asmack and can generate a library and my of wrapper class for initilazing a connection to a OpenFire XMPP server. However, I cannot seem to get Presence sent or recieved. I can log in, register new user and populate their roster, send messages, but cannot send any presence.

The code auto registrate users who never have used the app before. It also uses a preset friend list, and auto populates the roster with these friends.

The code is as follow (sorry for all the debug lines, can't use breakpoints when using Unity):

public class ASmackWrapper
{
    private XMPPConnection connection;
    private String[] friends;
    private static final String eventClass = "ASmackEventListener";
    private static ASmackWrapper wrapper;

    public static ASmackWrapper instance()
    {
        System.out.println("instancecreator of ASmackWrapper 1!");
        if (wrapper == null)
            wrapper = new ASmackWrapper();
        return wrapper;
    }

    public ASmackWrapper()
    {
        System.out.println("constructor of ASmackWrapper");
    }

    public boolean tryToRegister(String user, String pass){
        AccountManager acManager = connection.getAccountManager();
        try {
            Map<String, String> attributes = new HashMap<String,String>();
            attributes.put("email", "MY email");
            acManager.createAccount(user, pass,attributes);
        } catch (XMPPException e) {
            System.out.println("cant autoregister user "+ user +" ... with pass: "+pass+" on server. error:" + e.getLocalizedMessage());
            if (e.getLocalizedMessage().contains("conflict"))
                return false; // Wrong password, since there is already an account with that id!
            return false;
        }
        return true;
    }

    public void setFriends(String[] _friends) {
        friends = _friends;
    }

    public void start(String host, String user, String pass)
    {
        System.out.println("Java: openConenction host:"+host);
        ConnectionConfiguration cc = new ConnectionConfiguration(host,5222);
        //cc.setSendPresence(true);
        this.connection = new XMPPConnection(cc);
        Connection.DEBUG_ENABLED = true;
        try {
            this.connection.connect();
        } catch (XMPPException e) {
            System.out.println("Error connecting to server");
            return;
        }

        if(!this.connection.isConnected()) {
            System.out.println("Java: is not connected");
            onError("Connection failed");
            return;
        }

        boolean loginStatus = login(user, pass);
        if (!loginStatus) {
            onError("Login Failed");
            return;
        }

        RosterListener rl = new RosterListener() {
            public void entriesAdded(Collection<String> addresses) {}
            public void entriesUpdated(Collection<String> addresses) {}
            public void entriesDeleted(Collection<String> addresses) {}
            public void presenceChanged(Presence presence) {
                System.out.println("presence changed!" + presence.getFrom() + " "+presence.getStatus());
                onPresence(presence);
            }
        };
        if (connection.getRoster() != null) {
            connection.getRoster().setSubscriptionMode(Roster.SubscriptionMode.accept_all);

            System.out.println("7");
            connection.getRoster().addRosterListener(rl);
        }

        onAuthenticate("");
        System.out.println("10");

        //Set presence to online!
        Presence presence = new Presence(Presence.Type.available);
        presence.setStatus("Online, Programmatically!");
        presence.setPriority(24);
        presence.setMode(Presence.Mode.available);
        connection.sendPacket(presence);
    }

    private void addFriends() throws Exception {
        if (friends == null) {
            System.out.println("No friends to add");
            return;
        }
        System.out.println("Number of friends to add: "+friends.length);
        for (int i = 0;i<friends.length;i++) {
            System.out.println("Create user in roster: "+friends[i]);
            connection.getRoster().createEntry("fb"+friends[i], "No name_",null);
        }
    }

    private boolean login(String jid, String password) {
        System.out.println("1");
        boolean isLoggedIn=true;
        try { 
            this.connection.login(jid, password);
        } catch (XMPPException e) {
            isLoggedIn=false;
        }

        System.out.println("2");
        if(!isLoggedIn) {
            boolean isRegistred = tryToRegister(jid,password);
            if (isRegistred) {
                connection.disconnect();
                try {
                    connection.connect();
                    connection.login(jid, password); 
                } catch (XMPPException e) {
                    onError("Could not connect and login after registring");
                    return false;
                }
            } else {
                return false;
            }
        } 

        try {
            addFriends();
        } catch (Exception e) {
            onError("Could not add friends to roster");
        }
        ChatManager chatmanager = connection.getChatManager();

        chatmanager.addChatListener(new ChatManagerListener()
        {
            public void chatCreated(final Chat chat, final boolean createdLocally)
            {
                System.out.println("OK Chat created!");
                chat.addMessageListener(new MessageListener()
                {
                    public void processMessage(Chat chat, Message message)
                    {
                        onMessage(chat, message);
                    }
                });
            }
        });

        return true;
    }

    public void sendMessage(String rec, String message) {
        System.out.println("sendMessage(string,string) to host :"+connection.getHost());
        Chat chat = connection.getChatManager().createChat(rec+"@"+connection.getHost(), new MessageListener() {
            public void processMessage(Chat chat, Message message) {
                // Print out any messages we get back to standard out.
                System.out.println("Probably an error, since we got a instant reply on sent message. Received message body: " + message.getBody() + " from:"+message.getFrom() + " to:"+message.getTo());
            }
        });
        try {
            chat.sendMessage(message);
            System.out.println("Message sent");
        } catch (XMPPException e) {
            System.out.println("Error sending message: "+e.toString());
            e.printStackTrace();

        }
    }

    public void logout () {
        System.out.println("Login out...");
        connection.disconnect();
    }

    public void getOnlineFriends() {
        Roster roster = connection.getRoster(); 
        Collection<RosterEntry> entries = roster.getEntries();

        for(RosterEntry rosterEntry: entries) {
            String user = rosterEntry.getUser();
            Presence presence = roster.getPresence(user);
            System.out.println("Presence : "+presence);                                     
            System.out.println("Presence type: "+presence.getType());                
            System.out.println("Presence mode: "+presence.getMode());
        }

        //Set presence to online!
        Presence presence = new Presence(Presence.Type.available);
        presence.setStatus("Online, Programmatically!");
        presence.setPriority(24);
        presence.setMode(Presence.Mode.available);
        connection.sendPacket(presence);
    }

    private void onMessage(Chat chat, Message message) {
        String m = ("Received message: " + (message != null ? message.getBody() : "NULL"));
        System.out.println(m);

        UnityPlayer.UnitySendMessage(eventClass, "Message", m);
    }

    private void onError(String message) {
        UnityPlayer.UnitySendMessage(eventClass, "Error", message);
    }

    private void onAuthenticate(String message) {
        UnityPlayer.UnitySendMessage(eventClass, "Authenticate", message);
    }

    private void onPresence(Presence presence) {
        String user = presence.getFrom();
        if (presence.getType() == Presence.Type.available)
            UnityPlayer.UnitySendMessage(eventClass, "Online", user);
        else
            UnityPlayer.UnitySendMessage(eventClass, "Offline", user);

            System.out.println("Java: Presence changed, from:" +presence.getFrom() + " type:"+presence.getType() + " toString:"+presence.toString());
    }


}

The presence is checked in two ways, by setting a presence listener and by fetching the status after login. This SO page suggest waiting 5 sec before getting the precense: Unable to get presence of roster by using smack, openfire I have tried that as well by calling getOnlineFriends from a button, more than 5 sec after login. The listener never gets called. It just fires once after login, once for each on the roster with a presence = null.

Edit: After turning on Debug mode on Asmack I see the following reply to my presence send message:

SENT <presence id="3sG7l-11"><status>Online, Programmatically!</status><priority>24</priority></presence>

RCV <presence id="6s7BX-5" to="[email protected]/Smack" from="10000063946242" type="error">
<error code="404" type="cancel">
<remote-server-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
</error></presence>

The server log:

org.jivesoftware.openfire.nio.ConnectionHandler - Closing connection due to error while processing message:
<iq id="566-4" type="error" from="xxx.tripnet.se/f55aea72" to="xxx.tripnet.se">
<error /><error type="cancel" code="501">
<feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /></error>
<ping xmlns="urn:xmpp:ping" /></iq> 

java.lang.IllegalArgumentException: IQ must be of type 'set' or 'get'. Original IQ:
<iq id="566-4" type="error" from="xxx.tripnet.se/f55aea72" to="xxx.tripnet.se">
<error/><error type="cancel" code="501">
<feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error>
<ping xmlns="urn:xmpp:ping"/></iq> 
at org.xmpp.packet.IQ.createResultIQ(IQ.java:384) 

I have also tried to pass along setSendPresence = true to the connectionconfiguration passed in connect(), but no difference.

I have also tried to set the subscription mode manually in OpenFire server to "both" (from "none") on both users, but with no effect.

2
Perhaps a bug in the latest version? :S community.igniterealtime.org/thread/43986Sunkas
My latest clue was that the roster jid was in the wrong format. But changing from "id" to "[email protected]" did not help :(Sunkas

2 Answers

3
votes

Got it working! It was probably due to that I did not use the correct format on my jid's in the roster list. The correct format had to be [email protected], not just user.

1
votes

Write a RosterListener and see if it works.

public void rosterOnlineStatus(){
    Roster roster = connection.getRoster();
    Presence status = new Presence(Type.available);
    status.setStatus("Hello This is Phaneendra");
    roster.addRosterListener(new RosterListener() {

        @Override
        public void presenceChanged(Presence presence) {

            System.out.println(presence.getFrom()+ "is "+presence+" "+presence.getStatus());
            presence.getStatus();


        }

        @Override
        public void entriesUpdated(Collection<String> addresses) {}

        @Override
        public void entriesDeleted(Collection<String> addresses) {}

        @Override
        public void entriesAdded(Collection<String> addresses) {}
    });
}

See if adding the RosterListener will work?