/* * Copyright 2006-2010 Daniel Henninger. All rights reserved. * * This software is published under the terms of the GNU Public License (GPL), * a copy of which is included in this distribution. */ package net.sf.kraken.roster; import net.sf.kraken.session.TransportSession; import net.sf.kraken.type.PresenceType; import org.xmpp.packet.JID; import org.xmpp.packet.Packet; import org.jivesoftware.util.NotFoundException; import org.jivesoftware.openfire.user.UserNotFoundException; import org.apache.log4j.Logger; import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; import java.lang.ref.WeakReference; /** * Manager for all legacy buddies on a legacy service. * * This stores an entire "roster" worth of contacts associated with a particular session. * * @author Daniel Henninger */ public class TransportBuddyManager<B extends TransportBuddy> { static Logger Log = Logger.getLogger(TransportBuddyManager.class); /** * Creates the transport buddy manager instance and initializes. * * @param session Transport session associated with this buddy manager. */ public TransportBuddyManager(TransportSession<B> session) { this.sessionRef = new WeakReference<TransportSession<B>>(session); } private final ConcurrentHashMap<JID,B> buddies = new ConcurrentHashMap<JID,B>(); private final ConcurrentHashMap<JID,PresenceType> pendingPresences = new ConcurrentHashMap<JID,PresenceType>(); private final ConcurrentHashMap<JID,String> pendingVerboseStatuses = new ConcurrentHashMap<JID,String>(); private WeakReference<TransportSession<B>> sessionRef = null; private boolean isActive = false; public TransportSession<B> getSession() { return sessionRef.get(); } /** * Has the buddy manager been activated? * * @return True or false if the buddy manager has been activated. */ public boolean isActivated() { return isActive; } /** * Activates the full functionality of the buddy manager and sends available presences to client. */ public synchronized void activate() { for (JID jid : pendingPresences.keySet()) { if (pendingVerboseStatuses.containsKey(jid)) { try { B buddy = getBuddy(jid); buddy.setPresenceAndStatus(pendingPresences.get(jid), pendingVerboseStatuses.get(jid)); } catch (NotFoundException e) { // Alrighty then.... } pendingVerboseStatuses.remove(jid); } else { try { B buddy = getBuddy(jid); buddy.setPresence(pendingPresences.get(jid)); } catch (NotFoundException e) { // Alrighty then.... } } } for (JID jid : pendingVerboseStatuses.keySet()) { try { B buddy = getBuddy(jid); buddy.setVerboseStatus(pendingVerboseStatuses.get(jid)); } catch (NotFoundException e) { // Alrighty then.... } } pendingPresences.clear(); pendingVerboseStatuses.clear(); isActive = true; sendAllAvailablePresences(getSession().getJID()); } /** * Stores a status setting to be recorded after the buddy manager has been activated. * This is typically used when status's arrive before the buddy list is activated. * * @param jid JID of the contact to set status for later. * @param presence Presence of contact. * @param status verbose status string of contact. */ public synchronized void storePendingStatus(JID jid, PresenceType presence, String status) { if (!isActivated()) { pendingPresences.put(jid, presence); pendingVerboseStatuses.put(jid, status); } else { try { B buddy = getBuddy(jid); buddy.setPresenceAndStatus(presence, status); } catch (NotFoundException e) { // Alrighty then.... } } } /** * Resets (clears) the list of buddies assigned to the buddy manager. */ public void resetBuddies() { buddies.clear(); } /** * Retrieve the buddy instance for a given JID. * * @param jid JID of the buddy to be retrieved. * @throws NotFoundException if the given jid is not found. * @return TransportBuddy instance requested. */ public B getBuddy(JID jid) throws NotFoundException { B buddy = buddies.get(jid); if (buddy == null) { throw new NotFoundException("Could not find buddy requested."); } return buddy; } /** * Retrieve the buddy instance for a given user. * * @param username Username of the buddy to be retrieved. * @throws NotFoundException if the given username is not found. * @return TransportBuddy instance requested. */ // Disabling this because it can cause confusion in one's attempt to match a buddy. // public B getBuddy(String username) throws NotFoundException { // B buddy = buddies.get(username.toLowerCase()); // if (buddy == null) { // throw new NotFoundException("Could not find buddy requested."); // } // return buddy; // } /** * Stores a new buddy instance in the buddy list. * * @param buddy TransportBuddy associated with the username. */ public void storeBuddy(B buddy) { if (!buddies.containsKey(buddy.jid)) { Log.debug("("+getSession().getTransport().getType().toString().toUpperCase()+") Storing new buddy: "+buddy); buddies.put(buddy.jid, buddy); if (isActivated()) { getSession().lockRoster(buddy.getJID().toString()); try { getSession().getTransport().addOrUpdateRosterItem(getSession().getJID(), buddy.getJID(), buddy.getNickname(), buddy.getGroups()); } catch (UserNotFoundException e) { Log.error("TransportBuddyManager: Unable to find roster when adding contact."); } finally { getSession().unlockRoster(buddy.getJID().toString()); } } } else { Log.debug("("+getSession().getTransport().getType().toString().toUpperCase()+") Replacing buddy: "+buddy); buddies.put(buddy.jid, buddy); } } /** * Removes a buddy instance from the buddy list. * * @param username buddy to be removed. */ public void removeBuddy(String username) { B buddy = buddies.remove(getSession().getTransport().convertIDToJID(username)); if (buddy != null && isActivated()) { Log.debug("TransportBuddyManager: Triggering contact removal for "+buddy); getSession().removeContact(buddy); } } /** * Retrieves a collection of all buddies. * * @return List of buddies. */ public Collection<B> getBuddies() { return buddies.values(); } /** * Conduit for sending presence packets, so that packets can be held at bay temporarily. * * @param packet Packet to send. */ public void sendPacket(Packet packet) { if (isActivated()) { getSession().getTransport().sendPacket(packet); } } /** * Sends all presences regardless of status. * * Typically used to send initial presences. * * @param to JID to send presence updates to. */ public void sendAllPresences(JID to) { for (B buddy : buddies.values()) { buddy.sendPresence(to); } } /** * Sends all presences that are not unavailable. * * Typically used to send initial presences. * * @param to JID to send presence updates to. */ public void sendAllAvailablePresences(JID to) { for (B buddy : buddies.values()) { buddy.sendPresenceIfAvailable(to); } } /** * Sends all presences that are not unavailable. * * Typically used to send initial presences. * * @param to JID to send presence updates to. */ public void sendOfflineForAllAvailablePresences(JID to) { for (B buddy : buddies.values()) { buddy.sendOfflinePresenceIfAvailable(to); } } public boolean hasBuddy(JID jid) { return buddies.containsKey(jid); } }