001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.pool.impl; 019 020 import java.lang.ref.SoftReference; 021 import java.lang.ref.ReferenceQueue; 022 import java.lang.ref.Reference; 023 import java.util.ArrayList; 024 import java.util.Iterator; 025 import java.util.List; 026 import java.util.NoSuchElementException; 027 028 import org.apache.commons.pool.BaseObjectPool; 029 import org.apache.commons.pool.ObjectPool; 030 import org.apache.commons.pool.PoolableObjectFactory; 031 import org.apache.commons.pool.PoolUtils; 032 033 /** 034 * A {@link java.lang.ref.SoftReference SoftReference} based 035 * {@link ObjectPool}. 036 * 037 * @author Rodney Waldhoff 038 * @author Sandy McArthur 039 * @version $Revision: 777748 $ $Date: 2009-05-22 20:00:44 -0400 (Fri, 22 May 2009) $ 040 * @since Pool 1.0 041 */ 042 public class SoftReferenceObjectPool extends BaseObjectPool implements ObjectPool { 043 /** 044 * Create a <code>SoftReferenceObjectPool</code> without a factory. 045 * {@link #setFactory(PoolableObjectFactory) setFactory} should be called 046 * before any attempts to use the pool are made. 047 * Generally speaking you should prefer the {@link #SoftReferenceObjectPool(PoolableObjectFactory)} constructor. 048 * 049 * @see #SoftReferenceObjectPool(PoolableObjectFactory) 050 */ 051 public SoftReferenceObjectPool() { 052 _pool = new ArrayList(); 053 _factory = null; 054 } 055 056 /** 057 * Create a <code>SoftReferenceObjectPool</code> with the specified factory. 058 * 059 * @param factory object factory to use. 060 */ 061 public SoftReferenceObjectPool(PoolableObjectFactory factory) { 062 _pool = new ArrayList(); 063 _factory = factory; 064 } 065 066 /** 067 * Create a <code>SoftReferenceObjectPool</code> with the specified factory and initial idle object count. 068 * 069 * @param factory object factory to use. 070 * @param initSize initial size to attempt to prefill the pool. 071 * @throws Exception when there is a problem prefilling the pool. 072 * @throws IllegalArgumentException when <code>factory</code> is <code>null</code>. 073 * @deprecated because this is a SoftReference pool, prefilled idle obejects may be garbage collected before they are used. 074 * To be removed in Pool 3.0. 075 */ 076 public SoftReferenceObjectPool(PoolableObjectFactory factory, int initSize) throws Exception, IllegalArgumentException { 077 if (factory == null) { 078 throw new IllegalArgumentException("factory required to prefill the pool."); 079 } 080 _pool = new ArrayList(initSize); 081 _factory = factory; 082 PoolUtils.prefill(this, initSize); 083 } 084 085 public synchronized Object borrowObject() throws Exception { 086 assertOpen(); 087 Object obj = null; 088 boolean newlyCreated = false; 089 while(null == obj) { 090 if(_pool.isEmpty()) { 091 if(null == _factory) { 092 throw new NoSuchElementException(); 093 } else { 094 newlyCreated = true; 095 obj = _factory.makeObject(); 096 } 097 } else { 098 SoftReference ref = (SoftReference)(_pool.remove(_pool.size() - 1)); 099 obj = ref.get(); 100 ref.clear(); // prevent this ref from being enqueued with refQueue. 101 } 102 if (null != _factory && null != obj) { 103 try { 104 _factory.activateObject(obj); 105 if (!_factory.validateObject(obj)) { 106 throw new Exception("ValidateObject failed"); 107 } 108 } catch (Throwable t) { 109 try { 110 _factory.destroyObject(obj); 111 } catch (Throwable t2) { 112 // swallowed 113 } finally { 114 obj = null; 115 } 116 if (newlyCreated) { 117 throw new NoSuchElementException( 118 "Could not create a validated object, cause: " + 119 t.getMessage()); 120 } 121 } 122 } 123 } 124 _numActive++; 125 return obj; 126 } 127 128 public synchronized void returnObject(Object obj) throws Exception { 129 boolean success = !isClosed(); 130 if (_factory != null) { 131 if(!_factory.validateObject(obj)) { 132 success = false; 133 } else { 134 try { 135 _factory.passivateObject(obj); 136 } catch(Exception e) { 137 success = false; 138 } 139 } 140 } 141 142 boolean shouldDestroy = !success; 143 _numActive--; 144 if(success) { 145 _pool.add(new SoftReference(obj, refQueue)); 146 } 147 notifyAll(); // _numActive has changed 148 149 if (shouldDestroy && _factory != null) { 150 try { 151 _factory.destroyObject(obj); 152 } catch(Exception e) { 153 // ignored 154 } 155 } 156 } 157 158 public synchronized void invalidateObject(Object obj) throws Exception { 159 _numActive--; 160 if (_factory != null) { 161 _factory.destroyObject(obj); 162 } 163 notifyAll(); // _numActive has changed 164 } 165 166 /** 167 * Create an object, and place it into the pool. 168 * addObject() is useful for "pre-loading" a pool with idle objects. 169 */ 170 public synchronized void addObject() throws Exception { 171 assertOpen(); 172 if (_factory == null) { 173 throw new IllegalStateException("Cannot add objects without a factory."); 174 } 175 Object obj = _factory.makeObject(); 176 177 boolean success = true; 178 if(!_factory.validateObject(obj)) { 179 success = false; 180 } else { 181 _factory.passivateObject(obj); 182 } 183 184 boolean shouldDestroy = !success; 185 if(success) { 186 _pool.add(new SoftReference(obj, refQueue)); 187 notifyAll(); // _numActive has changed 188 } 189 190 if(shouldDestroy) { 191 try { 192 _factory.destroyObject(obj); 193 } catch(Exception e) { 194 // ignored 195 } 196 } 197 } 198 199 /** Returns an approximation not less than the of the number of idle instances in the pool. */ 200 public synchronized int getNumIdle() { 201 pruneClearedReferences(); 202 return _pool.size(); 203 } 204 205 /** 206 * Return the number of instances currently borrowed from this pool. 207 * 208 * @return the number of instances currently borrowed from this pool 209 */ 210 public synchronized int getNumActive() { 211 return _numActive; 212 } 213 214 /** 215 * Clears any objects sitting idle in the pool. 216 */ 217 public synchronized void clear() { 218 if(null != _factory) { 219 Iterator iter = _pool.iterator(); 220 while(iter.hasNext()) { 221 try { 222 Object obj = ((SoftReference)iter.next()).get(); 223 if(null != obj) { 224 _factory.destroyObject(obj); 225 } 226 } catch(Exception e) { 227 // ignore error, keep destroying the rest 228 } 229 } 230 } 231 _pool.clear(); 232 pruneClearedReferences(); 233 } 234 235 public void close() throws Exception { 236 super.close(); 237 clear(); 238 } 239 240 /** 241 * Sets the {@link PoolableObjectFactory factory} this pool uses 242 * to create new instances. Trying to change 243 * the <code>factory</code> while there are borrowed objects will 244 * throw an {@link IllegalStateException}. 245 * 246 * @param factory the {@link PoolableObjectFactory} used to create new instances. 247 * @throws IllegalStateException when the factory cannot be set at this time 248 */ 249 public synchronized void setFactory(PoolableObjectFactory factory) throws IllegalStateException { 250 assertOpen(); 251 if(0 < getNumActive()) { 252 throw new IllegalStateException("Objects are already active"); 253 } else { 254 clear(); 255 _factory = factory; 256 } 257 } 258 259 /** 260 * If any idle objects were garbage collected, remove their 261 * {@link Reference} wrappers from the idle object pool. 262 */ 263 private void pruneClearedReferences() { 264 Reference ref; 265 while ((ref = refQueue.poll()) != null) { 266 try { 267 _pool.remove(ref); 268 } catch (UnsupportedOperationException uoe) { 269 // ignored 270 } 271 } 272 } 273 274 /** My pool. */ 275 private List _pool = null; 276 277 /** My {@link PoolableObjectFactory}. */ 278 private PoolableObjectFactory _factory = null; 279 280 /** 281 * Queue of broken references that might be able to be removed from <code>_pool</code>. 282 * This is used to help {@link #getNumIdle()} be more accurate with minimial 283 * performance overhead. 284 */ 285 private final ReferenceQueue refQueue = new ReferenceQueue(); 286 287 /** Number of active objects. */ 288 private int _numActive = 0; 289 }