/******************************************************************************* * Copyright © 2012-2015 eBay Software Foundation * This program is dual licensed under the MIT and Apache 2.0 licenses. * Please see LICENSE for more information. *******************************************************************************/ package com.ebay.jetstream.util; import java.util.Iterator; import java.util.Map.Entry; import java.util.Set; import java.util.StringTokenizer; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; /** * * * @author shmurthy@ebay.com * @version 1.0 * * */ public class TimeSlotHashMap<T> extends TimerTask { public static class Key { public static Key newKey() { Key key = new Key(); key.setGuid(GuidGenerator.gen()); return key; } public static Key newKey(long id) { Key key = new Key(); key.setGuid(id); return key; } public static Key newKey(String id) { Key key = new Key(); StringTokenizer st = new StringTokenizer(id, "-"); if (st.countTokens() != 2) return null; try { key.setTimeSlot(Integer.parseInt(st.nextToken())); key.setGuid(Long.parseLong(st.nextToken())); } catch (Throwable t) { return null; } return key; } private int m_timeSlot = 0; private long m_guid; /* * (non-Javadoc) * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj == null) return false; if (!(obj instanceof Key)) return false; Key key = (Key) obj; if (m_guid != key.m_guid) return false; if (m_timeSlot != key.m_timeSlot) return false; return true; } /** * @return the guid */ public long getGuid() { return m_guid; } /** * @return the timeSlot */ private int getTimeSlot() { return m_timeSlot; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (int) (m_guid ^ (m_guid >>> 32)); result = prime * result + m_timeSlot; return result; } /** * @param guid * the guid to set */ public void setGuid(long guid) { m_guid = guid; } /** * @param timeSlot * the timeSlot to set */ private void setTimeSlot(int timeSlot) { m_timeSlot = timeSlot; } @Override public String toString() { StringBuffer buf = new StringBuffer(); buf.append(getTimeSlot()); buf.append("-"); buf.append(getGuid()); return buf.toString(); } } public interface Listener { public void onTimeout(Object obj); } final static int NO_DELAY = 0; public static Key genKey() { return Key.newKey(); } private int schedPeriod = 10000; private int m_numSlots = 6; private AtomicInteger m_slot = new AtomicInteger(0); private CopyOnWriteArrayList<ConcurrentHashMap<Key, T>> m_timeslotDB = new CopyOnWriteArrayList<ConcurrentHashMap<Key, T>>(); // key private final AtomicBoolean m_initialized = new AtomicBoolean(false); private Listener m_listener = null; public TimeSlotHashMap(Timer timer, int numTimeSlotsPerMinute, Listener listener) { m_numSlots = numTimeSlotsPerMinute; schedPeriod = 60000 / numTimeSlotsPerMinute; m_listener = listener; m_timeslotDB = new CopyOnWriteArrayList<ConcurrentHashMap<Key, T>>(); for (int i = 0; i < m_numSlots; i++) { m_timeslotDB.add(new ConcurrentHashMap<Key, T>()); } timer.scheduleAtFixedRate(this, NO_DELAY, schedPeriod); m_initialized.set(true); } /** * */ private void advance() { int prevSlot = m_slot.get(); if (prevSlot == m_numSlots-1) m_slot.set(0); else m_slot.addAndGet(1); int gcSlot = 0; if (prevSlot == 0) gcSlot = m_numSlots - 1; else gcSlot = prevSlot - 1; gc(gcSlot); } /** * */ public void destroy() { cancel(); } /** * @param slot */ private void gc(int slot) { ConcurrentHashMap<Key, T> objCache = m_timeslotDB.get(slot); Set<Entry<Key, T>> objs = objCache.entrySet(); Iterator<Entry<Key, T>> itr = objs.iterator(); while (itr.hasNext()) { Entry<Key, T> entry = itr.next(); Object obj = entry.getValue(); if (m_listener != null) m_listener.onTimeout(obj); } objCache.clear(); } /** * @param id * @return */ public Object get(Key key) { return m_timeslotDB.get(key.getTimeSlot()).get(key); } /** * @return */ public int getCurSlot() { return m_slot.get(); } /** * @param id * @param obj */ public void put(Key key, T object) { int curSlot = m_slot.get(); key.setTimeSlot(curSlot); m_timeslotDB.get(curSlot).put(key, object); } /** * @param dispid */ public T remove(Key key) { ConcurrentHashMap<Key, T> objCache = m_timeslotDB.get(key.getTimeSlot()); T obj = objCache.remove(key); return obj; } /* * (non-Javadoc) * * @see java.util.TimerTask#run() */ @Override public void run() { if (m_initialized.get()) advance(); } }