Bug 567221: Improve configuration of detection of lost clients

  - Add support to disable detection of lost clients
  - Improve spec of timings in JMPoolTest

Change-Id: Id5d2599c730bc0f0116b06367753b22692ad808d
diff --git a/servi/org.eclipse.statet.rj.servi-tests/src/org/eclipse/statet/rj/servi/pool/AbstractServiTest.java b/servi/org.eclipse.statet.rj.servi-tests/src/org/eclipse/statet/rj/servi/pool/AbstractServiTest.java
index 04cf27e..02401cf 100644
--- a/servi/org.eclipse.statet.rj.servi-tests/src/org/eclipse/statet/rj/servi/pool/AbstractServiTest.java
+++ b/servi/org.eclipse.statet.rj.servi-tests/src/org/eclipse/statet/rj/servi/pool/AbstractServiTest.java
@@ -160,11 +160,16 @@
 	}
 	
 	
-	protected void assertNodeOperative(final RServi servi) throws StatusException, UnexpectedRDataException {
-		final ProgressMonitor m= new NullProgressMonitor();
+	protected void assertNodeOperative(final RServi servi,
+			final ProgressMonitor m) throws StatusException, UnexpectedRDataException {
 		final int i= this.testInt++;
 		final RObject result= servi.evalData(i + "L + 1L", m);
 		assertEquals(i + 1, RDataUtils.checkSingleIntValue(result));
 	}
 	
+	protected void assertNodeOperative(
+			final RServi servi) throws StatusException, UnexpectedRDataException {
+		assertNodeOperative(servi, new NullProgressMonitor());
+	}
+	
 }
diff --git a/servi/org.eclipse.statet.rj.servi-tests/src/org/eclipse/statet/rj/servi/pool/JMPoolTest.java b/servi/org.eclipse.statet.rj.servi-tests/src/org/eclipse/statet/rj/servi/pool/JMPoolTest.java
index b4efd31..67732ef 100644
--- a/servi/org.eclipse.statet.rj.servi-tests/src/org/eclipse/statet/rj/servi/pool/JMPoolTest.java
+++ b/servi/org.eclipse.statet.rj.servi-tests/src/org/eclipse/statet/rj/servi/pool/JMPoolTest.java
@@ -22,7 +22,7 @@
 import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
 import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullLateInit;
 
-import static org.eclipse.statet.internal.rj.servi.APool2.CLIENT_CHECK_INTERVAL_MILLIS_PROPERTY_KEY;
+import static org.eclipse.statet.internal.rj.servi.APool2.CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_PROPERTY_KEY;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -63,9 +63,9 @@
 public class JMPoolTest extends AbstractServiTest {
 	
 	
-	private static int NODE_STARTUP_MILLIS= 7000;
+	private static int NODE_STARTUP_MILLIS= 7_000;
 	
-	private static int WAIT_IDLE_SEC= 30;
+	private static int WAIT_IDLE_MILLIS= 30_000;
 	
 	private static List<JMPoolServer> allServers= new ArrayList<>();
 	private static @Nullable JMPoolServer sharedServer;
@@ -234,7 +234,7 @@
 		assertNodeOperative(servi1);
 		assertNodeOperative(servi2);
 		
-		Thread.sleep(WAIT_IDLE_SEC);
+		Thread.sleep(WAIT_IDLE_MILLIS);
 		assertIdleCount(0);
 		
 		closeServi(servi1);
@@ -277,7 +277,8 @@
 	
 	@Test
 	public void UsageCount_borrowMax() throws Exception {
-		final ProgressMonitor m= new NullProgressMonitor();
+		final ProgressMonitor m1= new NullProgressMonitor();
+		final ProgressMonitor m2= new NullProgressMonitor();
 		
 		final PoolConfig poolConfig= new PoolConfig();
 		poolConfig.setMaxTotalCount(1);
@@ -286,13 +287,13 @@
 		this.server.start();
 		
 		final RServi servi1= getServi("test1");
-		assertNodeOperative(servi1);
-		final int pid1= RDataUtils.checkSingleIntValue(servi1.evalData("Sys.getpid()", m));
+		assertNodeOperative(servi1, m1);
+		final int pid1= RDataUtils.checkSingleIntValue(servi1.evalData("Sys.getpid()", m1));
 		closeServi(servi1);
 		
 		final RServi servi2= getServi("test2");
-		assertNodeOperative(servi2);
-		final int pid2= RDataUtils.checkSingleIntValue(servi2.evalData("Sys.getpid()", m));
+		assertNodeOperative(servi2, m2);
+		final int pid2= RDataUtils.checkSingleIntValue(servi2.evalData("Sys.getpid()", m2));
 		
 		assertTrue(pid1 != pid2);
 		
@@ -302,10 +303,9 @@
 	
 	@Test
 	public void ClientCheck_alive() throws Exception {
-		final ProgressMonitor m= new NullProgressMonitor();
-		final int intervalSec= 10;
-		System.setProperty(CLIENT_CHECK_INTERVAL_MILLIS_PROPERTY_KEY,
-				Long.toString(TimeUnit.SECONDS.toMillis(intervalSec)) );
+		final int periodMillis= 15_000;
+		System.setProperty(CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_PROPERTY_KEY,
+				Integer.toString(periodMillis) );
 		try {
 			final PoolConfig poolConfig= new PoolConfig();
 			poolConfig.setMaxTotalCount(1);
@@ -319,22 +319,21 @@
 			final PoolNodeObject node1= assertSinglePoolNode();
 			
 			awaitStateCondition(node1, (state) -> (state != PoolNodeState.ALLOCATED),
-					(int)(2.5 * intervalSec) + WAIT_IDLE_SEC );
+					(int)(1.2 * periodMillis) + WAIT_IDLE_MILLIS );
 			skipChecking(node1);
 			assertEquals(PoolNodeState.ALLOCATED, node1.getState());
 			assertNodeOperative(servi1);
 		}
 		finally {
-			System.clearProperty(CLIENT_CHECK_INTERVAL_MILLIS_PROPERTY_KEY);
+			System.clearProperty(CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_PROPERTY_KEY);
 		}
 	}
 	
 	@Test
 	public void ClientCheck_dead() throws Exception {
-		final ProgressMonitor m= new NullProgressMonitor();
-		final int intervalSec= 10;
-		System.setProperty(CLIENT_CHECK_INTERVAL_MILLIS_PROPERTY_KEY,
-				Long.toString(TimeUnit.SECONDS.toMillis(intervalSec)) );
+		final int periodMillis= 15_000;
+		System.setProperty(CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_PROPERTY_KEY,
+				Integer.toString(periodMillis) );
 		try {
 			final PoolConfig poolConfig= new PoolConfig();
 			poolConfig.setMaxTotalCount(1);
@@ -353,21 +352,47 @@
 			clientCheckJob.cancel(false);
 			
 			awaitStateCondition(node1, (state) -> (state != PoolNodeState.ALLOCATED),
-					(int)(2.5 * intervalSec) + WAIT_IDLE_SEC );
+					(int)(1.2 * periodMillis) + WAIT_IDLE_MILLIS );
 			skipChecking(node1);
 			assertEquals(PoolNodeState.DISPOSED, node1.getState());
 			Assertions.assertThrows(StatusException.class, () -> assertNodeOperative(servi1));
 		}
 		finally {
-			System.clearProperty(CLIENT_CHECK_INTERVAL_MILLIS_PROPERTY_KEY);
+			System.clearProperty(CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_PROPERTY_KEY);
+		}
+	}
+	
+	@Test
+	public void ClientCheck_disabled() throws Exception {
+		System.setProperty(CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_PROPERTY_KEY, "-1");
+		try {
+			final PoolConfig poolConfig= new PoolConfig();
+			poolConfig.setMaxTotalCount(1);
+			poolConfig.setMaxUsageCount(1);
+			this.server.setPoolConfig(poolConfig);
+			this.server.start();
+			
+			final RServi servi1= getServi("test1");
+			assertNodeOperative(servi1);
+			
+//			final PoolNodeObject node1= assertSinglePoolNode();
+//			
+//			awaitStateCondition(node1, (state) -> (state != PoolNodeState.ALLOCATED),
+//					(int)(1.2 * APool2.CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_DEFAULT_VALUE) + WAIT_IDLE_MILLIS );
+//			skipChecking(node1);
+//			assertEquals(PoolNodeState.ALLOCATED, node1.getState());
+//			assertNodeOperative(servi1);
+			
+			closeServi(servi1);
+		}
+		finally {
+			System.clearProperty(CLIENT_ALLOCATION_RENEW_PERIOD_MILLIS_PROPERTY_KEY);
 		}
 	}
 	
 	
 	@Test
 	public void PoolNode_getState() throws Exception {
-		final ProgressMonitor m= new NullProgressMonitor();
-		
 		final PoolConfig poolConfig= new PoolConfig();
 		poolConfig.setMaxTotalCount(1);
 		poolConfig.setMinIdleCount(1);
@@ -392,8 +417,6 @@
 	
 	@Test
 	public void PoolNode_getClientLabel() throws Exception {
-		final ProgressMonitor m= new NullProgressMonitor();
-		
 		final PoolConfig poolConfig= new PoolConfig();
 		poolConfig.setMaxTotalCount(1);
 		poolConfig.setMinIdleCount(1);
@@ -415,8 +438,6 @@
 	
 	@Test
 	public void Bug570660() throws Exception {
-		final ProgressMonitor m= new NullProgressMonitor();
-		
 		final PoolConfig poolConfig= new PoolConfig();
 		poolConfig.setMaxTotalCount(2);
 		poolConfig.setMinIdleCount(2);
@@ -443,7 +464,7 @@
 			if (expected == poolStatus.getNumIdling()) {
 				return;
 			}
-			if (TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - t) > WAIT_IDLE_SEC) {
+			if (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t) > WAIT_IDLE_MILLIS) {
 				assertEquals(expected, poolStatus.getNumIdling(), "num idle");
 			}
 			Thread.sleep(100);
@@ -451,17 +472,17 @@
 	}
 	
 	private void skipChecking(final PoolNodeObject node) throws InterruptedException {
-		awaitStateCondition(node, (state) -> (state != PoolNodeState.CHECKING), WAIT_IDLE_SEC);
+		awaitStateCondition(node, (state) -> (state != PoolNodeState.CHECKING), WAIT_IDLE_MILLIS);
 	}
 	
 	private void awaitStateCondition(final PoolNodeObject node,
-			final Predicate<PoolNodeState> condition, final int timeoutSec) throws InterruptedException {
+			final Predicate<PoolNodeState> condition, final int timeoutMillis) throws InterruptedException {
 		final long t= System.nanoTime();
 		while (true) {
 			if (condition.test(node.getState())) {
 				return;
 			}
-			if (TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - t) > timeoutSec) {
+			if (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t) > timeoutMillis) {
 				return;
 			}
 			Thread.sleep(100);
diff --git a/servi/org.eclipse.statet.rj.servi/srcServiPool/org/eclipse/statet/internal/rj/servi/APool2.java b/servi/org.eclipse.statet.rj.servi/srcServiPool/org/eclipse/statet/internal/rj/servi/APool2.java
index c148fdc..d0c2633 100644
--- a/servi/org.eclipse.statet.rj.servi/srcServiPool/org/eclipse/statet/internal/rj/servi/APool2.java
+++ b/servi/org.eclipse.statet.rj.servi/srcServiPool/org/eclipse/statet/internal/rj/servi/APool2.java
@@ -25,6 +25,7 @@
 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;
 
 
@@ -32,7 +33,10 @@
 public class APool2 extends GenericObjectPool<APool2NodeHandler> {
 	
 	
-	public static final String CLIENT_CHECK_INTERVAL_MILLIS_PROPERTY_KEY= "org.eclipse.statet.rj.servi.pool.ClientCheck.Interval.millis"; //$NON-NLS-1$
+	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;
@@ -78,19 +82,18 @@
 	
 	private final APool2NodeFactory factory;
 	
-	private int clientCheckIntervalMillis;
+	private int clientAllocationRenewPeriodMillis;
 	
 	private volatile byte state;
 	private boolean evicting;
 	private final Object stateLock= new Object();
 	
 	
-	public APool2(final String id, final APool2NodeFactory factory, final PoolConfig config) {
+	public APool2(final String id, final APool2NodeFactory factory, final PoolConfig config)
+			throws RjInvalidConfigurationException {
 		super(factory, createAConfig(config));
 		this.id= id;
-		{	final String s= System.getProperty(CLIENT_CHECK_INTERVAL_MILLIS_PROPERTY_KEY);
-			this.clientCheckIntervalMillis= (s != null) ? Integer.parseInt(s) : 60000;
-		}
+		this.clientAllocationRenewPeriodMillis= getClientAllocationRenewPeriodMillisDefaultValue();
 		
 		factory.setPool(this);
 		this.factory= factory;
@@ -114,8 +117,26 @@
 		return super.getMaxIdle();
 	}
 	
-	public int getClientCheckIntervalMillis() {
-		return this.clientCheckIntervalMillis;
+	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;
 	}
 	
 	
diff --git a/servi/org.eclipse.statet.rj.servi/srcServiPool/org/eclipse/statet/internal/rj/servi/APool2NodeHandler.java b/servi/org.eclipse.statet.rj.servi/srcServiPool/org/eclipse/statet/internal/rj/servi/APool2NodeHandler.java
index 7de6298..5646f15 100644
--- a/servi/org.eclipse.statet.rj.servi/srcServiPool/org/eclipse/statet/internal/rj/servi/APool2NodeHandler.java
+++ b/servi/org.eclipse.statet.rj.servi/srcServiPool/org/eclipse/statet/internal/rj/servi/APool2NodeHandler.java
@@ -57,8 +57,8 @@
 	
 	private volatile long accessId= -1;
 	
-	private int checkInterval;
-	private long checkTimestamp;
+	private int allocationRenewPeriodMillis;
+	private long allocationRenewTimestamp;
 	
 	
 	public APool2NodeHandler(final APool2 pool) {
@@ -137,8 +137,8 @@
 		super.bindClient(name, host);
 		synchronized (this) {
 			this.accessId= this.p.getBorrowedCount();
-			this.checkInterval= this.pool.getClientCheckIntervalMillis();
-			this.checkTimestamp= System.nanoTime();
+			this.allocationRenewPeriodMillis= this.pool.getClientAllocationRenewPeriodMillis();
+			this.allocationRenewTimestamp= System.nanoTime();
 		}
 	}
 	
@@ -191,7 +191,9 @@
 	
 	@Override
 	public int getCheckIntervalMillis() throws RemoteException {
-		return this.checkInterval;
+		final int allocationRenewPeriodMillis;
+		return ((allocationRenewPeriodMillis= this.allocationRenewPeriodMillis) != -1) ? 
+				(int)(allocationRenewPeriodMillis / 2.125) - 1000 : -1;
 	}
 	
 	@Override
@@ -199,19 +201,24 @@
 		if (this.accessId != accessId) {
 			throw new RjClosedException("RServi instance is no longer valid.");
 		}
-		this.checkTimestamp= System.nanoTime();
+		this.allocationRenewTimestamp= System.nanoTime();
 	}
 	
 	void checkClientLost(final long nanos) {
-		if (this.accessId == -1
-				|| nanos - this.checkTimestamp < (long)(2.1 * this.checkInterval + 1000) * 1_000_000
-				|| getPooledObject().getState() == PooledObjectState.INVALID ) {
-			return;
+		{	final int allocationRenewPeriodMillis;
+			if (this.accessId == -1
+					|| (allocationRenewPeriodMillis= this.allocationRenewPeriodMillis) == -1
+					|| nanos - this.allocationRenewTimestamp < allocationRenewPeriodMillis * 1_000_000L
+					|| getPooledObject().getState() == PooledObjectState.INVALID ) {
+				return;
+			}
 		}
 		String clientLabel;
 		synchronized (this) {
+			final int allocationRenewPeriodMillis;
 			if (this.accessId == -1
-					|| nanos - this.checkTimestamp < (long)(2.1 * this.checkInterval + 1000) * 1_000_000 ) {
+					|| (allocationRenewPeriodMillis= this.allocationRenewPeriodMillis) == -1
+					|| nanos - this.allocationRenewTimestamp < allocationRenewPeriodMillis * 1_000_000L ) {
 				return;
 			}
 			clientLabel= getClientLabel();