    001package com.hammurapi.common;
    002
    003import java.lang.ref.Reference;
    004import java.lang.ref.ReferenceQueue;
    005import java.lang.ref.WeakReference;
    006import java.util.ArrayList;
    007import java.util.Iterator;
    008import java.util.LinkedList;
    009import java.util.List;
    010
    011/**
    012 * Maintains weak references to objects. Removes cleared entries in
    013 * getIdentity() method.
    014 * @author Pavel Vlasov
    015 *
    016 */
    017public class WeakIdentityManager implements IdentityManager<Object> {
    018
    019        private static ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
    020
    021        private static long[] counter = {0};
    022
    023        private static class Entry extends WeakReference<Object> {
    024                String identity;
    025
    026                Entry(Object obj) {
    027                        super(obj, rq);
    028                        identity="wim_"+Long.toString(counter[0]++, Character.MAX_RADIX);
    029                }
    030        }
    031
    032        private static List<IdentityExtractor<?>> extractors = new ArrayList<IdentityExtractor<?>>();
    033
    034        static {
    035                Thread reaper = new Thread("Removes cleared references") {
    036
    037                        @Override
    038                        public void run() {
    039                                while (true) {
    040                                        try {
    041                                                Reference<? extends Object> removed = rq.remove();
    042                                                if (removed instanceof Entry) {
    043                                                        synchronized (counter) {
    044                                                                Iterator<Entry> it = entries.iterator();
    045                                                                while (it.hasNext()) {
    046                                                                        Entry entry = it.next();
    047                                                                        if (removed==entry) {
    048                                                                                it.remove();
    049                                                                                break;
    050                                                                        }
    051                                                                }
    052                                                        }
    053                                                }
    054                                        } catch (InterruptedException e) {
    055                                                return; // Time to exit.
    056                                        }
    057                                }
    058                        }
    059                };
    060
    061                reaper.setDaemon(true);
    062                reaper.start();
    063        }
    064
    065        private static LinkedList<Entry> entries = new LinkedList<Entry>();
    066
    067        @SuppressWarnings("unchecked")
    068        public Object getIdentity(Object obj) {
    069                if (obj==null) {
    070                        return null;
    071                }
    072                if (obj instanceof Identifiable) {
    073                        return ((Identifiable<Object>) obj).getIdentity();
    074                }
    075                for (IdentityExtractor<?> ie: extractors) {
    076                        Object identity = ie.getIdentity(obj);
    077                        if (identity!=null) {
    078                                return identity;
    079                        }
    080                }
    081                synchronized (counter) {
    082                        Iterator<Entry> it = entries.iterator();
    083                        while (it.hasNext()) {
    084                                Entry entry = it.next();
    085                                Object eObj = entry.get();
    086                                if (eObj==null) {
    087                                        it.remove();
    088                                } else if (eObj==obj) {
    089                                        return entry.identity;
    090                                }
    091                        }
    092                        Entry entry = new Entry(obj);
    093                        entries.add(entry);
    094                        return entry.identity;
    095                }
    096        }
    097
    098}