| /*=============================================================================# |
| # Copyright (c) 2019, 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.rj.servi.pool; |
| |
| import static org.junit.jupiter.api.Assertions.assertEquals; |
| import static org.junit.jupiter.api.Assertions.assertNotNull; |
| import static org.junit.jupiter.api.Assertions.assertTrue; |
| |
| import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert; |
| import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullLateInit; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.NoSuchElementException; |
| import java.util.concurrent.TimeUnit; |
| |
| import javax.security.auth.login.LoginException; |
| |
| import org.junit.jupiter.api.AfterAll; |
| import org.junit.jupiter.api.AfterEach; |
| import org.junit.jupiter.api.BeforeEach; |
| import org.junit.jupiter.api.Test; |
| import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; |
| |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| import org.eclipse.statet.jcommons.status.NullProgressMonitor; |
| import org.eclipse.statet.jcommons.status.ProgressMonitor; |
| import org.eclipse.statet.jcommons.status.Status; |
| import org.eclipse.statet.jcommons.status.StatusException; |
| |
| import org.eclipse.statet.rj.data.RDataUtils; |
| import org.eclipse.statet.rj.server.util.RJContext; |
| import org.eclipse.statet.rj.servi.RServi; |
| import org.eclipse.statet.rj.servi.RServiUtils; |
| import org.eclipse.statet.rj.servi.jmx.PoolStatusMX; |
| import org.eclipse.statet.rj.servi.node.RServiNodeConfig; |
| |
| |
| @EnabledIfEnvironmentVariable(named= "STATET_TEST_FILES", matches= ".+") |
| @NonNullByDefault |
| public class JMPoolTest extends AbstractServiTest { |
| |
| |
| private static int NODE_STARTUP_MILLIS= 7000; |
| |
| private static int WAIT_IDLE_SEC= 30; |
| |
| private static List<JMPoolServer> allServers= new ArrayList<>(); |
| private static @Nullable JMPoolServer sharedServer; |
| |
| @AfterAll |
| public static void disposeServers() throws Exception { |
| final List<Throwable> exceptions= new ArrayList<>(); |
| try { |
| for (final JMPoolServer server : allServers) { |
| try { |
| server.shutdown(); |
| server.waitForDisposal(0); |
| } |
| catch (final Exception e) { |
| exceptions.add(e); |
| } |
| } |
| } |
| finally { |
| allServers.clear(); |
| |
| Thread.sleep(1000); |
| } |
| |
| reportErrors(exceptions); |
| } |
| |
| |
| private JMPoolServer server= nonNullLateInit(); |
| |
| |
| public JMPoolTest() throws Exception { |
| } |
| |
| |
| @BeforeEach |
| public void initServer() throws Exception { |
| JMPoolServer server= sharedServer; |
| if (server == null) { |
| final RJContext context= ServiTests.getRJContext(); |
| |
| final RServiNodeConfig nodeConfig= new RServiNodeConfig(); |
| nodeConfig.load(getEnvConfiguration("default")); |
| |
| server= new JMPoolServer("JMPoolTest", context); |
| final NetConfig netConfig= new NetConfig(); |
| netConfig.setRegistryEmbed(true); |
| netConfig.setHostAddress("localhost"); |
| netConfig.setRegistryPort(ServiTests.getRegistryPort()); |
| server.setNetConfig(netConfig); |
| server.setNodeConfig(nodeConfig); |
| |
| allServers.add(server); |
| JMPoolTest.sharedServer= server; |
| } |
| this.server= server; |
| } |
| |
| @AfterEach |
| public void stopServer() throws Exception { |
| final List<Throwable> exceptions= new ArrayList<>(); |
| disposeServis(exceptions); |
| try { |
| if (this.server != null) { |
| try { |
| this.server.stop(); |
| } |
| catch (final Throwable e) { |
| exceptions.add(e); |
| } |
| } |
| } |
| catch (final Exception e) { |
| exceptions.add(e); |
| } |
| |
| reportErrors(exceptions); |
| } |
| |
| |
| private RServi getServi(final String id) throws NoSuchElementException, LoginException, StatusException { |
| final RServi servi= RServiUtils.getRServi(nonNullAssert(this.server.getPoolAddress()), id); |
| onServiGet(servi); |
| return servi; |
| } |
| |
| |
| @Test |
| public void test1() throws Exception { |
| final PoolConfig poolConfig= new PoolConfig(); |
| this.server.setPoolConfig(poolConfig); |
| this.server.start(); |
| |
| final RServi servi1= getServi("test1"); |
| assertNodeOperative(servi1); |
| closeServi(servi1); |
| } |
| |
| @Test |
| public void closed() throws Exception { |
| final PoolConfig poolConfig= new PoolConfig(); |
| this.server.setPoolConfig(poolConfig); |
| this.server.start(); |
| |
| final RServi servi1= getServi("test1"); |
| assertNodeOperative(servi1); |
| assertEquals(false, servi1.isClosed()); |
| closeServi(servi1); |
| |
| assertEquals(true, servi1.isClosed()); |
| try { |
| assertNodeOperative(servi1); |
| throw new AssertionError("not closed"); |
| } |
| catch (final StatusException e) { |
| final Status eStatus= e.getStatus(); |
| assertEquals(Status.ERROR, eStatus.getSeverity()); |
| assertTrue(eStatus.getMessage().contains("closed")); |
| } |
| } |
| |
| @Test |
| public void IdleNodes_default() throws Exception { |
| final PoolConfig poolConfig= new PoolConfig(); |
| this.server.setPoolConfig(poolConfig); |
| this.server.start(); |
| |
| assertIdleCount(1); |
| |
| final RServi servi1= getServi("test1"); |
| |
| assertIdleCount(1); |
| |
| assertNodeOperative(servi1); |
| closeServi(servi1); |
| |
| assertIdleCount(2); |
| } |
| |
| @Test |
| public void IdleNodes_ensureMin() throws Exception, |
| InterruptedException { |
| final PoolConfig poolConfig= new PoolConfig(); |
| this.server.setPoolConfig(poolConfig); |
| this.server.start(); |
| |
| assertIdleCount(1); |
| |
| poolConfig.setMinIdleCount(2); |
| this.server.setPoolConfig(poolConfig); |
| |
| assertIdleCount(2); |
| } |
| |
| @Test |
| public void TotalNodes_borrowMax() throws Exception, |
| InterruptedException { |
| final PoolConfig poolConfig= new PoolConfig(); |
| poolConfig.setMaxTotalCount(2); |
| this.server.setPoolConfig(poolConfig); |
| this.server.start(); |
| |
| final RServi servi1= getServi("test1"); |
| final RServi servi2= getServi("test2"); |
| |
| assertNodeOperative(servi1); |
| assertNodeOperative(servi2); |
| |
| Thread.sleep(WAIT_IDLE_SEC); |
| assertIdleCount(0); |
| |
| closeServi(servi1); |
| closeServi(servi2); |
| } |
| |
| @Test |
| public void TotalNodes_borrowTimeout() throws Exception { |
| final PoolConfig poolConfig= new PoolConfig(); |
| poolConfig.setMaxTotalCount(2); |
| this.server.setPoolConfig(poolConfig); |
| this.server.start(); |
| |
| final RServi servi1= getServi("test1"); |
| assertNotNull(servi1); |
| |
| final long t2= System.nanoTime(); |
| final RServi servi2= getServi("test2"); |
| assertNotNull(servi2); |
| { final long d2= TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t2); |
| final long dMax= poolConfig.getMaxWaitTime() + NODE_STARTUP_MILLIS; |
| assertTrue(d2 < dMax, |
| () -> String.format("duration expected: < %1$sms, actual: %2$sms", dMax, d2) ); |
| } |
| |
| final long t3= System.nanoTime(); |
| try { |
| final RServi servi3= getServi("test3"); |
| assertNotNull(servi3); |
| throw new AssertionError("totalCount"); |
| } |
| catch (final NoSuchElementException e) { |
| final long d3= TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t3); |
| final long dExpected= poolConfig.getMaxWaitTime(); |
| final long dTol= 500; |
| assertTrue(d3 > poolConfig.getMaxWaitTime() - dTol && d3 < poolConfig.getMaxWaitTime() + dTol, |
| () -> String.format("duration expected: %1$sms (±%3$sms), actual: %2$sms", dExpected, d3, dTol)); |
| } |
| } |
| |
| @Test |
| public void UsageCount_borrowMax() throws Exception { |
| final ProgressMonitor m= new NullProgressMonitor(); |
| |
| 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 int pid1= RDataUtils.checkSingleIntValue(servi1.evalData("Sys.getpid()", m)); |
| closeServi(servi1); |
| |
| final RServi servi2= getServi("test2"); |
| assertNodeOperative(servi2); |
| final int pid2= RDataUtils.checkSingleIntValue(servi2.evalData("Sys.getpid()", m)); |
| |
| assertTrue(pid1 != pid2); |
| |
| closeServi(servi2); |
| } |
| |
| |
| @Test |
| public void Bug570660() throws Exception { |
| final ProgressMonitor m= new NullProgressMonitor(); |
| |
| final PoolConfig poolConfig= new PoolConfig(); |
| poolConfig.setMaxTotalCount(2); |
| poolConfig.setMinIdleCount(2); |
| this.server.setPoolConfig(poolConfig); |
| this.server.start(); |
| |
| final RServi servi1= getServi("test1"); |
| assertNodeOperative(servi1); |
| |
| closeServi(servi1); |
| } |
| |
| |
| private void assertIdleCount(final int expected) throws InterruptedException { |
| final long t= System.nanoTime(); |
| while (true) { |
| final PoolStatusMX poolStatus= this.server.getPoolStatus(); |
| if (expected == poolStatus.getNumIdling()) { |
| return; |
| } |
| if (TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - t) > WAIT_IDLE_SEC) { |
| assertEquals(expected, poolStatus.getNumIdling(), "num idle"); |
| } |
| Thread.sleep(100); |
| } |
| } |
| |
| } |