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