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 */ 017package org.apache.commons.pool2.impl; 018 019import java.lang.ref.Reference; 020import java.lang.ref.ReferenceQueue; 021import java.lang.ref.SoftReference; 022import java.util.ArrayList; 023import java.util.Iterator; 024import java.util.NoSuchElementException; 025 026import org.apache.commons.pool2.BaseObjectPool; 027import org.apache.commons.pool2.ObjectPool; 028import org.apache.commons.pool2.PoolUtils; 029import org.apache.commons.pool2.PooledObjectFactory; 030 031/** 032 * A {@link java.lang.ref.SoftReference SoftReference} based {@link ObjectPool}. 033 * <p> 034 * This class is intended to be thread-safe. 035 * 036 * @param <T> 037 * Type of element pooled in this pool. 038 * 039 * @version $Revision: 1531139 $ 040 * 041 * @since 2.0 042 */ 043public class SoftReferenceObjectPool<T> extends BaseObjectPool<T> { 044 045 /** Factory to source pooled objects */ 046 private final PooledObjectFactory<T> factory; 047 048 /** 049 * Queue of broken references that might be able to be removed from 050 * <code>_pool</code>. This is used to help {@link #getNumIdle()} be more 051 * accurate with minimal performance overhead. 052 */ 053 private final ReferenceQueue<T> refQueue = new ReferenceQueue<T>(); 054 055 /** Count of instances that have been checkout out to pool clients */ 056 private int numActive = 0; // @GuardedBy("this") 057 058 /** Total number of instances that have been destroyed */ 059 private long destroyCount = 0; 060 061 /** Total number of instances that have been created */ 062 private long createCount = 0; 063 064 /** Idle references - waiting to be borrowed */ 065 private final LinkedBlockingDeque<PooledSoftReference<T>> idleReferences = 066 new LinkedBlockingDeque<PooledSoftReference<T>>(); 067 068 /** All references - checked out or waiting to be borrowed. */ 069 private final ArrayList<PooledSoftReference<T>> allReferences = 070 new ArrayList<PooledSoftReference<T>>(); 071 072 /** 073 * Create a <code>SoftReferenceObjectPool</code> with the specified factory. 074 * 075 * @param factory object factory to use. 076 */ 077 public SoftReferenceObjectPool(PooledObjectFactory<T> factory) { 078 this.factory = factory; 079 } 080 081 /** 082 * Borrow an object from the pool. If there are no idle instances available 083 * in the pool, the configured factory's 084 * {@link PooledObjectFactory#makeObject()} method is invoked to create a 085 * new instance. 086 * <p> 087 * All instances are {@link PooledObjectFactory#activateObject( 088 * org.apache.commons.pool2.PooledObject) activated} 089 * and {@link PooledObjectFactory#validateObject( 090 * org.apache.commons.pool2.PooledObject) 091 * validated} before being returned by this method. If validation fails or 092 * an exception occurs activating or validating an idle instance, the 093 * failing instance is {@link PooledObjectFactory#destroyObject( 094 * org.apache.commons.pool2.PooledObject) 095 * destroyed} and another instance is retrieved from the pool, validated and 096 * activated. This process continues until either the pool is empty or an 097 * instance passes validation. If the pool is empty on activation or it does 098 * not contain any valid instances, the factory's <code>makeObject</code> 099 * method is used to create a new instance. If the created instance either 100 * raises an exception on activation or fails validation, 101 * <code>NoSuchElementException</code> is thrown. Exceptions thrown by 102 * <code>MakeObject</code> are propagated to the caller; but other than 103 * <code>ThreadDeath</code> or <code>VirtualMachineError</code>, exceptions 104 * generated by activation, validation or destroy methods are swallowed 105 * silently. 106 * 107 * @throws NoSuchElementException 108 * if a valid object cannot be provided 109 * @throws IllegalStateException 110 * if invoked on a {@link #close() closed} pool 111 * @throws Exception 112 * if an exception occurs creating a new instance 113 * @return a valid, activated object instance 114 */ 115 @SuppressWarnings("null") // ref can not be null 116 @Override 117 public synchronized T borrowObject() throws Exception { 118 assertOpen(); 119 T obj = null; 120 boolean newlyCreated = false; 121 PooledSoftReference<T> ref = null; 122 while (null == obj) { 123 if (idleReferences.isEmpty()) { 124 if (null == factory) { 125 throw new NoSuchElementException(); 126 } else { 127 newlyCreated = true; 128 obj = factory.makeObject().getObject(); 129 createCount++; 130 // Do not register with the queue 131 ref = new PooledSoftReference<T>(new SoftReference<T>(obj)); 132 allReferences.add(ref); 133 } 134 } else { 135 ref = idleReferences.pollFirst(); 136 obj = ref.getObject(); 137 // Clear the reference so it will not be queued, but replace with a 138 // a new, non-registered reference so we can still track this object 139 // in allReferences 140 ref.getReference().clear(); 141 ref.setReference(new SoftReference<T>(obj)); 142 } 143 if (null != factory && null != obj) { 144 try { 145 factory.activateObject(ref); 146 if (!factory.validateObject(ref)) { 147 throw new Exception("ValidateObject failed"); 148 } 149 } catch (Throwable t) { 150 PoolUtils.checkRethrow(t); 151 try { 152 destroy(ref); 153 } catch (Throwable t2) { 154 PoolUtils.checkRethrow(t2); 155 // Swallowed 156 } finally { 157 obj = null; 158 } 159 if (newlyCreated) { 160 throw new NoSuchElementException( 161 "Could not create a validated object, cause: " + 162 t.getMessage()); 163 } 164 } 165 } 166 } 167 numActive++; 168 ref.allocate(); 169 return obj; 170 } 171 172 /** 173 * Returns an instance to the pool after successful validation and 174 * passivation. The returning instance is destroyed if any of the following 175 * are true: 176 * <ul> 177 * <li>the pool is closed</li> 178 * <li>{@link PooledObjectFactory#validateObject( 179 * org.apache.commons.pool2.PooledObject) validation} fails 180 * </li> 181 * <li>{@link PooledObjectFactory#passivateObject( 182 * org.apache.commons.pool2.PooledObject) passivation} 183 * throws an exception</li> 184 * </ul> 185 * Exceptions passivating or destroying instances are silently swallowed. 186 * Exceptions validating instances are propagated to the client. 187 * 188 * @param obj 189 * instance to return to the pool 190 */ 191 @Override 192 public synchronized void returnObject(T obj) throws Exception { 193 boolean success = !isClosed(); 194 final PooledSoftReference<T> ref = findReference(obj); 195 if (ref == null) { 196 throw new IllegalStateException( 197 "Returned object not currently part of this pool"); 198 } 199 if (factory != null) { 200 if (!factory.validateObject(ref)) { 201 success = false; 202 } else { 203 try { 204 factory.passivateObject(ref); 205 } catch (Exception e) { 206 success = false; 207 } 208 } 209 } 210 211 boolean shouldDestroy = !success; 212 numActive--; 213 if (success) { 214 215 // Deallocate and add to the idle instance pool 216 ref.deallocate(); 217 idleReferences.add(ref); 218 } 219 notifyAll(); // numActive has changed 220 221 if (shouldDestroy && factory != null) { 222 try { 223 destroy(ref); 224 } catch (Exception e) { 225 // ignored 226 } 227 } 228 } 229 230 /** 231 * {@inheritDoc} 232 */ 233 @Override 234 public synchronized void invalidateObject(T obj) throws Exception { 235 final PooledSoftReference<T> ref = findReference(obj); 236 if (ref == null) { 237 throw new IllegalStateException( 238 "Object to invalidate is not currently part of this pool"); 239 } 240 if (factory != null) { 241 destroy(ref); 242 } 243 numActive--; 244 notifyAll(); // numActive has changed 245 } 246 247 /** 248 * Create an object, and place it into the pool. addObject() is useful for 249 * "pre-loading" a pool with idle objects. 250 * <p> 251 * Before being added to the pool, the newly created instance is 252 * {@link PooledObjectFactory#validateObject( 253 * org.apache.commons.pool2.PooledObject) validated} and 254 * {@link PooledObjectFactory#passivateObject( 255 * org.apache.commons.pool2.PooledObject) passivated}. If 256 * validation fails, the new instance is 257 * {@link PooledObjectFactory#destroyObject( 258 * org.apache.commons.pool2.PooledObject) destroyed}. Exceptions 259 * generated by the factory <code>makeObject</code> or 260 * <code>passivate</code> are propagated to the caller. Exceptions 261 * destroying instances are silently swallowed. 262 * 263 * @throws IllegalStateException 264 * if invoked on a {@link #close() closed} pool 265 * @throws Exception 266 * when the {@link #getFactory() factory} has a problem creating 267 * or passivating an object. 268 */ 269 @Override 270 public synchronized void addObject() throws Exception { 271 assertOpen(); 272 if (factory == null) { 273 throw new IllegalStateException( 274 "Cannot add objects without a factory."); 275 } 276 T obj = factory.makeObject().getObject(); 277 createCount++; 278 // Create and register with the queue 279 PooledSoftReference<T> ref = new PooledSoftReference<T>( 280 new SoftReference<T>(obj, refQueue)); 281 allReferences.add(ref); 282 283 boolean success = true; 284 if (!factory.validateObject(ref)) { 285 success = false; 286 } else { 287 factory.passivateObject(ref); 288 } 289 290 boolean shouldDestroy = !success; 291 if (success) { 292 idleReferences.add(ref); 293 notifyAll(); // numActive has changed 294 } 295 296 if (shouldDestroy) { 297 try { 298 destroy(ref); 299 } catch (Exception e) { 300 // ignored 301 } 302 } 303 } 304 305 /** 306 * Returns an approximation not less than the of the number of idle 307 * instances in the pool. 308 * 309 * @return estimated number of idle instances in the pool 310 */ 311 @Override 312 public synchronized int getNumIdle() { 313 pruneClearedReferences(); 314 return idleReferences.size(); 315 } 316 317 /** 318 * Return the number of instances currently borrowed from this pool. 319 * 320 * @return the number of instances currently borrowed from this pool 321 */ 322 @Override 323 public synchronized int getNumActive() { 324 return numActive; 325 } 326 327 /** 328 * Clears any objects sitting idle in the pool. 329 */ 330 @Override 331 public synchronized void clear() { 332 if (null != factory) { 333 Iterator<PooledSoftReference<T>> iter = idleReferences.iterator(); 334 while (iter.hasNext()) { 335 try { 336 final PooledSoftReference<T> ref = iter.next(); 337 if (null != ref.getObject()) { 338 factory.destroyObject(ref); 339 } 340 } catch (Exception e) { 341 // ignore error, keep destroying the rest 342 } 343 } 344 } 345 idleReferences.clear(); 346 pruneClearedReferences(); 347 } 348 349 /** 350 * Close this pool, and free any resources associated with it. Invokes 351 * {@link #clear()} to destroy and remove instances in the pool. 352 * <p> 353 * Calling {@link #addObject} or {@link #borrowObject} after invoking this 354 * method on a pool will cause them to throw an 355 * {@link IllegalStateException}. 356 */ 357 @Override 358 public void close() { 359 super.close(); 360 clear(); 361 } 362 363 /** 364 * Returns the {@link PooledObjectFactory} used by this pool to create and 365 * manage object instances. 366 * 367 * @return the factory 368 */ 369 public synchronized PooledObjectFactory<T> getFactory() { 370 return factory; 371 } 372 373 /** 374 * If any idle objects were garbage collected, remove their 375 * {@link Reference} wrappers from the idle object pool. 376 */ 377 private void pruneClearedReferences() { 378 // Remove wrappers for enqueued references from idle and allReferences lists 379 removeClearedReferences(idleReferences.iterator()); 380 removeClearedReferences(allReferences.iterator()); 381 while (refQueue.poll() != null) {} 382 } 383 384 /** 385 * Find the PooledSoftReference in allReferences that points to obj. 386 * 387 * @param obj returning object 388 * @return PooledSoftReference wrapping a soft reference to obj 389 */ 390 private PooledSoftReference<T> findReference(T obj) { 391 Iterator<PooledSoftReference<T>> iterator = allReferences.iterator(); 392 while (iterator.hasNext()) { 393 final PooledSoftReference<T> reference = iterator.next(); 394 if (reference.getObject() != null && reference.getObject().equals(obj)) { 395 return reference; 396 } 397 } 398 return null; 399 } 400 401 /** 402 * Destroy a {@code PooledSoftReference} and remove it from the idle and all 403 * references pools. 404 * 405 * @param toDestroy PooledSoftReference to destroy 406 * @throws Exception 407 */ 408 private void destroy(PooledSoftReference<T> toDestroy) throws Exception { 409 toDestroy.invalidate(); 410 idleReferences.remove(toDestroy); 411 allReferences.remove(toDestroy); 412 try { 413 factory.destroyObject(toDestroy); 414 } finally { 415 destroyCount++; 416 toDestroy.getReference().clear(); 417 } 418 } 419 420 /** 421 * Clears cleared references from iterator's collection 422 * @param iterator iterator over idle/allReferences 423 */ 424 private void removeClearedReferences(Iterator<PooledSoftReference<T>> iterator) { 425 PooledSoftReference<T> ref; 426 while (iterator.hasNext()) { 427 ref = iterator.next(); 428 if (ref.getReference() == null || ref.getReference().isEnqueued()) { 429 iterator.remove(); 430 } 431 } 432 } 433}