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 * </p> 036 * 037 * @param <T> 038 * Type of element pooled in this pool. 039 * 040 * @since 2.0 041 */ 042public class SoftReferenceObjectPool<T> extends BaseObjectPool<T> { 043 044 /** Factory to source pooled objects */ 045 private final PooledObjectFactory<T> factory; 046 047 /** 048 * Queue of broken references that might be able to be removed from 049 * <code>_pool</code>. This is used to help {@link #getNumIdle()} be more 050 * accurate with minimal performance overhead. 051 */ 052 private final ReferenceQueue<T> refQueue = new ReferenceQueue<>(); 053 054 /** Count of instances that have been checkout out to pool clients */ 055 private int numActive = 0; // @GuardedBy("this") 056 057 /** Total number of instances that have been destroyed */ 058 private long destroyCount = 0; // @GuardedBy("this") 059 060 061 /** Total number of instances that have been created */ 062 private long createCount = 0; // @GuardedBy("this") 063 064 /** Idle references - waiting to be borrowed */ 065 private final LinkedBlockingDeque<PooledSoftReference<T>> idleReferences = 066 new LinkedBlockingDeque<>(); 067 068 /** All references - checked out or waiting to be borrowed. */ 069 private final ArrayList<PooledSoftReference<T>> allReferences = 070 new ArrayList<>(); 071 072 /** 073 * Create a <code>SoftReferenceObjectPool</code> with the specified factory. 074 * 075 * @param factory object factory to use. 076 */ 077 public SoftReferenceObjectPool(final PooledObjectFactory<T> factory) { 078 this.factory = factory; 079 } 080 081 /** 082 * Borrows 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 cannot 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 } 127 newlyCreated = true; 128 obj = factory.makeObject().getObject(); 129 createCount++; 130 // Do not register with the queue 131 ref = new PooledSoftReference<>(new SoftReference<>(obj)); 132 allReferences.add(ref); 133 } else { 134 ref = idleReferences.pollFirst(); 135 obj = ref.getObject(); 136 // Clear the reference so it will not be queued, but replace with a 137 // a new, non-registered reference so we can still track this object 138 // in allReferences 139 ref.getReference().clear(); 140 ref.setReference(new SoftReference<>(obj)); 141 } 142 if (null != factory && null != obj) { 143 try { 144 factory.activateObject(ref); 145 if (!factory.validateObject(ref)) { 146 throw new Exception("ValidateObject failed"); 147 } 148 } catch (final Throwable t) { 149 PoolUtils.checkRethrow(t); 150 try { 151 destroy(ref); 152 } catch (final Throwable t2) { 153 PoolUtils.checkRethrow(t2); 154 // Swallowed 155 } finally { 156 obj = null; 157 } 158 if (newlyCreated) { 159 throw new NoSuchElementException( 160 "Could not create a validated object, cause: " + 161 t.getMessage()); 162 } 163 } 164 } 165 } 166 numActive++; 167 ref.allocate(); 168 return obj; 169 } 170 171 /** 172 * Returns an instance to the pool after successful validation and 173 * passivation. The returning instance is destroyed if any of the following 174 * are true: 175 * <ul> 176 * <li>the pool is closed</li> 177 * <li>{@link PooledObjectFactory#validateObject( 178 * org.apache.commons.pool2.PooledObject) validation} fails 179 * </li> 180 * <li>{@link PooledObjectFactory#passivateObject( 181 * org.apache.commons.pool2.PooledObject) passivation} 182 * throws an exception</li> 183 * </ul> 184 * Exceptions passivating or destroying instances are silently swallowed. 185 * Exceptions validating instances are propagated to the client. 186 * 187 * @param obj 188 * instance to return to the pool 189 * @throws IllegalArgumentException 190 * if obj is not currently part of this pool 191 */ 192 @Override 193 public synchronized void returnObject(final 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 (final Exception e) { 207 success = false; 208 } 209 } 210 } 211 212 final 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 (final Exception e) { 226 // ignored 227 } 228 } 229 } 230 231 /** 232 * {@inheritDoc} 233 */ 234 @Override 235 public synchronized void invalidateObject(final 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 * Creates an object, and places 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 final T obj = factory.makeObject().getObject(); 278 createCount++; 279 // Create and register with the queue 280 final PooledSoftReference<T> ref = new PooledSoftReference<>( 281 new SoftReference<>(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 final 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 (final 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 * Returns 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 final 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 (final Exception e) { 342 // ignore error, keep destroying the rest 343 } 344 } 345 } 346 idleReferences.clear(); 347 pruneClearedReferences(); 348 } 349 350 /** 351 * Closes this pool, and frees 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 // empty 384 } 385 } 386 387 /** 388 * Finds the PooledSoftReference in allReferences that points to obj. 389 * 390 * @param obj returning object 391 * @return PooledSoftReference wrapping a soft reference to obj 392 */ 393 private PooledSoftReference<T> findReference(final T obj) { 394 final Iterator<PooledSoftReference<T>> iterator = allReferences.iterator(); 395 while (iterator.hasNext()) { 396 final PooledSoftReference<T> reference = iterator.next(); 397 if (reference.getObject() != null && reference.getObject().equals(obj)) { 398 return reference; 399 } 400 } 401 return null; 402 } 403 404 /** 405 * Destroys a {@code PooledSoftReference} and removes it from the idle and all 406 * references pools. 407 * 408 * @param toDestroy PooledSoftReference to destroy 409 * 410 * @throws Exception If an error occurs while trying to destroy the object 411 */ 412 private void destroy(final PooledSoftReference<T> toDestroy) throws Exception { 413 toDestroy.invalidate(); 414 idleReferences.remove(toDestroy); 415 allReferences.remove(toDestroy); 416 try { 417 factory.destroyObject(toDestroy); 418 } finally { 419 destroyCount++; 420 toDestroy.getReference().clear(); 421 } 422 } 423 424 /** 425 * Clears cleared references from iterator's collection 426 * @param iterator iterator over idle/allReferences 427 */ 428 private void removeClearedReferences(final Iterator<PooledSoftReference<T>> iterator) { 429 PooledSoftReference<T> ref; 430 while (iterator.hasNext()) { 431 ref = iterator.next(); 432 if (ref.getReference() == null || ref.getReference().isEnqueued()) { 433 iterator.remove(); 434 } 435 } 436 } 437 438 @Override 439 protected void toStringAppendFields(final StringBuilder builder) { 440 super.toStringAppendFields(builder); 441 builder.append(", factory="); 442 builder.append(factory); 443 builder.append(", refQueue="); 444 builder.append(refQueue); 445 builder.append(", numActive="); 446 builder.append(numActive); 447 builder.append(", destroyCount="); 448 builder.append(destroyCount); 449 builder.append(", createCount="); 450 builder.append(createCount); 451 builder.append(", idleReferences="); 452 builder.append(idleReferences); 453 builder.append(", allReferences="); 454 builder.append(allReferences); 455 } 456}