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}