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
-~----------~----~----~----~------~----~------~--~---

Reply via email to