I’‘m using Roster.removeEntry() to remove a buddy from my roster. Instead of seeing me offline on my ex-buddy’‘s roster, i was also removed from my ex-buddy’'s roster.
This seem to contradict with RFC3921 item 8.6 where it says:
“Note: When the user removes the contact from the user’‘s roster, the end state of the contact’‘s roster is that the user is still in the contact’'s roster with a subscription state of “none”; in order to completely remove the roster item for the user, the contact needs to also send a roster removal request.”
I have tried Spark and had the same problem. Is it my misunderstanding with the RFC or there’'s something wrong in there?
A Roster#Remove causes an unsubscribe packet to be sent to the remote client, depending upon how the remote client handles this it may cause the removing user to be removed from the removed user´s roster as well. There is an open issue to deal with this:
SMACK-12
Vote for it if you would like to see it implemented. If you would like to change how the remote entity handles the unsubscribe packet, then you will need to turn off auto subscription handling and add a listener to handle the packets how you would like manually.
I would definitely prefer this, as mentioned in XMPP specs:
“Note: When the user removes the contact from the user’‘s roster, the end state of the contact’‘s roster is that the user is still in the contact’'s roster with a subscription state of “none”; in order to completely remove the roster item for the user, the contact needs to also send a roster removal request.”
Since Smack is an API for XMPP clients, I think it should leave the removal decision to the actual client application using Smack, such as Spark to do the roster removal.
However, since there have been no votes to that feature request since 2004, does that mean the majority prefers the existing behaviour?
I don`t think the lack of votes indicates any sort of lack of interest in this behaviour change. One thing you have to remember though is that if the unsubscribe packet is sent, the fact that the user is completely removed from the roster has nothing to do with the client that initiated the remove, in this case smack. If you are using smack on both ends you will need to configure when you recieve this unsubscribe packet what to do, whether to just remove the user (the default behavior you are seeing), or just indicating to the user that they will no longer see the removing users presence and leaving the roster entry. To do this you will need to turn off auto handling of unsubscribe packets, add a listener to handle the packets, and handle them how you desire.
I’'ve used 2 classes: 1 to delete the buddy, the other to listen for the unsubscribe packet. On receiving the unsubscribe i do a println and do nothing about it. Yet the roster ends up with nobody.
public class BuddyDeleteMe {
public static void main(String[] args) throws Exception {
XMPPConnection.DEBUG_ENABLED = true;
Roster.setDefaultSubscriptionMode(Roster.SUBSCRIPTION_MANUAL);
XMPPConnection con = new XMPPConnection(new ConnectionConfiguration("cube", 5222));
con.login("e", "e");
// add listener to handle unsubscribe packet
UnsubscribeHandler unsubscribeHandler = new UnsubscribeHandler();
con.addPacketListener(unsubscribeHandler, unsubscribeHandler);
// get notified about roster events
Roster roster = con.getRoster();
roster.addRosterListener(new RosterHandler());
printRoster(roster);
// wait 20 seconds for MeDeleteBuddy.
Thread.sleep(20*1000); printRoster(roster);
con.close();
}
private static void printRoster(Roster roster) {
System.out.print("> Roster [");
Iterator it = roster.getEntries();
while (it.hasNext()) {
System.out.print(" " + it.next() + " ");
}
System.out.println("]");
}
static class UnsubscribeHandler implements PacketFilter, PacketListener { // Check that packet is an unsubscribe presence
public boolean accept(Packet packet) {
if (packet instanceof Presence) {
Presence presence = (Presence)packet;
if (presence.getType() == Presence.Type.UNSUBSCRIBE)
return true;
} return false;
} public void processPacket(Packet packet) {
System.out.println("> Received unsubscribe packet. Doing nothing about it.");
// Do nothing.
}
} static class RosterHandler implements RosterListener {
public void entriesAdded(Collection arg0) {
// TODO Auto-generated method stub
}
public void entriesDeleted(Collection entries) {
System.out.println(">> Entries Deleted " + entries);
}
public void entriesUpdated(Collection arg0) {
// TODO Auto-generated method stub
}
public void presenceChanged(String changer) {
System.out.println(">> Presence Changed: " + changer);
}
}
} public class MeDeleteBuddy {
public static void main(String[] args) throws Exception {
XMPPConnection.DEBUG_ENABLED = true;
Roster.setDefaultSubscriptionMode(Roster.SUBSCRIPTION_MANUAL);
XMPPConnection con = new XMPPConnection(new ConnectionConfiguration("cube", 5222));
con.login("k", "k");
Roster roster = con.getRoster();
printRoster(roster);
roster.removeEntry(roster.getEntry("e@cube"));
printRoster(roster); con.close();
}
private static void printRoster(Roster roster) {
System.out.print("> Roster [");
Iterator it = roster.getEntries();
while (it.hasNext()) {
System.out.print(" " + it.next() + " ");
}
System.out.println("]");
}
}