blob: c6507cfee86d11e83e0e1057d988199ba1925ac8 [file]
/*
* 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
*
* https://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.pool3;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.pool3.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.
*
* @param <K> The type of keys managed by this factory.
*/
public class WaiterFactory<K> implements PooledObjectFactory<Waiter, IllegalStateException>, KeyedPooledObjectFactory<K, Waiter, RuntimeException> {
/** Integer value 0. */
private static final Integer ZERO = Integer.valueOf(0);
/** Integer value 1. */
private static final Integer ONE = Integer.valueOf(1);
/** 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;
/** Count of (makes - destroys) per key since last reset */
private final Map<K, Integer> activeCounts = new HashMap<>();
/** 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(final long activateLatency, final long destroyLatency, final long makeLatency, final long passivateLatency, final long validateLatency,
final long waiterLatency) {
this(activateLatency, destroyLatency, makeLatency, passivateLatency, validateLatency, waiterLatency, Long.MAX_VALUE, Long.MAX_VALUE, 0);
}
public WaiterFactory(final long activateLatency, final long destroyLatency, final long makeLatency, final long passivateLatency, final long validateLatency,
final long waiterLatency, final long maxActive) {
this(activateLatency, destroyLatency, makeLatency, passivateLatency, validateLatency, waiterLatency, maxActive, Long.MAX_VALUE, 0);
}
public WaiterFactory(final long activateLatency, final long destroyLatency, final long makeLatency, final long passivateLatency, final long validateLatency,
final long waiterLatency, final long maxActive, final long maxActivePerKey, final 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;
}
@Override
public void activateObject(final K key, final PooledObject<Waiter> obj) {
activateObject(obj);
}
@Override
public void activateObject(final PooledObject<Waiter> obj) {
doWait(activateLatency);
obj.getObject().setActive(true);
}
@Override
public void destroyObject(final K key, final PooledObject<Waiter> obj) {
destroyObject(obj);
synchronized (this) {
activeCounts.computeIfPresent(key, (k, v) -> Integer.valueOf(v.intValue() - 1));
}
}
@Override
public void destroyObject(final PooledObject<Waiter> obj) {
doWait(destroyLatency);
obj.getObject().setValid(false);
obj.getObject().setActive(false);
// Decrement *after* destroy
synchronized (this) {
activeCount--;
}
}
protected void doWait(final long latency) {
if (latency == 0) {
return;
}
Waiter.sleepQuietly(latency);
}
/**
* @return the maxActive
*/
public synchronized long getMaxActive() {
return maxActive;
}
@Override
public PooledObject<Waiter> makeObject() {
// Increment and test *before* make
synchronized (this) {
if (activeCount >= maxActive) {
throw new IllegalStateException("Too many active instances: " +
activeCount + " in circulation with maxActive = " + maxActive);
}
activeCount++;
}
doWait(makeLatency);
return new DefaultPooledObject<>(new Waiter(false, true, waiterLatency));
}
@Override
public PooledObject<Waiter> makeObject(final K key) {
synchronized (this) {
activeCounts.merge(key, ONE, (v1, v2) -> {
if (v1.intValue() >= maxActivePerKey) {
throw new IllegalStateException("Too many active " +
"instances for key = " + key + ": " + v1.intValue() +
" in circulation with maxActivePerKey = " +
maxActivePerKey);
}
return Integer.valueOf(v1.intValue() + 1);
});
}
return makeObject();
}
@Override
public void passivateObject(final K key, final PooledObject<Waiter> obj) {
passivateObject(obj);
}
@Override
public void passivateObject(final PooledObject<Waiter> obj) {
obj.getObject().setActive(false);
doWait(passivateLatency);
if (Math.random() < passivateInvalidationProbability) {
obj.getObject().setValid(false);
}
}
public synchronized void reset() {
activeCount = 0;
if (activeCounts.isEmpty()) {
return;
}
activeCounts.keySet().forEach(key -> activeCounts.put(key, ZERO));
}
@Override
public boolean validateObject(final K key, final PooledObject<Waiter> obj) {
return validateObject(obj);
}
@Override
public boolean validateObject(final PooledObject<Waiter> obj) {
doWait(validateLatency);
return obj.getObject().isValid();
}
}