View Javadoc

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 }