Java tutorial
/* * Copyright (C) 2008-2010 Institute for Computational Biomedicine, * Weill Medical College of Cornell University * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package edu.cornell.med.icb.R; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; import org.junit.After; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Test; import org.rosuda.REngine.Rserve.RConnection; import org.rosuda.REngine.Rserve.RserveException; import java.io.StringReader; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * Validates basic functionality of the {@link edu.cornell.med.icb.R.RConnectionPool}. */ public class TestRConnectionPool { /** * A pool configuration with a single server on localhost. */ private static final String POOL_CONFIGURATION_XML = "<RConnectionPool><RConfiguration><RServer host=\"localhost\"/></RConfiguration></RConnectionPool>"; /** * The connection pool under test. We store it so that it can be shutdown properly. */ private RConnectionPool pool; /** * Reset the pool before each test. */ @Before public void startup() { pool = null; } /** * Shutdown the pool after each test. */ @After public void shutdown() { if (pool != null) { pool.close(); } } /** * Validates that the connection pool will properly hand out a connection * to an active Rserve process. Note: this test assumes that a Rserve * process is already running on localhost using the default port. * @throws ConfigurationException if there is a problem setting up the default test connection * @throws RserveException if there is a problem with the connection to the Rserve process */ @Test public void validConnection() throws ConfigurationException, RserveException { final XMLConfiguration configuration = new XMLConfiguration(); configuration.load(new StringReader(POOL_CONFIGURATION_XML)); pool = new RConnectionPool(configuration); assertNotNull("Connection pool should never be null", pool); assertFalse("Everybody in - the pool should be open!", pool.isClosed()); assertEquals("There should be one connection", 1, pool.getNumberOfConnections()); assertEquals("No connection should be active", 0, pool.getNumberOfActiveConnections()); assertEquals("The connection should be idle", 1, pool.getNumberOfIdleConnections()); // get a connection from the pool final RConnection connection = pool.borrowConnection(); assertNotNull("Connection should not be null", connection); assertTrue("The connection should be connected to the server", connection.isConnected()); // there should be no more connections available assertEquals("There should be one connections", 1, pool.getNumberOfConnections()); assertEquals("The connection should be active", 1, pool.getNumberOfActiveConnections()); assertEquals("There should be no idle connections", 0, pool.getNumberOfIdleConnections()); // but try and get another final RConnection connection2 = pool.borrowConnection(100, TimeUnit.MILLISECONDS); assertNull("The first connection hasn't been returned - it should be null", connection2); // it didn't give us a connection, but make sure the counts didn't change assertEquals("There should be one connections", 1, pool.getNumberOfConnections()); assertEquals("The connection should be active", 1, pool.getNumberOfActiveConnections()); assertEquals("There should be no idle connections", 0, pool.getNumberOfIdleConnections()); // return the connection to the pool pool.returnConnection(connection); // now there should be one available in the pool assertEquals("There should be one connection", 1, pool.getNumberOfConnections()); assertEquals("No connection should be active", 0, pool.getNumberOfActiveConnections()); assertEquals("The connection should be idle", 1, pool.getNumberOfIdleConnections()); // make sure we can get another good connection after returning the previous one final RConnection connection3 = pool.borrowConnection(100, TimeUnit.MILLISECONDS); assertNotNull("Connection should not be null", connection3); assertTrue("The connection should be connected to the server", connection3.isConnected()); // there should be no more connections available assertEquals("There should be one connections", 1, pool.getNumberOfConnections()); assertEquals("The connection should be active", 1, pool.getNumberOfActiveConnections()); assertEquals("There should be no idle connections", 0, pool.getNumberOfIdleConnections()); pool.returnConnection(connection3); } /** * Validates that the connection pool will not allow connections to be handed out * when no valid servers were set. * @throws RserveException if there is an issue connecting with an Rserver */ @Test(expected = IllegalStateException.class) public void noValidServers() throws RserveException { // set up a pool with an empty configuration pool = new RConnectionPool(new XMLConfiguration()); assertNotNull("Connection pool should never be null", pool); assertTrue("The pool should not be open", pool.isClosed()); assertEquals("There should be no connections", 0, pool.getNumberOfConnections()); assertEquals("There should be no connections", 0, pool.getNumberOfActiveConnections()); assertEquals("There should be no connections", 0, pool.getNumberOfIdleConnections()); // if we try to get a connection, we shouldn't be able to - there are none configured pool.borrowConnection(); } /** * Validates that attempting to return a connection that has been closed throws no error. * @throws ConfigurationException if there is a problem setting up the default test connection * @throws RserveException if there is a problem with the connection to the Rserve process */ @Test public void returnClosedConnection() throws ConfigurationException, RserveException { final XMLConfiguration configuration = new XMLConfiguration(); configuration.load(new StringReader(POOL_CONFIGURATION_XML)); pool = new RConnectionPool(configuration); assertNotNull("Connection pool should never be null", pool); assertFalse("Everybody in - the pool should be open!", pool.isClosed()); assertEquals("There should be one connection", 1, pool.getNumberOfConnections()); assertEquals("No connections should be active", 0, pool.getNumberOfActiveConnections()); assertEquals("The connection should be idle", 1, pool.getNumberOfIdleConnections()); // get a connection from the pool final RConnection connection = pool.borrowConnection(); assertNotNull("Connection should not be null", connection); assertTrue("The connection should be connected to the server", connection.isConnected()); connection.close(); assertFalse("The connection should not be connected to the server anymore", connection.isConnected()); // return the connection to the pool pool.returnConnection(connection); // and the connection should remain closed assertFalse("The connection should not be connected to the server anymore", connection.isConnected()); assertEquals("There should be one connection", 1, pool.getNumberOfConnections()); assertEquals("No connections should be active", 0, pool.getNumberOfActiveConnections()); assertEquals("The connection should be idle", 1, pool.getNumberOfIdleConnections()); // get another connection from the pool final RConnection connection2 = pool.borrowConnection(); assertNotNull("Connection should not be null", connection2); assertTrue("The connection should be connected to the server", connection2.isConnected()); } /** * Validates that an attempt to return an invalid connection to the pool throws an error. * @throws ConfigurationException if there is a problem setting up the default test connection */ @Test(expected = NullPointerException.class) public void returnNullConnection() throws ConfigurationException { final XMLConfiguration configuration = new XMLConfiguration(); configuration.load(new StringReader(POOL_CONFIGURATION_XML)); pool = new RConnectionPool(configuration); pool.returnConnection(null); } /** * Validates that the pool can be shut down properly and also that attempting to * get a connection after the pool has been shutdown throws an error. * @throws ConfigurationException if there is a problem setting up the default test connection * @throws RserveException if there is an issue connecting with an Rserver */ @Test(expected = IllegalStateException.class) public void borrowConnectionAfterShutdown() throws ConfigurationException, RserveException { final XMLConfiguration configuration = new XMLConfiguration(); configuration.load(new StringReader(POOL_CONFIGURATION_XML)); pool = new RConnectionPool(configuration); assertNotNull("Connection pool should never be null", pool); assertFalse("Everybody in - the pool should be open!", pool.isClosed()); pool.close(); assertNotNull("Connection pool should never be null", pool); assertTrue("Everybody out of the pool!", pool.isClosed()); assertEquals("There should be no connections", 0, pool.getNumberOfConnections()); assertEquals("No connections should be active", 0, pool.getNumberOfActiveConnections()); assertEquals("No connections should be idle", 0, pool.getNumberOfIdleConnections()); pool.borrowConnection(); } /** * Validates that the pool can be shut down properly and also that attempting to * return a connection after the pool has been shutdown throws an error. * @throws ConfigurationException if there is a problem setting up the default test connection * @throws RserveException if there is a problem with the connection to the Rserve process */ @Test(expected = IllegalStateException.class) public void returnConnectionAfterShutdown() throws ConfigurationException, RserveException { final XMLConfiguration configuration = new XMLConfiguration(); configuration.load(new StringReader(POOL_CONFIGURATION_XML)); pool = new RConnectionPool(configuration); final RConnection connection = pool.borrowConnection(); assertNotNull("Connection should not be null", connection); assertTrue("The connection should be connected to the server", connection.isConnected()); pool.close(); assertNotNull("Connection pool should never be null", pool); assertTrue("Everybody out of the pool!", pool.isClosed()); assertEquals("There should be no connections", 0, pool.getNumberOfConnections()); assertEquals("No connections should be active", 0, pool.getNumberOfActiveConnections()); assertEquals("No connections should be idle", 0, pool.getNumberOfIdleConnections()); assertNotNull("Connection should not be null", connection); assertFalse("The connection should not be connected to the server", connection.isConnected()); pool.returnConnection(connection); } /** * Validates that the connections that have been invalidated are removed from the pool properly. * @throws ConfigurationException if there is a problem setting up the default test connection * @throws RserveException if there is an issue connecting with an Rserver */ @Test public void invalidateConnection() throws ConfigurationException, RserveException { final XMLConfiguration configuration = new XMLConfiguration(); configuration.load(new StringReader(POOL_CONFIGURATION_XML)); pool = new RConnectionPool(configuration); final RConnection connection = pool.borrowConnection(); assertNotNull("Connection should not be null", connection); assertTrue("The connection should be connected to the server", connection.isConnected()); assertFalse("The pool should be open", pool.isClosed()); assertEquals("There should be one connection", 1, pool.getNumberOfConnections()); assertEquals("The connection should active", 1, pool.getNumberOfActiveConnections()); assertEquals("No connections should be idle", 0, pool.getNumberOfIdleConnections()); pool.invalidateConnection(connection); // the connection should no longer be connected assertNotNull("Connection should not be null", connection); assertFalse("The connection should not be connected to the server", connection.isConnected()); assertEquals("There should be no connections", 0, pool.getNumberOfConnections()); assertEquals("No connections should be active", 0, pool.getNumberOfActiveConnections()); assertEquals("No connections should be idle", 0, pool.getNumberOfIdleConnections()); assertTrue("The pool should be closed", pool.isClosed()); } /** * Checks that two threads actually get the same connection pool. * @throws InterruptedException if the threads are interrupted during the test */ @Test public void validateSingleton() throws InterruptedException { final RConnectionPool[] pools = new RConnectionPool[2]; final CountDownLatch latch = new CountDownLatch(2); final ExecutorService threadPool = Executors.newCachedThreadPool(); try { threadPool.submit(new Callable<Boolean>() { public Boolean call() { pools[0] = RConnectionPool.getInstance(); latch.countDown(); return true; } }); threadPool.submit(new Callable<Boolean>() { public Boolean call() { pools[1] = RConnectionPool.getInstance(); latch.countDown(); return true; } }); latch.await(); assertNotNull("Connection pool should never be null", pools[0]); assertNotNull("Connection pool should never be null", pools[1]); assertEquals("Pools should be the same", pools[0], pools[1]); } finally { threadPool.shutdown(); if (pools[0] != null) { pools[0].close(); } if (pools[1] != null) { pools[1].close(); } } } /** * Validates that a pool can be reopened after it has been closed. * @throws ConfigurationException if there is a problem setting up the default test connection */ @Test public void closeAndReopen() throws ConfigurationException { final XMLConfiguration configuration = new XMLConfiguration(); configuration.load(new StringReader(POOL_CONFIGURATION_XML)); pool = new RConnectionPool(configuration); assertFalse("The pool should be open", pool.isClosed()); assertEquals("There should be one connection", 1, pool.getNumberOfConnections()); assertEquals("No connections should be active", 0, pool.getNumberOfActiveConnections()); assertEquals("The connections should be idle", 1, pool.getNumberOfIdleConnections()); pool.close(); assertEquals("There should be no connections", 0, pool.getNumberOfConnections()); assertEquals("No connections should be active", 0, pool.getNumberOfActiveConnections()); assertEquals("No connections should be idle", 0, pool.getNumberOfIdleConnections()); assertTrue("The pool should be closed", pool.isClosed()); pool.reopen(); assertFalse("The pool should be open", pool.isClosed()); assertEquals("There should be one connection", 1, pool.getNumberOfConnections()); assertEquals("No connections should be active", 0, pool.getNumberOfActiveConnections()); assertEquals("The connections should be idle", 1, pool.getNumberOfIdleConnections()); } /** * Tests re-establishing connections to embedded Rserve instances. * @throws ConfigurationException * @throws RserveException */ // @Test - TODO - disabled for now since the command is different on each platform public void embeddedServer() throws ConfigurationException, RserveException { final String xml = "<RConnectionPool><RConfiguration><RServer host=\"localhost\" port=\"6312\" embedded=\"true\" command=\"C:\\\\Program\\ Files\\\\R\\\\R-2.6.0\\\\library\\\\Rserve\\\\Rserve_d.exe\"/></RConfiguration></RConnectionPool>"; final XMLConfiguration configuration = new XMLConfiguration(); configuration.load(new StringReader(xml)); pool = new RConnectionPool(configuration); assertEquals("There should be one connection", 1, pool.getNumberOfConnections()); assertEquals("No connections should be active", 0, pool.getNumberOfActiveConnections()); assertEquals("The connections should be idle", 1, pool.getNumberOfIdleConnections()); final RConnection connection = pool.borrowConnection(); assertNotNull(connection); assertTrue(connection.isConnected()); assertEquals("There should be one connection", 1, pool.getNumberOfConnections()); assertEquals("The connections should be active", 1, pool.getNumberOfActiveConnections()); assertEquals("The connections should not be idle", 0, pool.getNumberOfIdleConnections()); final RConnection newConnection = pool.reEstablishConnection(connection); assertNotNull(newConnection); assertFalse(connection.isConnected()); assertTrue(newConnection.isConnected()); assertEquals("There should be one connection", 1, pool.getNumberOfConnections()); assertEquals("The connections should be active", 1, pool.getNumberOfActiveConnections()); assertEquals("The connections should not be idle", 0, pool.getNumberOfIdleConnections()); pool.returnConnection(newConnection); assertEquals("There should be one connection", 1, pool.getNumberOfConnections()); assertEquals("No connections should be active", 0, pool.getNumberOfActiveConnections()); assertEquals("The connections should be idle", 1, pool.getNumberOfIdleConnections()); } }