INT-1871 TCP CachingClientConnectionFactory
Pool based on algorithm used for spring-integration-file CachingSessionFactory introduced by INT-2146. Refactored that code to use the common SimplePool. One difference to the previous implementation is the ability to change the pool size dynamically. If the size is reduced and more than the new size are in use, items are closed as they are returned until the pool size is as requested. Initial commit. Allow Pool Size Changes Factor out Pool Polishing Pool Tests Default forever Javadocs, File Polishing INT-1871 PR Polishing * Consistent/cleaner method names * Track checkouts; reject release of 'foreign' objects. * Add 'getAllocatedCount()'
This commit is contained in:
committed by
Oleg Zhurakousky
parent
ab0989d73a
commit
411aa296a4
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* http://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.springframework.integration.ip.tcp.connection;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.serializer.Deserializer;
|
||||
import org.springframework.core.serializer.Serializer;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.util.SimplePool;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 2.2
|
||||
*
|
||||
*/
|
||||
public class CachingClientConnectionFactory extends AbstractClientConnectionFactory {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final AbstractClientConnectionFactory targetConnectionFactory;
|
||||
|
||||
private final SimplePool<TcpConnection> pool;
|
||||
|
||||
public CachingClientConnectionFactory(AbstractClientConnectionFactory target, int poolSize) {
|
||||
super("", 0);
|
||||
// override single-use to true to force "close" after use
|
||||
target.setSingleUse(true);
|
||||
this.targetConnectionFactory = target;
|
||||
pool = new SimplePool<TcpConnection>(poolSize, new SimplePool.PoolItemCallback<TcpConnection>() {
|
||||
|
||||
public TcpConnection createForPool() {
|
||||
try {
|
||||
return targetConnectionFactory.getConnection();
|
||||
} catch (Exception e) {
|
||||
throw new MessagingException("Failed to obtain connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isStale(TcpConnection connection) {
|
||||
return !connection.isOpen();
|
||||
}
|
||||
|
||||
public void removedFromPool(TcpConnection connection) {
|
||||
connection.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setConnectionWaitTimeout(int connectionWaitTimeout) {
|
||||
this.pool.setWaitTimeout(connectionWaitTimeout);
|
||||
}
|
||||
|
||||
public synchronized void setPoolSize(int poolSize) {
|
||||
this.pool.setPoolSize(poolSize);
|
||||
}
|
||||
|
||||
public int getPoolSize() {
|
||||
return this.pool.getPoolSize();
|
||||
}
|
||||
|
||||
public int getIdleCount() {
|
||||
return this.pool.getIdleCount();
|
||||
}
|
||||
|
||||
public int getActiveCount() {
|
||||
return this.pool.getActiveCount();
|
||||
}
|
||||
|
||||
public int getAllocatedCount() {
|
||||
return this.pool.getAllocatedCount();
|
||||
}
|
||||
|
||||
public TcpConnection getOrMakeConnection() throws Exception {
|
||||
return new CachedConnection(this.pool.getItem());
|
||||
}
|
||||
|
||||
private class CachedConnection extends AbstractTcpConnectionInterceptor {
|
||||
|
||||
private volatile boolean released;
|
||||
|
||||
public CachedConnection(TcpConnection connection) {
|
||||
super.setTheConnection(connection);
|
||||
if (connection instanceof AbstractTcpConnection) {
|
||||
((AbstractTcpConnection) connection).registerListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
/**
|
||||
* If the delegate is stopped, actually close
|
||||
* the connection.
|
||||
*/
|
||||
if (!isRunning()) {
|
||||
if (logger.isDebugEnabled()){
|
||||
logger.debug("Factory not running - closing " + this.getConnectionId());
|
||||
}
|
||||
pool.releaseItem(null); // just open up a permit
|
||||
super.close();
|
||||
}
|
||||
else if(this.released) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Connection " + this.getConnectionId() + " has already been released");
|
||||
}
|
||||
}
|
||||
else {
|
||||
pool.releaseItem(this.getTheConnection());
|
||||
this.released = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConnectionId() {
|
||||
return "Cached:" + super.getConnectionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getConnectionId();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///////////////// DELEGATE METHODS ///////////////////////
|
||||
|
||||
public void run() {
|
||||
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return targetConnectionFactory.isRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
targetConnectionFactory.close();
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return targetConnectionFactory.hashCode();
|
||||
}
|
||||
|
||||
public void setComponentName(String componentName) {
|
||||
targetConnectionFactory.setComponentName(componentName);
|
||||
}
|
||||
|
||||
public String getComponentType() {
|
||||
return targetConnectionFactory.getComponentType();
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
return targetConnectionFactory.equals(obj);
|
||||
}
|
||||
|
||||
public int getSoTimeout() {
|
||||
return targetConnectionFactory.getSoTimeout();
|
||||
}
|
||||
|
||||
public void setSoTimeout(int soTimeout) {
|
||||
targetConnectionFactory.setSoTimeout(soTimeout);
|
||||
}
|
||||
|
||||
public int getSoReceiveBufferSize() {
|
||||
return targetConnectionFactory.getSoReceiveBufferSize();
|
||||
}
|
||||
|
||||
public void setSoReceiveBufferSize(int soReceiveBufferSize) {
|
||||
targetConnectionFactory.setSoReceiveBufferSize(soReceiveBufferSize);
|
||||
}
|
||||
|
||||
public int getSoSendBufferSize() {
|
||||
return targetConnectionFactory.getSoSendBufferSize();
|
||||
}
|
||||
|
||||
public void setSoSendBufferSize(int soSendBufferSize) {
|
||||
targetConnectionFactory.setSoSendBufferSize(soSendBufferSize);
|
||||
}
|
||||
|
||||
public boolean isSoTcpNoDelay() {
|
||||
return targetConnectionFactory.isSoTcpNoDelay();
|
||||
}
|
||||
|
||||
public void setSoTcpNoDelay(boolean soTcpNoDelay) {
|
||||
targetConnectionFactory.setSoTcpNoDelay(soTcpNoDelay);
|
||||
}
|
||||
|
||||
public int getSoLinger() {
|
||||
return targetConnectionFactory.getSoLinger();
|
||||
}
|
||||
|
||||
public void setSoLinger(int soLinger) {
|
||||
targetConnectionFactory.setSoLinger(soLinger);
|
||||
}
|
||||
|
||||
public boolean isSoKeepAlive() {
|
||||
return targetConnectionFactory.isSoKeepAlive();
|
||||
}
|
||||
|
||||
public void setSoKeepAlive(boolean soKeepAlive) {
|
||||
targetConnectionFactory.setSoKeepAlive(soKeepAlive);
|
||||
}
|
||||
|
||||
public int getSoTrafficClass() {
|
||||
return targetConnectionFactory.getSoTrafficClass();
|
||||
}
|
||||
|
||||
public void setSoTrafficClass(int soTrafficClass) {
|
||||
targetConnectionFactory.setSoTrafficClass(soTrafficClass);
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return targetConnectionFactory.getHost();
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return targetConnectionFactory.getPort();
|
||||
}
|
||||
|
||||
public TcpListener getListener() {
|
||||
return targetConnectionFactory.getListener();
|
||||
}
|
||||
|
||||
public TcpSender getSender() {
|
||||
return targetConnectionFactory.getSender();
|
||||
}
|
||||
|
||||
public Serializer<?> getSerializer() {
|
||||
return targetConnectionFactory.getSerializer();
|
||||
}
|
||||
|
||||
public Deserializer<?> getDeserializer() {
|
||||
return targetConnectionFactory.getDeserializer();
|
||||
}
|
||||
|
||||
public TcpMessageMapper getMapper() {
|
||||
return targetConnectionFactory.getMapper();
|
||||
}
|
||||
|
||||
public void registerListener(TcpListener listener) {
|
||||
targetConnectionFactory.registerListener(listener);
|
||||
}
|
||||
|
||||
public void registerSender(TcpSender sender) {
|
||||
targetConnectionFactory.registerSender(sender);
|
||||
}
|
||||
|
||||
public void setTaskExecutor(Executor taskExecutor) {
|
||||
targetConnectionFactory.setTaskExecutor(taskExecutor);
|
||||
}
|
||||
|
||||
public void setDeserializer(Deserializer<?> deserializer) {
|
||||
targetConnectionFactory.setDeserializer(deserializer);
|
||||
}
|
||||
|
||||
public void setSerializer(Serializer<?> serializer) {
|
||||
targetConnectionFactory.setSerializer(serializer);
|
||||
}
|
||||
|
||||
public void setMapper(TcpMessageMapper mapper) {
|
||||
targetConnectionFactory.setMapper(mapper);
|
||||
}
|
||||
|
||||
public boolean isSingleUse() {
|
||||
return targetConnectionFactory.isSingleUse();
|
||||
}
|
||||
|
||||
public void setSingleUse(boolean singleUse) {
|
||||
targetConnectionFactory.setSingleUse(singleUse);
|
||||
}
|
||||
|
||||
public void setInterceptorFactoryChain(
|
||||
TcpConnectionInterceptorFactoryChain interceptorFactoryChain) {
|
||||
targetConnectionFactory
|
||||
.setInterceptorFactoryChain(interceptorFactoryChain);
|
||||
}
|
||||
|
||||
public void setLookupHost(boolean lookupHost) {
|
||||
targetConnectionFactory.setLookupHost(lookupHost);
|
||||
}
|
||||
|
||||
public boolean isLookupHost() {
|
||||
return targetConnectionFactory.isLookupHost();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
this.setActive(true);
|
||||
targetConnectionFactory.start();
|
||||
}
|
||||
|
||||
public synchronized void stop() {
|
||||
targetConnectionFactory.stop();
|
||||
this.pool.removeAllIdleItems();
|
||||
}
|
||||
|
||||
public int getPhase() {
|
||||
return targetConnectionFactory.getPhase();
|
||||
}
|
||||
|
||||
public boolean isAutoStartup() {
|
||||
return targetConnectionFactory.isAutoStartup();
|
||||
}
|
||||
|
||||
public void stop(Runnable callback) {
|
||||
targetConnectionFactory.stop(callback);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int-ip="http://www.springframework.org/schema/integration/ip"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration/ip http://www.springframework.org/schema/integration/ip/spring-integration-ip-2.1.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
|
||||
<int-ip:tcp-connection-factory
|
||||
id="scf"
|
||||
type="server"
|
||||
so-timeout="60000"
|
||||
port="9876"/>
|
||||
|
||||
<int-ip:tcp-inbound-channel-adapter
|
||||
connection-factory="scf"
|
||||
channel="inbound"/>
|
||||
|
||||
<int:channel id="inbound">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
<int-ip:tcp-connection-factory
|
||||
id="ccf"
|
||||
type="client"
|
||||
host="localhost"
|
||||
port="9876"
|
||||
so-timeout="60000"
|
||||
|
||||
/>
|
||||
|
||||
<bean id="caching.ccf" class="org.springframework.integration.ip.tcp.connection.CachingClientConnectionFactory">
|
||||
<constructor-arg ref="ccf" />
|
||||
<constructor-arg value="10" />
|
||||
<property name="connectionWaitTimeout" value="10000"/>
|
||||
</bean>
|
||||
|
||||
<int-ip:tcp-outbound-channel-adapter
|
||||
connection-factory="caching.ccf"
|
||||
channel="outbound"/>
|
||||
|
||||
<int:channel id="outbound" />
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* http://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.springframework.integration.ip.tcp.connection;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.core.PollableChannel;
|
||||
import org.springframework.integration.core.SubscribableChannel;
|
||||
import org.springframework.integration.ip.IpHeaders;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.test.annotation.ExpectedException;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 2.2
|
||||
*
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class CachingClientConnectionFactoryTests {
|
||||
|
||||
@Autowired
|
||||
SubscribableChannel outbound;
|
||||
|
||||
@Autowired
|
||||
PollableChannel inbound;
|
||||
|
||||
@Autowired
|
||||
AbstractServerConnectionFactory serverCf;
|
||||
|
||||
@Test
|
||||
public void testReuse() throws Exception {
|
||||
AbstractClientConnectionFactory factory = mock(AbstractClientConnectionFactory.class);
|
||||
when(factory.isRunning()).thenReturn(true);
|
||||
TcpConnection mockConn1 = makeMockConnection("conn1");
|
||||
TcpConnection mockConn2 = makeMockConnection("conn2");
|
||||
when(factory.getConnection()).thenReturn(mockConn1).thenReturn(mockConn2);
|
||||
CachingClientConnectionFactory cachingFactory = new CachingClientConnectionFactory(factory, 2);
|
||||
cachingFactory.start();
|
||||
TcpConnection conn1 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn1.toString(), conn1.toString());
|
||||
conn1.close();
|
||||
conn1 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn1.toString(), conn1.toString());
|
||||
TcpConnection conn2 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn2.toString(), conn2.toString());
|
||||
conn1.close();
|
||||
conn2.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReuseNoLimit() throws Exception {
|
||||
AbstractClientConnectionFactory factory = mock(AbstractClientConnectionFactory.class);
|
||||
when(factory.isRunning()).thenReturn(true);
|
||||
TcpConnection mockConn1 = makeMockConnection("conn1");
|
||||
TcpConnection mockConn2 = makeMockConnection("conn2");
|
||||
when(factory.getConnection()).thenReturn(mockConn1).thenReturn(mockConn2);
|
||||
CachingClientConnectionFactory cachingFactory = new CachingClientConnectionFactory(factory, 0);
|
||||
cachingFactory.start();
|
||||
TcpConnection conn1 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn1.toString(), conn1.toString());
|
||||
conn1.close();
|
||||
conn1 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn1.toString(), conn1.toString());
|
||||
TcpConnection conn2 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn2.toString(), conn2.toString());
|
||||
conn1.close();
|
||||
conn2.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReuseClosed() throws Exception {
|
||||
AbstractClientConnectionFactory factory = mock(AbstractClientConnectionFactory.class);
|
||||
when(factory.isRunning()).thenReturn(true);
|
||||
TcpConnection mockConn1 = makeMockConnection("conn1");
|
||||
TcpConnection mockConn2 = makeMockConnection("conn2");
|
||||
when(factory.getConnection()).thenReturn(mockConn1)
|
||||
.thenReturn(mockConn2).thenReturn(mockConn1)
|
||||
.thenReturn(mockConn2);
|
||||
CachingClientConnectionFactory cachingFactory = new CachingClientConnectionFactory(factory, 2);
|
||||
cachingFactory.start();
|
||||
TcpConnection conn1 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn1.toString(), conn1.toString());
|
||||
conn1.close();
|
||||
conn1 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn1.toString(), conn1.toString());
|
||||
TcpConnection conn2 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn2.toString(), conn2.toString());
|
||||
conn1.close();
|
||||
conn2.close();
|
||||
when(mockConn1.isOpen()).thenReturn(false);
|
||||
TcpConnection conn2a = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn2.toString(), conn2a.toString());
|
||||
assertSame(TestUtils.getPropertyValue(conn2, "theConnection"),
|
||||
TestUtils.getPropertyValue(conn2a, "theConnection"));
|
||||
conn2a.close();
|
||||
}
|
||||
|
||||
@Test @ExpectedException(MessagingException.class)
|
||||
public void testLimit() throws Exception {
|
||||
AbstractClientConnectionFactory factory = mock(AbstractClientConnectionFactory.class);
|
||||
when(factory.isRunning()).thenReturn(true);
|
||||
TcpConnection mockConn1 = makeMockConnection("conn1");
|
||||
TcpConnection mockConn2 = makeMockConnection("conn2");
|
||||
when(factory.getConnection()).thenReturn(mockConn1).thenReturn(mockConn2);
|
||||
CachingClientConnectionFactory cachingFactory = new CachingClientConnectionFactory(factory, 2);
|
||||
cachingFactory.setConnectionWaitTimeout(10);
|
||||
cachingFactory.start();
|
||||
TcpConnection conn1 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn1.toString(), conn1.toString());
|
||||
conn1.close();
|
||||
conn1 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn1.toString(), conn1.toString());
|
||||
TcpConnection conn2 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn2.toString(), conn2.toString());
|
||||
cachingFactory.getConnection();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStop() throws Exception {
|
||||
AbstractClientConnectionFactory factory = mock(AbstractClientConnectionFactory.class);
|
||||
when(factory.isRunning()).thenReturn(true);
|
||||
TcpConnection mockConn1 = makeMockConnection("conn1");
|
||||
TcpConnection mockConn2 = makeMockConnection("conn2");
|
||||
int i = 3;
|
||||
when(factory.getConnection()).thenReturn(mockConn1)
|
||||
.thenReturn(mockConn2)
|
||||
.thenReturn(makeMockConnection("conn" + (i++)));
|
||||
CachingClientConnectionFactory cachingFactory = new CachingClientConnectionFactory(factory, 2);
|
||||
cachingFactory.start();
|
||||
TcpConnection conn1 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn1.toString(), conn1.toString());
|
||||
conn1.close();
|
||||
conn1 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn1.toString(), conn1.toString());
|
||||
TcpConnection conn2 = cachingFactory.getConnection();
|
||||
assertEquals("Cached:" + mockConn2.toString(), conn2.toString());
|
||||
cachingFactory.stop();
|
||||
Answer<Object> answer = new Answer<Object> () {
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
return null;
|
||||
}};
|
||||
doAnswer(answer).when(mockConn1).close();
|
||||
doAnswer(answer).when(mockConn2).close();
|
||||
when(factory.isRunning()).thenReturn(false);
|
||||
conn1.close();
|
||||
conn2.close();
|
||||
verify(mockConn1).close();
|
||||
verify(mockConn2).close();
|
||||
when(factory.isRunning()).thenReturn(true);
|
||||
TcpConnection conn3 = cachingFactory.getConnection();
|
||||
assertNotSame(TestUtils.getPropertyValue(conn1, "theConnection"),
|
||||
TestUtils.getPropertyValue(conn3, "theConnection"));
|
||||
assertNotSame(TestUtils.getPropertyValue(conn2, "theConnection"),
|
||||
TestUtils.getPropertyValue(conn3, "theConnection"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnlargePool() throws Exception {
|
||||
AbstractClientConnectionFactory factory = mock(AbstractClientConnectionFactory.class);
|
||||
when(factory.isRunning()).thenReturn(true);
|
||||
TcpConnection mockConn = makeMockConnection("conn");
|
||||
when(factory.getConnection()).thenReturn(mockConn);
|
||||
CachingClientConnectionFactory cachingFactory = new CachingClientConnectionFactory(factory, 2);
|
||||
cachingFactory.start();
|
||||
TcpConnection conn1 = cachingFactory.getConnection();
|
||||
TcpConnection conn2 = cachingFactory.getConnection();
|
||||
assertNotSame(conn1, conn2);
|
||||
Semaphore semaphore = TestUtils.getPropertyValue(
|
||||
TestUtils.getPropertyValue(cachingFactory, "pool"), "permits", Semaphore.class);
|
||||
assertEquals(0, semaphore.availablePermits());
|
||||
cachingFactory.setPoolSize(4);
|
||||
TcpConnection conn3 = cachingFactory.getConnection();
|
||||
TcpConnection conn4 = cachingFactory.getConnection();
|
||||
assertEquals(0, semaphore.availablePermits());
|
||||
conn1.close();
|
||||
conn1.close();
|
||||
conn2.close();
|
||||
conn3.close();
|
||||
conn4.close();
|
||||
assertEquals(4, semaphore.availablePermits());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReducePool() throws Exception {
|
||||
AbstractClientConnectionFactory factory = mock(AbstractClientConnectionFactory.class);
|
||||
when(factory.isRunning()).thenReturn(true);
|
||||
TcpConnection mockConn1 = makeMockConnection("conn", true);
|
||||
TcpConnection mockConn2 = makeMockConnection("conn", true);
|
||||
TcpConnection mockConn3 = makeMockConnection("conn", true);
|
||||
TcpConnection mockConn4 = makeMockConnection("conn", true);
|
||||
when(factory.getConnection()).thenReturn(mockConn1)
|
||||
.thenReturn(mockConn2).thenReturn(mockConn3)
|
||||
.thenReturn(mockConn4);
|
||||
CachingClientConnectionFactory cachingFactory = new CachingClientConnectionFactory(factory, 4);
|
||||
cachingFactory.start();
|
||||
TcpConnection conn1 = cachingFactory.getConnection();
|
||||
TcpConnection conn2 = cachingFactory.getConnection();
|
||||
TcpConnection conn3 = cachingFactory.getConnection();
|
||||
TcpConnection conn4 = cachingFactory.getConnection();
|
||||
Semaphore semaphore = TestUtils.getPropertyValue(
|
||||
TestUtils.getPropertyValue(cachingFactory, "pool"), "permits", Semaphore.class);
|
||||
assertEquals(0, semaphore.availablePermits());
|
||||
conn1.close();
|
||||
assertEquals(1, semaphore.availablePermits());
|
||||
cachingFactory.setPoolSize(2);
|
||||
assertEquals(0, semaphore.availablePermits());
|
||||
assertEquals(3, cachingFactory.getActiveCount());
|
||||
conn2.close();
|
||||
assertEquals(0, semaphore.availablePermits());
|
||||
assertEquals(2, cachingFactory.getActiveCount());
|
||||
conn3.close();
|
||||
assertEquals(1, cachingFactory.getActiveCount());
|
||||
assertEquals(1, cachingFactory.getIdleCount());
|
||||
conn4.close();
|
||||
assertEquals(2, semaphore.availablePermits());
|
||||
assertEquals(0, cachingFactory.getActiveCount());
|
||||
assertEquals(2, cachingFactory.getIdleCount());
|
||||
verify(mockConn1).close();
|
||||
verify(mockConn2).close();
|
||||
}
|
||||
|
||||
private TcpConnection makeMockConnection(String name) {
|
||||
return makeMockConnection(name, false);
|
||||
}
|
||||
|
||||
private TcpConnection makeMockConnection(String name, boolean closeOk) {
|
||||
TcpConnection mockConn1 = mock(TcpConnection.class);
|
||||
when(mockConn1.getConnectionId()).thenReturn(name);
|
||||
when(mockConn1.toString()).thenReturn(name);
|
||||
when(mockConn1.isOpen()).thenReturn(true);
|
||||
if (!closeOk) {
|
||||
doThrow(new RuntimeException("close() not expected")).when(mockConn1).close();
|
||||
}
|
||||
return mockConn1;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void integrationTest() throws Exception {
|
||||
int n = 0;
|
||||
while (!serverCf.isListening()) {
|
||||
Thread.sleep(100);
|
||||
n++;
|
||||
if (n > 10000) {
|
||||
fail("Server didn't begin listening");
|
||||
}
|
||||
}
|
||||
outbound.send(new GenericMessage<String>("Hello, world!"));
|
||||
Message<?> m = inbound.receive(1000);
|
||||
assertNotNull(m);
|
||||
String connectionId = m.getHeaders().get(IpHeaders.CONNECTION_ID, String.class);
|
||||
outbound.send(new GenericMessage<String>("Hello, world!"));
|
||||
m = inbound.receive(1000);
|
||||
assertNotNull(m);
|
||||
assertEquals(connectionId, m.getHeaders().get(IpHeaders.CONNECTION_ID, String.class));
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user