blob: b8f44fc52a45e6cf12cf9a23b320845206d40980 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.pool2;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import org.apache.commons.pool2.impl.DefaultPooledObject;
/**
* Object factory with configurable latencies for object lifecycle methods.
* This factory will also track and enforce maxActive, maxActivePerKey contracts.
* If the factory's maxActive / maxActivePerKey are set to match those of the
* pool, makeObject will throw IllegalStateException if the number of makes - destroys
* (per key) exceeds the configured max.
*
*/
public class WaiterFactory<K> implements PooledObjectFactory<Waiter>,
KeyedPooledObjectFactory<K,Waiter> {
/** Latency of activateObject */
private final long activateLatency;
/** Latency of destroyObject */
private final long destroyLatency;
/** Latency of makeObject */
private final long makeLatency;
/** Latency of passivateObject */
private final long passivateLatency;
/** Latency of validateObject */
private final long validateLatency;
/** Latency of doWait for Waiter instances created by this factory */
private final long waiterLatency;
/** Probability that passivation will invalidate Waiter instances */
private final double passivateInvalidationProbability;
/** Count of (makes - destroys) since last reset */
private long activeCount = 0;
/** Count of (makes - destroys) per key since last reset */
private final Map<K,Integer> activeCounts = new HashMap<K,Integer>();
/** Maximum of (makes - destroys) - if exceeded IllegalStateException */
private final long maxActive; // GKOP 1.x calls this maxTotal
/** Maximum of (makes - destroys) per key */
private final long maxActivePerKey; // GKOP 1.x calls this maxActive
public WaiterFactory(long activateLatency, long destroyLatency,
long makeLatency, long passivateLatency, long validateLatency,
long waiterLatency,long maxActive, long maxActivePerKey,
double passivateInvalidationProbability) {
this.activateLatency = activateLatency;
this.destroyLatency = destroyLatency;
this.makeLatency = makeLatency;
this.passivateLatency = passivateLatency;
this.validateLatency = validateLatency;
this.waiterLatency = waiterLatency;
this.maxActive = maxActive;
this.maxActivePerKey = maxActivePerKey;
this.passivateInvalidationProbability = passivateInvalidationProbability;
}
public WaiterFactory(long activateLatency, long destroyLatency,
long makeLatency, long passivateLatency, long validateLatency,
long waiterLatency) {
this(activateLatency, destroyLatency, makeLatency, passivateLatency,
validateLatency, waiterLatency, Long.MAX_VALUE, Long.MAX_VALUE, 0);
}
public WaiterFactory(long activateLatency, long destroyLatency,
long makeLatency, long passivateLatency, long validateLatency,
long waiterLatency,long maxActive) {
this(activateLatency, destroyLatency, makeLatency, passivateLatency,
validateLatency, waiterLatency, maxActive, Long.MAX_VALUE, 0);
}
@Override
public void activateObject(PooledObject<Waiter> obj) throws Exception {
doWait(activateLatency);
obj.getObject().setActive(true);
}
@Override
public void destroyObject(PooledObject<Waiter> obj) throws Exception {
doWait(destroyLatency);
obj.getObject().setValid(false);
obj.getObject().setActive(false);
// Decrement *after* destroy
synchronized (this) {
activeCount--;
}
}
@Override
public PooledObject<Waiter> makeObject() throws Exception {
// Increment and test *before* make
synchronized (this) {
if (activeCount >= maxActive) {
throw new IllegalStateException("Too many active instances: " +
activeCount + " in circulation with maxActive = " + maxActive);
} else {
activeCount++;
}
}
doWait(makeLatency);
return new DefaultPooledObject<Waiter>(new Waiter(false, true, waiterLatency));
}
@Override
public void passivateObject(PooledObject<Waiter> obj) throws Exception {
obj.getObject().setActive(false);
doWait(passivateLatency);
if (Math.random() < passivateInvalidationProbability) {
obj.getObject().setValid(false);
}
}
@Override
public boolean validateObject(PooledObject<Waiter> obj) {
doWait(validateLatency);
return obj.getObject().isValid();
}
protected void doWait(long latency) {
try {
Thread.sleep(latency);
} catch (InterruptedException ex) {
// ignore
}
}
public synchronized void reset() {
activeCount = 0;
if (activeCounts.isEmpty()) {
return;
}
Iterator<K> it = activeCounts.keySet().iterator();
while (it.hasNext()) {
K key = it.next();
activeCounts.put(key, Integer.valueOf(0));
}
}
/**
* @return the maxActive
*/
public synchronized long getMaxActive() {
return maxActive;
}
// KeyedPoolableObjectFactory methods
@Override
public void activateObject(K key, PooledObject<Waiter> obj) throws Exception {
activateObject(obj);
}
@Override
public void destroyObject(K key,PooledObject<Waiter> obj) throws Exception {
destroyObject(obj);
synchronized (this) {
Integer count = activeCounts.get(key);
activeCounts.put(key, Integer.valueOf(count.intValue() - 1));
}
}
@Override
public PooledObject<Waiter> makeObject(K key) throws Exception {
synchronized (this) {
Integer count = activeCounts.get(key);
if (count == null) {
count = Integer.valueOf(1);
activeCounts.put(key, count);
} else {
if (count.intValue() >= maxActivePerKey) {
throw new IllegalStateException("Too many active " +
"instances for key = " + key + ": " + count.intValue() +
" in circulation " + "with maxActivePerKey = " +
maxActivePerKey);
} else {
activeCounts.put(key, Integer.valueOf(count.intValue() + 1));
}
}
}
return makeObject();
}
@Override
public void passivateObject(K key, PooledObject<Waiter> obj) throws Exception {
passivateObject(obj);
}
@Override
public boolean validateObject(K key, PooledObject<Waiter> obj) {
return validateObject(obj);
}
}