OK, I came up with an implementation of FinalizableReferenceQueue (pasted below) that won't create a strong reference from a thread to your application class loader, but I really don't think we should commit this.
If you want to support web application re-deploying, it will be much simpler and safer to just put Guice in your system classpath. Bob /* * Copyright (C) 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.base; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.PhantomReference; import java.lang.reflect.Method; import java.lang.reflect.Field; import java.util.logging.Level; import java.util.logging.Logger; import java.io.InputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; /** * Starts a background thread that cleans up after reclaimed referents. * * <p>Most of this code goes to preventing creation of a strong reference * from the thread to this class's loader so we can support reloading in * application servers. * * @author Bob Lee */ class FinalizableReferenceQueue extends ReferenceQueue<Object> { private static final Logger logger = Logger.getLogger(FinalizableReferenceQueue.class.getName()); private static final ReferenceQueue<Object> queue = startFinalizer(); /** * Returns the singleton instance. */ public static ReferenceQueue<Object> getInstance() { return queue; } private static final String FINALIZER_CLASS_NAME = "com.google.common.base.Finalizer"; /** * Starts the finalizer thread. */ private static ReferenceQueue<Object> startFinalizer() { try { ClassLoader systemLoader = ClassLoader.getSystemClassLoader(); if (systemLoader != null) { /* * If the class is already in the system class path, don't bother * creating a new class loader. */ try { if (systemLoader.loadClass(FINALIZER_CLASS_NAME) != null) { //return startFinalizerDirectly(); } } catch (ClassNotFoundException e) { /* ignore */ } } // Read Finalizer class bytes from class loader. ClassLoader thisLoader = FinalizableReferenceQueue.class.getClassLoader(); String fileName = "/" + FINALIZER_CLASS_NAME.replace('.', '/') + ".class"; InputStream in = thisLoader.getResourceAsStream(fileName); if (in == null) { throw new IOException("Could not find " + fileName + " in class path."); } // Copy bytes into a byte[]. ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int read; while ((read = in.read(buffer)) > -1) { out.write(buffer, 0, read); } in.close(); byte[] finalizerBytes = out.toByteArray(); /* * We use URLClassLoader because it's the only concrete class loader * implementation in the JDK. If we used our own ClassLoader subclass, * Finalizer would have a reference to our custom class loader instance, * which would reference its class, which would reference the application * class loader, which we're trying to prevent. */ ClassLoader loader = new URLClassLoader(new URL[0], systemLoader); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); defineClass.setAccessible(true); Class<?> finalizer = (Class<?>) defineClass.invoke(loader, FINALIZER_CLASS_NAME, finalizerBytes, 0, finalizerBytes.length); // Give Finalizer a reference to this class so it knows when to stop. Method backReference = finalizer.getDeclaredMethod( "backReference", Class.class); backReference.setAccessible(true); backReference.invoke(null, FinalizableReferenceQueue.class); return extractQueue(finalizer); } catch (Exception e) { logger.log(Level.WARNING, "Could not load " + FINALIZER_CLASS_NAME + " in its own class loader.", e); // Fall back to loading Finalizer directly. return startFinalizerDirectly(); } } /** * Loads the finalizer in this class loader and starts it. */ private static ReferenceQueue<Object> startFinalizerDirectly() { try { return extractQueue(Class.forName(FINALIZER_CLASS_NAME)); } catch (ClassNotFoundException e) { throw new AssertionError(e); } } /** * Copies a queue reference from Finalizer into this class. */ @SuppressWarnings("unchecked") private static ReferenceQueue<Object> extractQueue(Class<?> finalizer) { try { Field queueField = finalizer.getDeclaredField("queue"); queueField.setAccessible(true); return (ReferenceQueue<Object>) queueField.get(null); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } } /** * Thread that finalizes referents. All references should implement Runnable. * * THE APPLICATION CLASS LOADER SHOULD *NOT* LOAD THIS CLASS DIRECTLY. THIS * CLASS AND ITS CLASS LOADER SHOULD NOT STRONGLY REFERENCE THE APPLICATION * CLASS LOADER. * * Doing so would create a strong reference from a thread to an application * class which would keep the garbage collector from reclaiming the * application class loader, thereby breaking reloading of applications * in application servers by leaking memory. */ class Finalizer extends Thread { private static final Logger logger = Logger.getLogger(Finalizer.class.getName()); /** * Reference queue. The application will grab a reference to this * reflectively. */ static final ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); private static PhantomReference<Class<?>> backReference; /** * Creates a reference back to the application class loader so we can find * out if it has been garbage collected. Once it has, we can safely stop * the finalizer thread. */ static void backReference(Class<?> clazz) { backReference = new PhantomReference<Class<?>>(clazz, queue); } static { // Start background thread. new Finalizer().start(); } /** Constructs a new finalizer thread. */ private Finalizer() { super(Finalizer.class.getSimpleName()); setDaemon(true); } /** * Loops continuously, pulling references off the queue and cleaning them up. */ @Override public void run() { while (true) { try { Reference<?> reference = queue.remove(); if (reference == backReference) { // The application class loader has been garbage collected. We can // stop and be collected ourselves. // Theoretically, this is the last reference in the queue. return; } cleanUp(reference); } catch (InterruptedException e) { /* ignore */ } } } /** * Cleans up a single reference. Catches and logs all throwables. */ private void cleanUp(Reference<?> reference) { try { // TODO: We really don't want to make the FinalizableXxxReference classes // implement Runnable (it would expose the finalization in the public // API!). We need to pass FinalizableReference.class into this class // (perhaps in backReference()) and then reflectively invoke // finalizeReferent(). ((Runnable) reference).run(); } catch (Throwable t) { logger.log(Level.SEVERE, "Error cleaning up after reference.", t); } } } --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "google-guice" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/google-guice?hl=en -~----------~----~----~----~------~----~------~--~---
