/*
* 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);
}
}