Java References
by Jonathan Amsterdam

Listing One
import java.lang.ref.*;

public abstract class SoftObject {
    private SoftReference ref = new SoftReference(null);
    public Object get() throws Exception {
        Object result = ref.get();
        if (result == null) {
            result = retrieve();
            ref = new SoftReference(result);
        }
        return result;
    }
    protected abstract Object retrieve() throws Exception;
}
                

Listing Two
import java.io.*;

public class FileObject extends SoftObject {
    private String filename;
  FileObject(String fn) {
        filename = fn;
    }
    protected Object retrieve() 
            throws IOException, ClassNotFoundException {
        ObjectInputStream in = 
            new ObjectInputStream(
                new FileInputStream(filename));
        try {
            return in.readObject();
        } finally {
            in.close();
        }
    }
}


Listing Three
import java.util.*;
import java.lang.ref.*;

class Symbol {
    private String name;
    Object value;

    private static Map table = new HashMap();
    private Symbol(String nm) { name = nm; }

    String getName() { return name; }

    static Symbol intern(String name) {
        Symbol s = (Symbol) table.get(name); 
        if (s == null) {
            s = new Symbol(name);
            table.put(name, s);
        }
        return s;
    }
}
            

Listing Four
static Symbol intern(String name) {
    Reference r = (Reference) table.get(name);
    Symbol s = null;
    if (r != null)
        s = (Symbol) r.get();
    if (r == null || s == null) {
        s = new Symbol(name);
        table.put(name, new WeakReference(s));
    }
    return s;
}       


Listing Five
import java.util.*;
import java.lang.ref.*;

/** This class is for maintaining canonical objects. */
public class CanonicalTable {
    private Map map = new HashMap();
    private ReferenceQueue queue = new ReferenceQueue();
    private Factory factory;

    public interface Factory {
        public Object create(Object key);
    }
    public CanonicalTable() {}
    public CanonicalTable(Factory f) {
        factory = f;
    }
    public synchronized Object canonicalize(Object key) {
        return canonicalize(key, null);
    }
    public synchronized Object canonicalize(Object key, Object o) {
        cleanup();
        Object value = map.get(key);
        if (value != null)
            value = ((WeakReference) value).get();
        if (value != null)
            return value;
        else {
            if (o == null)
                o = factory.create(key);
            map.put(key, new WeakValue(key, o, queue));
            return o;
        } 
    }
    public synchronized Object get(Object key) {
        cleanup();
        Object value = map.get(key);
        if (value != null)
            return ((WeakReference) value).get();
        else
            return null;
    }
    private void cleanup() {
        Reference r;
        while ((r = queue.poll()) != null)
            map.remove(((WeakValue) r).key);
    }
    ////////
    private static class WeakValue extends WeakReference {
        Object key;
        WeakValue(Object k, Object o, ReferenceQueue q) {
            super(o, q);
            key = k;
        }
    }
}


Listing Six
import java.util.*;
import java.lang.ref.*;

/** A class for simplifying the use of phantom references. */
public class Cleanup {
    // Doubly linked list of CleanupReferences, with an empty header.
    private static CleanupReference list = new CleanupReference();
    private static ReferenceQueue queue = new ReferenceQueue();
    private static Thread backgroundThread = null;
    private static ArrayList exceptions = null;

    public interface Handler {
        public void cleanup() throws Exception;
    }
    /** Register a cleanup handler with an object. */
    public static void register(Object o, Handler h) {
        synchronized (list) {
            CleanupReference r = new CleanupReference(o, h);
            r.linkAfter(list);
        }
    }
    /** Perform all pending cleanup operations. */
    public static void doPending() throws Exception {
        Reference r;
        while ((r = queue.poll()) != null)
            ((CleanupReference) r).cleanup();
    }
    /** Start a thread to do cleanup in the background. */
    public static synchronized void startBackground() {
        if (backgroundThread != null)
            return; // already running
        backgroundThread = new Thread(new Runnable() {
            public void run() {
                while (!Thread.interrupted()) {
                    try {
                      CleanupReference r = (CleanupReference) queue.remove();
                        r.cleanup();
                    } catch (InterruptedException e) {
                        // do nothing; loop will end
                    } catch (Exception e) {
                        addException(e);
                    }
                }
            }
        });
        backgroundThread.setPriority(Thread.MIN_PRIORITY);
        backgroundThread.start();
    }
    /** Stop the background cleanup thread. */
    public static synchronized void stopBackground() {
        if (backgroundThread != null) {
            backgroundThread.interrupt();
            backgroundThread = null;
        }
    }
    /** Get a list of all exceptions generated by cleanup 
        calls in the background thread. */
    public static synchronized List getExceptions() {
        ArrayList result = exceptions;
        exceptions = null;
        return result;
    }
    private static synchronized 
    void addException(Exception e) {
        if (exceptions == null)
            exceptions = new ArrayList();
        exceptions.add(e);
    }
    ////////////////////////////////////////////
    private static class CleanupReference 
                extends PhantomReference {
        private Handler handler;
        private CleanupReference next, prev;

        CleanupReference() {   // Used only for head of linked list.
            // Queue is never garbage; ensures 
            // no enqueuing.
            super(queue, queue);
            next = prev = this;
        }
        CleanupReference(Object o, Handler h) {
            super(o, queue);
            handler = h;
        }
        void linkAfter(CleanupReference c) {
            this.prev = c;
            this.next = c.next;
            c.next.prev = this;
            c.next = this;
        }
        void cleanup() throws Exception {
            try {
                handler.cleanup();
            } finally {
                this.clear();
                synchronized (list) {  // unlink
                    this.prev.next = this.next;
                    this.next.prev = this.prev;
                }
            }
        }
    }
}
            
         
        
        
        

5


