package org.flowcomputing.commons.resgc;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Collect resource holders that need to be collected after utilization. It
 * manages to release resource in explicit way and we have change to be notified
 * when its resource is going to be release. In addition, It embedded mechanism
 * to allow resource to be forced release in advance.
 * 
 * @author wg
 *
 * @param <HOLDER>
 *            holder type for any kind of resource.
 * 
 * @param <MRES>
 *            resource type to be holden.
 */
public class ResCollector<HOLDER extends Holder<MRES, HOLDER>, MRES> implements
		Collector<HOLDER, MRES> {

	public final static long WAITRECLAIMTIMEOUT = 2000;

	private final ReferenceQueue<HOLDER> refque = new ReferenceQueue<HOLDER>();
	private final Map<Reference<HOLDER>, MRES> refmap = 
			new ConcurrentHashMap<Reference<HOLDER>, MRES>();

	private ResReclaim<MRES> m_reclaimer;
	private Thread m_collector;
	private AtomicLong descnt = new AtomicLong(0L);
	
	private volatile boolean m_stopped = false;

	/**
	 * constructor to accept a resource reclaimer that is used to actual its
	 * resource when needed.
	 * 
	 * @param reclaimer 
	 *          a reclaimer object for managed resource reclaim.
	 */
	public ResCollector(ResReclaim<MRES> reclaimer) {
		m_reclaimer = reclaimer;
		m_collector = new Thread() {
			public void run() {
				while (!m_stopped) {
					try {
						Reference<? extends HOLDER> ref = refque.remove();
						destroyRes(ref);
						descnt.getAndIncrement();
					} catch (InterruptedException ex) {
						break;
					}
				}
			}
		};
		m_collector.setDaemon(true);
		m_collector.start();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.flowcomputing.commons.resgc.Collector#register(org.flowcomputing.
	 * commons.resgc.Holder, java.lang.Object)
	 */
	@Override
	public void register(HOLDER holder) {
		PhantomReference<HOLDER> pref = new PhantomReference<HOLDER>(holder,
				refque);
		refmap.put(pref, holder.get());
		holder.setCollector(this);
		holder.setRefId(pref);
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.flowcomputing.commons.resgc.Collector#unregister(org.flowcomputing.
	 * commons.resgc.Holder)
	 */
	@Override
	public void unregister(HOLDER holder) {
		refmap.remove(holder.getRefId());
		holder.setRefId(null);
		holder.setCollector(null);
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.flowcomputing.commons.resgc.Collector#unregister()
	 */
	@Override
	public void unregister(Reference<HOLDER> ref) {
		refmap.remove(ref);
	}

	/**
	 * destroy a specified holden resource.
	 * 
	 * @param ref
	 *            a specified reference that is referring a holder
	 */
	@Override
	public void destroyRes(Reference<? extends HOLDER> ref) {
		MRES mres = refmap.remove(ref);
		m_reclaimer.reclaim(mres);
	}

	@Override
	public void destroyRes(MRES mres) {
		m_reclaimer.reclaim(mres);
	}

	private void forceGC(long timeout) {
		System.gc();
		try {
			Thread.sleep(timeout);
		} catch (Exception ex) {
		}
	}

	public void waitReclaimCoolDown() {
		do {
			forceGC(WAITRECLAIMTIMEOUT);
		} while (0L != descnt.getAndSet(0L));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.flowcomputing.commons.resgc.Collector#close()
	 */
	@Override
	public void close() {
		waitReclaimCoolDown();
		m_stopped = true;
		refmap.clear();
	}

}

@@ -3178,16 +3178,60 @@
 %09%09%7D%0A%09%7D%0A%0A
+%09/**%0A%09 * wait for reclaim termination.%0A%09 */%0A
 %09public 

add a comment for waitReclaimCoolDown()