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 }