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