blob: c1ee7e9be650aba17d95aac76397da2ddb69190d [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 2021 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.internal.rj.servi;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectState;
import org.apache.commons.pool2.impl.DefaultEvictionPolicy;
import org.apache.commons.pool2.impl.EvictionConfig;
import org.apache.commons.pool2.impl.EvictionPolicy;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.rj.RjInvalidConfigurationException;
import org.eclipse.statet.rj.servi.pool.PoolConfig;
@NonNullByDefault
public class APool2 extends GenericObjectPool<APool2NodeHandler> {
public static final String CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_PROPERTY_KEY= "org.eclipse.statet.rj.servi.pool.client.AllocationRenewPeriod.millis"; //$NON-NLS-1$
public static final int CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_DEFAULT_VALUE= 600_000; // 10 min
private static final int CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_MIN_VALUE= 5_000;
private static final byte CLOSING= 2;
private static final byte CLOSING_FINALLY= 3;
private static final byte CLOSED= 4;
private static final EvictionPolicy<APool2NodeHandler> EVICTION_POLICY= new DefaultEvictionPolicy<>() {
@Override
public boolean evict(final EvictionConfig config,
final PooledObject<APool2NodeHandler> underTest, final int idleCount) {
return (super.evict(config, underTest, idleCount)
|| underTest.getObject().isEvictRequested(0) );
}
};
private static GenericObjectPoolConfig<APool2NodeHandler> createAConfig(final PoolConfig config) {
final GenericObjectPoolConfig<APool2NodeHandler> aConfig= new GenericObjectPoolConfig<>();
aConfig.setLifo(true);
aConfig.setTestOnReturn(true);
aConfig.setTestWhileIdle(false);
aConfig.setTestOnBorrow(false);
aConfig.setBlockWhenExhausted(true);
aConfig.setMaxTotal(config.getMaxTotalCount());
aConfig.setMaxWaitMillis(config.getMaxWaitTime());
aConfig.setMinIdle(config.getMinIdleCount());
aConfig.setMaxIdle(config.getMaxIdleCount());
aConfig.setMinEvictableIdleTimeMillis(0);
aConfig.setSoftMinEvictableIdleTimeMillis(config.getMinIdleTime());
aConfig.setTimeBetweenEvictionRunsMillis(5000);
aConfig.setNumTestsPerEvictionRun(-4);
aConfig.setEvictorShutdownTimeoutMillis(0);
return aConfig;
}
private final String id;
private final APool2NodeObjectFactory factory;
private int clientAllocationRenewPeriodMillis;
private volatile byte state;
private boolean evicting;
private final Object stateLock= new Object();
public APool2(final String id, final APool2NodeObjectFactory factory, final PoolConfig config)
throws RjInvalidConfigurationException {
super(factory, createAConfig(config));
this.id= id;
this.clientAllocationRenewPeriodMillis= getClientAllocationRenewPeriodMillisDefaultValue();
factory.setPool(this);
this.factory= factory;
}
public String getId() {
return this.id;
}
public void setConfig(final PoolConfig config) {
setConfig(createAConfig(config));
}
@Override
public int getMaxIdle() {
if (this.state != 0) {
return 0;
}
return super.getMaxIdle();
}
protected int getClientAllocationRenewPeriodMillisDefaultValue() throws RjInvalidConfigurationException {
try {
int millis= CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_DEFAULT_VALUE;
final String s= System.getProperty(CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_PROPERTY_KEY);
if (s != null) {
millis= Integer.parseInt(s);
}
if (millis != -1 && millis < CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_MIN_VALUE) {
throw new IllegalArgumentException(String.format("%1$s < %2$s",
this.clientAllocationRenewPeriodMillis, CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_MIN_VALUE ));
}
return millis;
}
catch (final Exception e) {
throw new RjInvalidConfigurationException("AllocationRenewPeriod.millis: " + e.getMessage());
}
}
public int getClientAllocationRenewPeriodMillis() {
return this.clientAllocationRenewPeriodMillis;
}
public APool2NodeHandler borrowObject(final String client) throws Exception {
if (this.state != 0) {
throw new IllegalStateException("Pool not open");
}
this.factory.registerArgs(client);
try {
return super.borrowObject();
}
finally {
this.factory.clearArgs();
}
}
public boolean isOpen() {
return (this.state == 0);
}
@Override
public void close() {
throw new UnsupportedOperationException();
}
public void close(final long evictionTimeout) {
synchronized (this.stateLock) {
if (this.state > CLOSING) {
return;
}
this.state= CLOSING;
}
final long evictNanos= APool2NodeHandler.evictNanos(evictionTimeout);
final boolean evictDirect= (evictionTimeout == 0);
try {
Thread.sleep(10);
}
catch (final InterruptedException e) {}
clear();
final ImList<APool2NodeHandler> objects= this.factory.getAllObjects();
for (final APool2NodeHandler poolObj : objects) {
poolObj.doEvict(evictNanos, evictDirect);
}
if (this.state > CLOSING) {
return;
}
setNumTestsPerEvictionRun(512);
setTimeBetweenEvictionRunsMillis(500);
}
protected void closeFinally() {
super.close();
synchronized (this.stateLock) {
this.state= CLOSED;
}
}
@Override
public void evict() throws Exception {
synchronized (this.stateLock) {
if (this.state > CLOSING || this.evicting) {
return;
}
this.evicting= true;
}
try {
int evicted;
do {
evicted= 0;
final long nanos= APool2NodeHandler.safeNanos(System.nanoTime());
final ImList<APool2NodeHandler> objects= this.factory.getAllObjects();
for (final APool2NodeHandler poolObj : objects) {
poolObj.checkClientLost(nanos);
if (poolObj.isEvictRequested(nanos)
&& poolObj.getPooledObject().getState() != PooledObjectState.INVALID) {
try {
invalidateObject(poolObj);
evicted++;
}
catch (final Exception e) {}
}
}
}
while (evicted > 0);
super.evict();
synchronized (this.stateLock) {
if (this.state != CLOSING || this.factory.getNumAll() > 0) {
return;
}
this.state= CLOSING_FINALLY;
}
closeFinally();
}
finally {
this.evicting= false;
}
}
@Override
public EvictionPolicy<APool2NodeHandler> getEvictionPolicy() {
return EVICTION_POLICY;
}
}