1090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson/*
2090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * Copyright (C) 2007 Google Inc.
3090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson *
4090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
5090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * you may not use this file except in compliance with the License.
6090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * You may obtain a copy of the License at
7090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson *
8090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * http://www.apache.org/licenses/LICENSE-2.0
9090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson *
10090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * Unless required by applicable law or agreed to in writing, software
11090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
12090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * See the License for the specific language governing permissions and
14090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * limitations under the License.
15090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson */
16090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
17090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilsonpackage com.google.common.base;
18090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
19090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilsonimport java.io.FileNotFoundException;
20090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilsonimport java.io.IOException;
21090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilsonimport java.lang.ref.Reference;
22090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilsonimport java.lang.ref.ReferenceQueue;
23090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilsonimport java.lang.reflect.Method;
24090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilsonimport java.net.URL;
25090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilsonimport java.net.URLClassLoader;
26090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilsonimport java.util.logging.Level;
27090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilsonimport java.util.logging.Logger;
28090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
29090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson/**
30090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * A reference queue with an associated background thread that dequeues
31090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * references and invokes {@link FinalizableReference#finalizeReferent()} on
32090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * them.
33090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson *
34090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * <p>Keep a strong reference to this object until all of the associated
35090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * referents have been finalized. If this object is garbage collected earlier,
36090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * the backing thread will not invoke {@code finalizeReferent()} on the
37090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * remaining references.
38090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson *
39090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * @author Bob Lee
40bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * @since 2010.01.04 <b>stable</b> (imported from Google Collections Library)
41090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson */
42090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilsonpublic class FinalizableReferenceQueue {
43090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
44090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /*
45090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * The Finalizer thread keeps a phantom reference to this object. When the
46090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * client (ReferenceMap, for example) no longer has a strong reference to
47090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * this object, the garbage collector will reclaim it and enqueue the
48090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * phantom reference. The enqueued reference will trigger the Finalizer to
49090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * stop.
50090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
51090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * If this library is loaded in the system class loader,
52090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * FinalizableReferenceQueue can load Finalizer directly with no problems.
53090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
54090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * If this library is loaded in an application class loader, it's important
55090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * that Finalizer not have a strong reference back to the class loader.
56090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Otherwise, you could have a graph like this:
57090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
58090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Finalizer Thread
59090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *   runs instance of -> Finalizer.class
60090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *     loaded by -> Application class loader
61090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *       which loaded -> ReferenceMap.class
62090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *         which has a static -> FinalizableReferenceQueue instance
63090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
64090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Even if no other references to classes from the application class loader
65090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * remain, the Finalizer thread keeps an indirect strong reference to the
66090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * queue in ReferenceMap, which keeps the Finalizer running, and as a result,
67090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * the application class loader can never be reclaimed.
68090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
69090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * This means that dynamically loaded web applications and OSGi bundles can't
70090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * be unloaded.
71090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
72090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * If the library is loaded in an application class loader, we try to break
73090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * the cycle by loading Finalizer in its own independent class loader:
74090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
75090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * System class loader
76090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *   -> Application class loader
77090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *     -> ReferenceMap
78090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *     -> FinalizableReferenceQueue
79090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *     -> etc.
80090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *   -> Decoupled class loader
81090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *     -> Finalizer
82090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
83090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Now, Finalizer no longer keeps an indirect strong reference to the
84090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * static FinalizableReferenceQueue field in ReferenceMap. The application
85090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * class loader can be reclaimed at which point the Finalizer thread will
86090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * stop and its decoupled class loader can also be reclaimed.
87090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
88090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * If any of this fails along the way, we fall back to loading Finalizer
89090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * directly in the application class loader.
90090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
91090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
92090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  private static final Logger logger
93090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      = Logger.getLogger(FinalizableReferenceQueue.class.getName());
94090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
95090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  private static final String FINALIZER_CLASS_NAME
96090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      = "com.google.common.base.internal.Finalizer";
97090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
98090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /** Reference to Finalizer.startFinalizer(). */
99090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  private static final Method startFinalizer;
100090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  static {
101090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    Class<?> finalizer = loadFinalizer(
102090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        new SystemLoader(), new DecoupledLoader(), new DirectLoader());
103090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    startFinalizer = getStartFinalizer(finalizer);
104090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
105090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
106090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
107090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * The actual reference queue that our background thread will poll.
108090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
109090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  final ReferenceQueue<Object> queue;
110090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
111090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
112090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Whether or not the background thread started successfully.
113090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
114090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  final boolean threadStarted;
115090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
116090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
117090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Constructs a new queue.
118090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
119090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  @SuppressWarnings("unchecked")
120090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  public FinalizableReferenceQueue() {
121090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    // We could start the finalizer lazily, but I'd rather it blow up early.
122090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    ReferenceQueue<Object> queue;
123090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    boolean threadStarted = false;
124090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    try {
125090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      queue = (ReferenceQueue<Object>) startFinalizer.invoke(null,
126090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson          FinalizableReference.class, this);
127090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      threadStarted = true;
128090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    } catch (IllegalAccessException e) {
129090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      // Finalizer.startFinalizer() is public.
130090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      throw new AssertionError(e);
131090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    } catch (Throwable t) {
132090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      logger.log(Level.INFO, "Failed to start reference finalizer thread."
133090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson          + " Reference cleanup will only occur when new references are"
134090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson          + " created.", t);
135090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      queue = new ReferenceQueue<Object>();
136090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
137090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
138090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    this.queue = queue;
139090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    this.threadStarted = threadStarted;
140090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
141090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
142090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
143090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Repeatedly dequeues references from the queue and invokes
144090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * {@link FinalizableReference#finalizeReferent()} on them until the queue
145090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * is empty. This method is a no-op if the background thread was created
146090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * successfully.
147090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
148090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  void cleanUp() {
149090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    if (threadStarted) {
150090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      return;
151090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
152090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
153090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    Reference<?> reference;
154090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    while ((reference = queue.poll()) != null) {
155090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      /*
156090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson       * This is for the benefit of phantom references. Weak and soft
157090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson       * references will have already been cleared by this point.
158090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson       */
159090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      reference.clear();
160090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      try {
161090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        ((FinalizableReference) reference).finalizeReferent();
162090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      } catch (Throwable t) {
163090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
164090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
165090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
166090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
167090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
168090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
169090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Iterates through the given loaders until it finds one that can load
170090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Finalizer.
171090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
172090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * @return Finalizer.class
173090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
174090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  private static Class<?> loadFinalizer(FinalizerLoader... loaders) {
175090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    for (FinalizerLoader loader : loaders) {
176090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      Class<?> finalizer = loader.loadFinalizer();
177090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      if (finalizer != null) {
178090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        return finalizer;
179090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
180090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
181090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
182090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    throw new AssertionError();
183090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
184090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
185090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
186090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Loads Finalizer.class.
187090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
188090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  interface FinalizerLoader {
189090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
190090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    /**
191090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * Returns Finalizer.class or null if this loader shouldn't or can't load
192090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * it.
193090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     *
194090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * @throws SecurityException if we don't have the appropriate privileges
195090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     */
196090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    Class<?> loadFinalizer();
197090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
198090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
199090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
200090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Tries to load Finalizer from the system class loader. If Finalizer is
201090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * in the system class path, we needn't create a separate loader.
202090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
203090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  static class SystemLoader implements FinalizerLoader {
204090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    public Class<?> loadFinalizer() {
205090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      ClassLoader systemLoader;
206090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      try {
207090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        systemLoader = ClassLoader.getSystemClassLoader();
208090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      } catch (SecurityException e) {
209090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        logger.info("Not allowed to access system class loader.");
210090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        return null;
211090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
212090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      if (systemLoader != null) {
213090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        try {
214090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson          return systemLoader.loadClass(FINALIZER_CLASS_NAME);
215090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        } catch (ClassNotFoundException e) {
216090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson          // Ignore. Finalizer is simply in a child class loader.
217090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson          return null;
218090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        }
219090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      } else {
220090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        return null;
221090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
222090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
223090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
224090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
225090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
226090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Try to load Finalizer in its own class loader. If Finalizer's thread
227090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * had a direct reference to our class loader (which could be that of
228090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * a dynamically loaded web application or OSGi bundle), it would prevent
229090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * our class loader from getting garbage collected.
230090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
231090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  static class DecoupledLoader implements FinalizerLoader {
232090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
233090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    private static final String LOADING_ERROR = "Could not load Finalizer in"
234090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        + " its own class loader. Loading Finalizer in the current class loader"
235090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        + " instead. As a result, you will not be able to garbage collect this"
236090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        + " class loader. To support reclaiming this class loader, either"
237090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        + " resolve the underlying issue, or move Google Collections to your"
238090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        + " system class path.";
239090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
240090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    public Class<?> loadFinalizer() {
241090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      try {
242090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        /*
243090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         * We use URLClassLoader because it's the only concrete class loader
244090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         * implementation in the JDK. If we used our own ClassLoader subclass,
245090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         * Finalizer would indirectly reference this class loader:
246090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         *
247090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         * Finalizer.class ->
248090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         *   CustomClassLoader ->
249090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         *     CustomClassLoader.class ->
250090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         *       This class loader
251090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         *
252090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         * System class loader will (and must) be the parent.
253090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         */
254090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        ClassLoader finalizerLoader = newLoader(getBaseUrl());
255090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        return finalizerLoader.loadClass(FINALIZER_CLASS_NAME);
256090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      } catch (Exception e) {
257090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        logger.log(Level.WARNING, LOADING_ERROR, e);
258090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        return null;
259090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
260090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
261090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
262090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    /**
263090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * Gets URL for base of path containing Finalizer.class.
264090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     */
265090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    URL getBaseUrl() throws IOException {
266090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      // Find URL pointing to Finalizer.class file.
267090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      String finalizerPath = FINALIZER_CLASS_NAME.replace('.', '/') + ".class";
268090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      URL finalizerUrl = getClass().getClassLoader().getResource(finalizerPath);
269090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      if (finalizerUrl == null) {
270090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        throw new FileNotFoundException(finalizerPath);
271090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
272090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
273090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      // Find URL pointing to base of class path.
274090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      String urlString = finalizerUrl.toString();
275090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      if (!urlString.endsWith(finalizerPath)) {
276090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        throw new IOException("Unsupported path style: " + urlString);
277090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
278090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      urlString = urlString.substring(0,
279090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson          urlString.length() - finalizerPath.length());
280090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      return new URL(finalizerUrl, urlString);
281090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
282090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
283090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    /** Creates a class loader with the given base URL as its classpath. */
284090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    URLClassLoader newLoader(URL base) {
285090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      return new URLClassLoader(new URL[] { base });
286090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
287090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
288090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
289090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
290090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Loads Finalizer directly using the current class loader. We won't be
291090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * able to garbage collect this class loader, but at least the world
292090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * doesn't end.
293090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
294090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  static class DirectLoader implements FinalizerLoader {
295090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    public Class<?> loadFinalizer() {
296090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      try {
297090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        return Class.forName(FINALIZER_CLASS_NAME);
298090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      } catch (ClassNotFoundException e) {
299090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        throw new AssertionError(e);
300090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
301090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
302090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
303090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
304090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
305090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Looks up Finalizer.startFinalizer().
306090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
307090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  static Method getStartFinalizer(Class<?> finalizer) {
308090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    try {
309090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      return finalizer.getMethod("startFinalizer", Class.class, Object.class);
310090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    } catch (NoSuchMethodException e) {
311090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      throw new AssertionError(e);
312090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
313090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
314090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson}
315