1090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson/*
21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2007 The Guava Authors
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/**
301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * A reference queue with an associated background thread that dequeues references and invokes
311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * {@link FinalizableReference#finalizeReferent()} on them.
32090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson *
331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <p>Keep a strong reference to this object until all of the associated referents have been
341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * finalized. If this object is garbage collected earlier, the backing thread will not invoke {@code
351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * finalizeReferent()} on the remaining references.
36090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson *
37090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * @author Bob Lee
381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @since 2.0 (imported from Google Collections Library)
39090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson */
40090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilsonpublic class FinalizableReferenceQueue {
41090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /*
421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * The Finalizer thread keeps a phantom reference to this object. When the client (for example, a
431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * map built by MapMaker) no longer has a strong reference to this object, the garbage collector
441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * will reclaim it and enqueue the phantom reference. The enqueued reference will trigger the
451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Finalizer to stop.
46090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * If this library is loaded in the system class loader, FinalizableReferenceQueue can load
481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Finalizer directly with no problems.
49090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * If this library is loaded in an application class loader, it's important that Finalizer not
511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * have a strong reference back to the class loader. Otherwise, you could have a graph like this:
52090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Finalizer Thread runs instance of -> Finalizer.class loaded by -> Application class loader
541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * which loaded -> ReferenceMap.class which has a static -> FinalizableReferenceQueue instance
55090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Even if no other references to classes from the application class loader remain, the Finalizer
571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * thread keeps an indirect strong reference to the queue in ReferenceMap, which keeps the
581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Finalizer running, and as a result, the application class loader can never be reclaimed.
59090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * This means that dynamically loaded web applications and OSGi bundles can't be unloaded.
61090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * If the library is loaded in an application class loader, we try to break the cycle by loading
631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Finalizer in its own independent class loader:
64090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * System class loader -> Application class loader -> ReferenceMap -> FinalizableReferenceQueue
661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * -> etc. -> Decoupled class loader -> Finalizer
67090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Now, Finalizer no longer keeps an indirect strong reference to the static
691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * FinalizableReferenceQueue field in ReferenceMap. The application class loader can be reclaimed
701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * at which point the Finalizer thread will stop and its decoupled class loader can also be
711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * reclaimed.
72090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * If any of this fails along the way, we fall back to loading Finalizer directly in the
741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * application class loader.
75090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
76090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static final Logger logger = Logger.getLogger(FinalizableReferenceQueue.class.getName());
78090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static final String FINALIZER_CLASS_NAME = "com.google.common.base.internal.Finalizer";
80090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
81090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /** Reference to Finalizer.startFinalizer(). */
82090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  private static final Method startFinalizer;
83090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  static {
84090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    Class<?> finalizer = loadFinalizer(
85090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        new SystemLoader(), new DecoupledLoader(), new DirectLoader());
86090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    startFinalizer = getStartFinalizer(finalizer);
87090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
88090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
89090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
90090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * The actual reference queue that our background thread will poll.
91090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
92090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  final ReferenceQueue<Object> queue;
93090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
94090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
95090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Whether or not the background thread started successfully.
96090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
97090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  final boolean threadStarted;
98090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
99090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
100090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Constructs a new queue.
101090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
102090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  @SuppressWarnings("unchecked")
103090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  public FinalizableReferenceQueue() {
104090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    // We could start the finalizer lazily, but I'd rather it blow up early.
105090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    ReferenceQueue<Object> queue;
106090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    boolean threadStarted = false;
107090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    try {
1081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      queue = (ReferenceQueue<Object>)
1091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          startFinalizer.invoke(null, FinalizableReference.class, this);
110090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      threadStarted = true;
1111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    } catch (IllegalAccessException impossible) {
1121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      throw new AssertionError(impossible); // startFinalizer() is public
113090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    } catch (Throwable t) {
114090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      logger.log(Level.INFO, "Failed to start reference finalizer thread."
1151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          + " Reference cleanup will only occur when new references are created.", t);
116090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      queue = new ReferenceQueue<Object>();
117090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
118090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
119090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    this.queue = queue;
120090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    this.threadStarted = threadStarted;
121090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
122090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
123090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
1241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Repeatedly dequeues references from the queue and invokes {@link
1251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * FinalizableReference#finalizeReferent()} on them until the queue is empty. This method is a
1261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * no-op if the background thread was created successfully.
127090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
128090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  void cleanUp() {
129090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    if (threadStarted) {
130090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      return;
131090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
132090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
133090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    Reference<?> reference;
134090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    while ((reference = queue.poll()) != null) {
135090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      /*
1361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert       * This is for the benefit of phantom references. Weak and soft references will have already
1371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert       * been cleared by this point.
138090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson       */
139090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      reference.clear();
140090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      try {
141090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        ((FinalizableReference) reference).finalizeReferent();
142090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      } catch (Throwable t) {
143090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
144090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
145090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
146090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
147090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
148090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
1491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Iterates through the given loaders until it finds one that can load Finalizer.
150090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
151090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * @return Finalizer.class
152090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
153090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  private static Class<?> loadFinalizer(FinalizerLoader... loaders) {
154090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    for (FinalizerLoader loader : loaders) {
155090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      Class<?> finalizer = loader.loadFinalizer();
156090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      if (finalizer != null) {
157090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        return finalizer;
158090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
159090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
160090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
161090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    throw new AssertionError();
162090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
163090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
164090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
165090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Loads Finalizer.class.
166090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
167090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  interface FinalizerLoader {
168090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
169090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    /**
1701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * Returns Finalizer.class or null if this loader shouldn't or can't load it.
171090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     *
172090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * @throws SecurityException if we don't have the appropriate privileges
173090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     */
174090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    Class<?> loadFinalizer();
175090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
176090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
177090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
1781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Tries to load Finalizer from the system class loader. If Finalizer is in the system class path,
1791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * we needn't create a separate loader.
180090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
181090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  static class SystemLoader implements FinalizerLoader {
1821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override
183090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    public Class<?> loadFinalizer() {
184090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      ClassLoader systemLoader;
185090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      try {
186090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        systemLoader = ClassLoader.getSystemClassLoader();
187090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      } catch (SecurityException e) {
188090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        logger.info("Not allowed to access system class loader.");
189090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        return null;
190090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
191090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      if (systemLoader != null) {
192090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        try {
193090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson          return systemLoader.loadClass(FINALIZER_CLASS_NAME);
194090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        } catch (ClassNotFoundException e) {
195090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson          // Ignore. Finalizer is simply in a child class loader.
196090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson          return null;
197090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        }
198090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      } else {
199090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        return null;
200090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
201090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
202090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
203090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
204090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
2051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Try to load Finalizer in its own class loader. If Finalizer's thread had a direct reference to
2061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * our class loader (which could be that of a dynamically loaded web application or OSGi bundle),
2071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * it would prevent our class loader from getting garbage collected.
208090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
209090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  static class DecoupledLoader implements FinalizerLoader {
2101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    private static final String LOADING_ERROR = "Could not load Finalizer in its own class loader."
2111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        + "Loading Finalizer in the current class loader instead. As a result, you will not be able"
2121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        + "to garbage collect this class loader. To support reclaiming this class loader, either"
2131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        + "resolve the underlying issue, or move Google Collections to your system class path.";
214090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
2151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override
216090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    public Class<?> loadFinalizer() {
217090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      try {
218090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        /*
2191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert         * We use URLClassLoader because it's the only concrete class loader implementation in the
2201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert         * JDK. If we used our own ClassLoader subclass, Finalizer would indirectly reference this
2211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert         * class loader:
222090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         *
2231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert         * Finalizer.class -> CustomClassLoader -> CustomClassLoader.class -> This class loader
224090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         *
225090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         * System class loader will (and must) be the parent.
226090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson         */
227090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        ClassLoader finalizerLoader = newLoader(getBaseUrl());
228090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        return finalizerLoader.loadClass(FINALIZER_CLASS_NAME);
229090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      } catch (Exception e) {
230090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        logger.log(Level.WARNING, LOADING_ERROR, e);
231090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        return null;
232090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
233090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
234090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
235090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    /**
236090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * Gets URL for base of path containing Finalizer.class.
237090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     */
238090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    URL getBaseUrl() throws IOException {
239090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      // Find URL pointing to Finalizer.class file.
240090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      String finalizerPath = FINALIZER_CLASS_NAME.replace('.', '/') + ".class";
241090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      URL finalizerUrl = getClass().getClassLoader().getResource(finalizerPath);
242090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      if (finalizerUrl == null) {
243090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        throw new FileNotFoundException(finalizerPath);
244090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
245090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
246090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      // Find URL pointing to base of class path.
247090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      String urlString = finalizerUrl.toString();
248090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      if (!urlString.endsWith(finalizerPath)) {
249090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        throw new IOException("Unsupported path style: " + urlString);
250090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
2511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      urlString = urlString.substring(0, urlString.length() - finalizerPath.length());
252090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      return new URL(finalizerUrl, urlString);
253090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
254090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
255090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    /** Creates a class loader with the given base URL as its classpath. */
256090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    URLClassLoader newLoader(URL base) {
2571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      return new URLClassLoader(new URL[] {base});
258090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
259090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
260090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
261090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
2621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Loads Finalizer directly using the current class loader. We won't be able to garbage collect
2631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * this class loader, but at least the world doesn't end.
264090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
265090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  static class DirectLoader implements FinalizerLoader {
2661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override
267090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    public Class<?> loadFinalizer() {
268090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      try {
269090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        return Class.forName(FINALIZER_CLASS_NAME);
270090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      } catch (ClassNotFoundException e) {
271090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        throw new AssertionError(e);
272090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
273090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
274090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
275090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
276090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
277090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Looks up Finalizer.startFinalizer().
278090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
279090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  static Method getStartFinalizer(Class<?> finalizer) {
280090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    try {
281090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      return finalizer.getMethod("startFinalizer", Class.class, Object.class);
282090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    } catch (NoSuchMethodException e) {
283090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      throw new AssertionError(e);
284090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
285090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
286090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson}
287