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 }