package com.rayo.server; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import com.voxeo.logging.Loggerf; import com.voxeo.servlet.xmpp.JID; /** * <p>JID registry keeps a map of JIDs and origin domains mapped to call ids. This way we can obtain the JID * and origin domain mapped to any call id without depending on the call's lifecycle. This way even if the * call has been completed and wiped out from the system, you can still access to the JID that was mapped * to the call.</p> * * <p>This class uses a thread to clean out any garbage that may have left due to calls not being finished up * properly</p> * * @author martin * */ public class JIDRegistry { Loggerf log = Loggerf.getLogger(JIDRegistry.class); // 5 minutes timeout for cleaning up calls that have already finished long purgeTimeout = 5 * 60 * 1000; // Purging task will run each 30 minutes long purgingTaskInterval = 30 * 60 * 1000; private Map<String, JIDEntry> jids = new ConcurrentHashMap<String, JIDEntry>(); private Map<String, List<String>> callsByJid = new ConcurrentHashMap<String, List<String>>(); private ReadWriteLock callsByJidLock = new ReentrantReadWriteLock(); private List<JIDEntry> toPurge = new ArrayList<JIDEntry>(); private Timer timer; public JIDRegistry(long purgingTaskInterval, long purgeTimeout) { setPurgeTimeout(purgeTimeout); setPurgingTaskInterval(purgingTaskInterval); init(); } public JIDRegistry() { init(); } private void init() { TimerTask task = new TimerTask() { @Override public void run() { log.debug("Starting call purging task"); List<JIDEntry> list = new ArrayList<JIDEntry>(); list.addAll(toPurge); Iterator<JIDEntry> it = list.iterator(); while (it.hasNext()) { JIDEntry entry = it.next(); if (System.currentTimeMillis() - entry.time > purgeTimeout) { log.debug("Purging call with mapped jid %s from the JID registry", entry.jid); jids.remove(entry.key); toPurge.remove(entry); } } } }; timer = new Timer(); timer.scheduleAtFixedRate(task, new Date(System.currentTimeMillis() + purgingTaskInterval), purgingTaskInterval); } public JID getJID(String callId) { if (callId == null) { return null; } JIDEntry entry = jids.get(callId); if (entry == null) { return null; } return entry.jid; } public void put(String callId, JID jid) { jids.put(callId, new JIDEntry(jid, -1L, callId)); callsByJidLock.writeLock().lock(); List<String> calls = callsByJid.get(jid.getBareJID().toString()); if (calls == null) { calls = new ArrayList<String>(); callsByJid.put(jid.getBareJID().toString(), calls); } calls.add(callId); callsByJidLock.writeLock().unlock(); } public void remove(String callId) { log.debug("Removing call id %s from the JID registry", callId); JIDEntry jid = jids.get(callId); if (jid != null) { jid.time = System.currentTimeMillis(); toPurge.add(jid); callsByJidLock.writeLock().lock(); List<String> calls = callsByJid.get(jid.jid.getBareJID().toString()); if (calls != null) { calls.remove(callId); } callsByJidLock.writeLock().unlock(); } } public List<String> getCallsByJID(JID jid) { if (jid == null) { log.warn("Trying to find calls for a null JID"); return new ArrayList<String>(); } callsByJidLock.readLock().lock(); try { List<String> values = callsByJid.get(jid.getBareJID().toString()); if (values == null) { return Collections.emptyList(); } else { return new ArrayList<String>(values); } } finally { callsByJidLock.readLock().unlock(); } } public void setPurgingTaskInterval(long interval) { purgingTaskInterval = interval; } public void setPurgeTimeout(long timeout) { purgeTimeout = timeout; } public int size() { return jids.size(); } public void shutdown() { jids.clear(); timer.cancel(); } } class JIDEntry { String key; JID jid; long time; JIDEntry(JID jid, long time, String key) { this.key = key; this.jid = jid; this.time = time; } }