001 /* ====================================================================
002 * The Apache Software License, Version 1.1
003 *
004 * Copyright (c) 2003 The Apache Software Foundation. All rights
005 * reserved.
006 *
007 * Redistribution and use in source and binary forms, with or without
008 * modification, are permitted provided that the following conditions
009 * are met:
010 *
011 * 1. Redistributions of source code must retain the above copyright
012 * notice, this list of conditions and the following disclaimer.
013 *
014 * 2. Redistributions in binary form must reproduce the above copyright
015 * notice, this list of conditions and the following disclaimer in
016 * the documentation and/or other materials provided with the
017 * distribution.
018 *
019 * 3. The end-user documentation included with the redistribution,
020 * if any, must include the following acknowledgment:
021 * "This product includes software developed by the
022 * Apache Software Foundation (http://www.apache.org/)."
023 * Alternately, this acknowledgment may appear in the software itself,
024 * if and wherever such third-party acknowledgments normally appear.
025 *
026 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
027 * Foundation" must not be used to endorse or promote products derived
028 * from this software without prior written permission. For written
029 * permission, please contact apache@apache.org.
030 *
031 * 5. Products derived from this software may not be called "Apache",
032 * nor may "Apache" appear in their name, without prior written
033 * permission of the Apache Software Foundation.
034 *
035 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046 * SUCH DAMAGE.
047 * ====================================================================
048 *
049 * This software consists of voluntary contributions made by many
050 * individuals on behalf of the Apache Software Foundation. For more
051 * information on the Apache Software Foundation, please see
052 * <http://www.apache.org/>.
053 *
054 */
055 package org.jpu.patterns.singleton.onePerContext;
056
057 import java.util.ArrayList;
058 import java.util.HashMap;
059 import java.util.HashSet;
060 import java.util.Iterator;
061 import java.util.List;
062 import java.util.Map;
063 import java.util.Set;
064 import java.util.WeakHashMap;
065
066 import org.apache.commons.lang.exception.NestableRuntimeException;
067 import org.apache.commons.logging.Log;
068 import org.apache.commons.logging.LogFactory;
069
070 /**
071 * This class and its associated interfaces implement
072 * a pattern that is a variant of Singleton intended for situations where you
073 * need one instance of the class per "context", rather than one per classloader as is the
074 * case with traditional singletons. A context can be any object of your choice, but can
075 * optionally implement {@link IContext} if if it wishes to be notified when objects are
076 * created on its behalf.
077 * <p>
078 * To use it, have your class provide an accessible no-arg constructor, and declare a private
079 * static field like this:
080 * <blockquote>
081 * <code>
082 * private static OnePerContextManager _opcMgr = new OnePerContextManager(<b><i>C</i></b>.class);
083 * </code>
084 * </blockquote>
085 * where <b><i>C</i></b> is the name of your class. When you want an instance of it, call
086 * <blockquote>
087 * <code>
088 * (<b><i>C</i></b>)_opcMgr.getInstance(ctx);
089 * </code>
090 * </blockquote>
091 * where <code>ctx</code> is the context. Typically you would wrap the above call in a public static
092 * method of <b><i>C</i></b> like this:
093 * <blockquote>
094 * <code>
095 * public static <b><i>C</i></b> getInstance(Object ctx) {
096 * return (<b><i>C</i></b>)_opcMgr.getInstance(ctx);
097 * }
098 * </code>
099 * </blockquote>
100 * The class may then be treated much like a singleton, except that one instance will be
101 * maintained <i>per context</i>, instead of one per classloader. Note <code>ctx</code>
102 * can be <code>null</code>, in which case it defaults to <code><b><i>C</i></b>.class</code>,
103 * which provides traditional singleton behavior with one instance per classloader.
104 * <p>
105 * <b>Custom Instantiation</b><br>
106 * By default the no-arg constructor of the class passed to the constructor is used to generate
107 * the One-Per-Context instances. However you can change this behavior in either of two ways: 1) have the
108 * context implement {@link IContext} and define a {@link IContext#newInstance} method that
109 * instantiates the One-Per-Context and returns it; or 2) write your own {@link IOnePerContextManager.IInstantiator} and
110 * set it via {@link IOnePerContextManager#setInstantiator}. Method (1) takes precedence, so if
111 * {@link IContext#newInstance} returns non-null, its return value will be used as the One-Per-Context
112 * instance and the instantiator will not be called.
113 * <p>
114 * Method (2) is generally intended for use by
115 * the One-Per-Context class itself to control its own instantiation, while Method (1) is intended
116 * for use by clients of the One-Per-Context class who need to substitute their own subclass
117 * implementation for use in particular contexts.
118 * <p>
119 * <b>Custom Initialization</b><br>
120 * If you want each One-Per-Context instance to be notified of its associated
121 * context, you can have it implement {@link IOnePerContext} and implement the
122 * {@link IOnePerContext#initOnePerContext} method. The method will be called
123 * on newly-created One-Per-Context's.
124 * <p>
125 * You can also perform custom initialization of One-Per-Context's by writing your own
126 * {@link IOnePerContextManager.IInitializer} and adding it via {@link IOnePerContextManager#addInitializer}. You can
127 * register as many initializers as you want, and they will be called in order on each newly
128 * created One-Per-Context instance. This sort of initialization is performed immediately
129 * <i>after</i> that described in the previous paragraph.
130 * <p>
131 * A third type of custom initialization can be performed by the context. To have the context
132 * be notified when a new One-Per-Context is created on its behalf,
133 * have it implement {@link IContext} and override {@link IContext#initialize}. This
134 * method will be called immediately <i>after</i> that described in the previous paragraph.
135 * <p>
136 * <b>Thread local Contexts</b><br>
137 * To avoid the necessity of passing around context references, you might want
138 * to hold the context as a <code>ThreadLocal</code> and adjust its value when
139 * you want to change contexts. The code might look something like this:
140 * <pre><code>
141 * public class Foo {
142 * public static final Object DEFAULT_CTX = new Object();
143 * public static Foo getInstance() {
144 * return (Foo)_opcMgr.getInstance( _ctx.get() );
145 * }
146 * public static void setCtx(Object ctx) {
147 * _ctx.set(ctx);
148 * }
149 * private static ThreadLocal _ctx = new ThreadLocal() {
150 * protected Object initialValue() { return DEFAULT_CTX; }
151 * };
152 * private static OnePerContextManager _opcMgr = new OnePerContextManager(Foo.class);
153 * }</code></pre>
154 * For the full example see the source code of {@link org.jpu.patterns.singleton.onePerContext.test.Foo}
155 * (source is included in the Javadoc's).
156 * <p>
157 * <b>Context References</b><br>
158 * The One-Per-Context facility supports both weak and strong holding of
159 * context references. Strong referencing can be requested by passing <code>true</code>
160 * as the second parameter to {@link OnePerContextManager#OnePerContextManager(Class,boolean)}. If weak referencing
161 * is requested (which is the default if the second parameter is omitted), the
162 * facility's internal data structures will not prevent garbage collection of the context. When
163 * the context is garbage collected, all One-Per-Context's associated with it
164 * become inaccessible through this facility (though of course the application may yet
165 * retain other references to them).
166 * <p>
167 */
168 public class OnePerContextManager implements IOnePerContextManager {
169
170 /**
171 * Default implementation of {@link IOnePerContextManager.IInstantiator}. Its {@link IOnePerContextManager.IInstantiator#newInstance} method
172 * simply calls "<code>onePerContextClass.newInstance()</code>".
173 */
174 public static class DefaultInstantiator implements IInstantiator {
175 public Object newInstance(IOnePerContextManager mgr, Object ctx) {
176 try {
177 Object onePerContext = (Object)mgr.getOnePerContextClass().newInstance();
178 return onePerContext;
179 }
180 catch( Throwable t ) {
181 throw new NestableRuntimeException(t);
182 }
183 }
184 }
185
186 /**
187 */
188 public static List findManagersByClass(Class opcClass) {
189 return findManagersByClass(opcClass, true);
190 }
191
192 /**
193 */
194 public static List findManagersByClass(Class opcClass, boolean exact) {
195 synchronized(_managers) {
196 List result = new ArrayList();
197 Iterator it = _managers.keySet().iterator();
198 while ( it.hasNext() ) {
199 OnePerContextManager mgr = (OnePerContextManager)it.next();
200 synchronized(mgr) {
201 if ( (exact && mgr.getOnePerContextClass().equals(opcClass)) ||
202 opcClass.isAssignableFrom( mgr.getOnePerContextClass() ) ) {
203 result.add(mgr);
204 }
205 }
206 }
207 return result;
208 }
209 }
210
211 /**
212 * Returns the set of all currently active <code>OnePerContextManager</code>'s. The returned
213 * set is a shallow copy of the internal data structure, so the caller can modify it at will.
214 */
215 public static Set getManagers() {
216 synchronized(_managers) {
217 Set mgrs = new HashSet();
218 mgrs.addAll( _managers.keySet() );
219 return mgrs;
220 }
221 }
222
223 /**
224 * Alias for "<code>OnePerContextManager(onePerContextClass, false)</code>".
225 */
226 public OnePerContextManager( Class onePerContextClass ) {
227 this(onePerContextClass, false);
228 }
229
230 /**
231 * Constructor.
232 *
233 * @param onePerContextClass The class of One-Per-Context's that will be created by subsequent calls to
234 * {@link #getInstance(Object)}.
235 *
236 * @param strong Indicates whether references to newly-created instances should
237 * be held strongly or weakly. If the context defines <code>equals()</code> and <code>hashCode()</code> in terms of
238 * object identity, this should usually be <code>false</code>; else it should be <code>true</code>. For instance if the
239 * context is a <code>String</code> or a primitive type, <code>strong</code> should be <code>true</code>.
240 */
241 public OnePerContextManager( Class onePerContextClass, boolean strong ) {
242 _onePerContextClass = onePerContextClass;
243 synchronized(_managers) {
244 _managers.put(this, "");
245 }
246 if ( strong ) {
247 _objects = new HashMap();
248 }
249 else {
250 _objects = new WeakHashMap();
251 }
252 }
253
254 // See interface documentation.
255 public synchronized Class getOnePerContextClass() {
256 return _onePerContextClass;
257 }
258
259 // See interface documentation.
260 public synchronized Map getObjects() {
261 Map map = new HashMap();
262 map.putAll( _objects );
263 return map;
264 }
265
266 // See interface documentation.
267 public synchronized List getInitializers() {
268 List list = new ArrayList();
269 list.addAll( _initializers );
270 return list;
271 }
272
273 // See interface documentation.
274 public synchronized void addInitializer(int i, IInitializer initializer) {
275 _initializers.add(i, initializer);
276 }
277
278 // See interface documentation.
279 public synchronized IInitializer removeInitializer(int i) {
280 return (IInitializer)_initializers.remove(i);
281 }
282
283 // See interface documentation.
284 public synchronized boolean removeInitializer(IInitializer ini) {
285 return _initializers.remove(ini);
286 }
287
288 // See interface documentation.
289 public synchronized IInstantiator getInstantiator() {
290 return _instantiator;
291 }
292
293 // See interface documentation.
294 public synchronized void setInstantiator(IInstantiator i) {
295 _instantiator = i;
296 }
297
298 protected Object getDefaultContext() {
299 return IOnePerContextManager.DEFAULT_CONTEXT;
300 }
301
302 // See interface documentation.
303 public synchronized Object getInstance( Object ctx ) {
304
305 if ( ctx == null ) {
306 ctx = getDefaultContext();
307 }
308 Object onePerContext = (Object)_objects.get(ctx);
309 if ( onePerContext == null ) {
310 if ( ctx instanceof IContext ) {
311 IContext myCtx = (IContext)ctx;
312 onePerContext = myCtx.newInstance( this );
313 }
314 if ( onePerContext == null ) {
315 onePerContext = getInstantiator().newInstance(this, ctx);
316 }
317 if ( onePerContext == null ) {
318 throw new IllegalArgumentException( "Instantiator returned null during instantiation of '" + getOnePerContextClass().getName() + "'" );
319 }
320 if ( onePerContext instanceof IOnePerContext ) {
321 IOnePerContext myOnePerContext = (IOnePerContext)onePerContext;
322 myOnePerContext.initOnePerContext(ctx);
323 }
324 Iterator it = _initializers.iterator();
325 while ( it.hasNext() ) {
326 IInitializer initializer = (IInitializer)it.next();
327 initializer.initialize(this, ctx, onePerContext);
328 }
329 if ( ctx instanceof IContext ) {
330 IContext myCtx = (IContext)ctx;
331 myCtx.initialize(this, onePerContext);
332 }
333 _objects.put(ctx, onePerContext);
334 }
335 return onePerContext;
336 }
337
338 private IInstantiator _instantiator = new DefaultInstantiator();
339 private Map _objects = null;
340 private List _initializers = new ArrayList();
341 private Class _onePerContextClass = null;
342 private static Map _managers = new WeakHashMap();
343 private static Log _log = LogFactory.getLog(OnePerContextManager.class);
344 }