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