1 /* 2 * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.] 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.opensaml.xml.util; 18 19 import java.lang.ref.WeakReference; 20 import java.util.HashSet; 21 import java.util.WeakHashMap; 22 23 import org.slf4j.Logger; 24 import org.slf4j.LoggerFactory; 25 26 /** 27 * An implementation of {@link SingletonFactory}, which provides some support for handling 28 * cases where the output class instance holds a reference to the input class instance. 29 * 30 * <p> 31 * A {@link WeakHashMap} is used as the underlying store. This ensures that if the input class 32 * instance become otherwise unused (weakly reachable), the input class instance key used 33 * within the factory will not prevent the input class from being garbage-collected, 34 * thereby preventing a memory leak. 35 * </p> 36 * 37 * <p> 38 * This class differs from {@link AbstractSimpleSingletonFactory} in that output value instances 39 * stored and returned by the factory are also wrapped internally in a {@link WeakReference}. 40 * This class should be used in cases where the output class holds a reference to the input 41 * class key, so as not to prevent the described weak reference-based garbage collection 42 * of the input class key, and thereby avoiding a memory leak. 43 * </p> 44 * 45 * <p> 46 * Because the output instance is held in a WeakReference, it is subject to aggressive 47 * garbage collection if it is otherwise weakly reachable (i.e. no strong or soft references 48 * to it are held outside of this factory), ostensibly defeating the purpose of this factory. 49 * Therefore if the lifecycle of external strong or soft references to any obtained output 50 * instances obtained from the factory is shorter than the desired lifecyle of the output instance 51 * (i.e. callers do not hold a strong or soft reference to an output instance for at least as 52 * long as to the input instance), then an option <code>requireExplicitRelease</code> is provided 53 * that causes the factory to internally maintain a strong reference to each output instance. 54 * This inhibits the garbage collection of the output instance. If this option is enabled, 55 * then callers must explicity indicate when the output instance may be garbage collected by 56 * calling {@link #release(Object)}. Failure to release an output instance when necessary 57 * will result in a memory leak of the output instance as well as the input instance (if 58 * the output instance holds a strong or soft reference to the input instance). 59 * </p> 60 * 61 * <p> 62 * The default value of <code>requireExplicitRelease</code> is <code>false</code>. This is appropriate 63 * for cases where calling code holds long-lived strong or soft references to the output instance, 64 * typically as long or longer than references to the corresponding input instance, or where explict release 65 * is undesirable or impractical. 66 * </p> 67 * 68 * <p> 69 * Subclasses of this class might also implement automatic release of output instances, 70 * instead of or in addition to, the explicit release mechanism supported by this class. 71 * This might be based for example on mechanisms such as object aging or a fixed size FIFO queue. 72 * </p> 73 * 74 * @param <Input> the factory input class type 75 * @param <Output> the factory output class type 76 */ 77 public abstract class AbstractWrappedSingletonFactory<Input, Output> 78 extends AbstractSingletonFactory<Input, Output> { 79 80 /** Class logger. */ 81 private final Logger log = LoggerFactory.getLogger(AbstractWrappedSingletonFactory.class); 82 83 /** Storage for the factory. */ 84 private WeakHashMap<Input, WeakReference<Output>> map; 85 86 /** Set which holds a separate strong reference to output class instances, 87 * to inhibit garbage collection of the referent of the WeakReference. */ 88 private HashSet<Output> outputSet; 89 90 /** Flag indicating whether explicit release of the output instances is required. */ 91 private boolean explicitRelease; 92 93 /** Constructor. */ 94 public AbstractWrappedSingletonFactory() { 95 this(false); 96 } 97 98 /** 99 * Constructor. 100 * 101 * @param requireExplicitRelease if true, callers must explicitly release 102 * output instances when garbage collection is desired. 103 */ 104 public AbstractWrappedSingletonFactory(boolean requireExplicitRelease) { 105 map = new WeakHashMap<Input, WeakReference<Output>>(); 106 explicitRelease = requireExplicitRelease; 107 outputSet = new HashSet<Output>(); 108 } 109 110 /** 111 * Obtain an instance of the output class based on an input class instance. 112 * 113 * @param input the input class instance 114 * @return an output class instance 115 */ 116 public synchronized Output getInstance(Input input) { 117 Output output = super.getInstance(input); 118 119 if (explicitRelease && output != null) { 120 log.trace("Explicit release was indicated, registering output instance to inhibit garbage collection"); 121 register(output); 122 } 123 124 return output; 125 } 126 127 /** 128 * Get whether explict release of output instances is required, 129 * in order to allow garbage collection and prevent memory leaks. 130 * 131 * @return true if enabled, false otherwise 132 */ 133 public boolean isRequireExplicitRelease() { 134 return explicitRelease; 135 } 136 137 /** 138 * Release the specified output instance so that, as the referent 139 * of a WeakReference, it may be garbage collected when it otherwise 140 * becomse weakly reachable. 141 * 142 * @param output the output instance to release 143 */ 144 public synchronized void release(Output output) { 145 outputSet.remove(output); 146 } 147 148 /** 149 * Release all currently held output instances so they 150 * may be garbage collected when they become otherwise 151 * weakly reachable. 152 */ 153 public synchronized void releaseAll() { 154 outputSet.clear(); 155 } 156 157 /** 158 * Register the output instance so as to inhibit garbage collection. 159 * 160 * @param output the ouput instance to register 161 */ 162 protected synchronized void register(Output output) { 163 outputSet.add(output); 164 } 165 166 /** 167 * {@inheritDoc} 168 * 169 * <p> 170 * The output instance will be automatically unwrapped from within the WeakReference. 171 * </p> 172 * 173 * <p> 174 * Note this will return null if either the input does not 175 * currently have an associated output, or if the WeakReference 176 * to the output stored had already been clearly in preparation 177 * for garbage collection. 178 * </p> 179 */ 180 protected synchronized Output get(Input input) { 181 WeakReference<Output> outputRef = map.get(input); 182 if (outputRef != null) { 183 log.trace("Input key mapped to a non-null WeakReference"); 184 if (outputRef.get() != null) { 185 log.trace("WeakReference referent was non-null, returning referent"); 186 return outputRef.get(); 187 } else { 188 log.trace("WeakReference referent was null, removing WeakReference entry from map"); 189 map.remove(input); 190 } 191 } 192 return null; 193 } 194 195 /** 196 * {@inheritDoc} 197 * 198 * <p> 199 * The output instance will be automatically wrapped in a WeakReference. 200 * </p> 201 */ 202 protected synchronized void put(Input input, Output output) { 203 map.put(input, new WeakReference<Output>(output)); 204 } 205 206 }