/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You 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. */ /* * Created on Feb 3, 2006 * */ package org.apache.geode.internal.cache.tier.sockets; import org.apache.geode.cache.*; import org.apache.geode.cache.client.PoolFactory; import org.apache.geode.cache.client.PoolManager; import org.apache.geode.cache.client.internal.Connection; import org.apache.geode.cache.client.internal.PoolImpl; import org.apache.geode.cache.client.internal.PutOp; import org.apache.geode.cache.client.internal.QueueStateImpl.SequenceIdAndExpirationObject; import org.apache.geode.cache.server.CacheServer; import org.apache.geode.cache.util.CacheListenerAdapter; import org.apache.geode.distributed.DistributedSystem; import org.apache.geode.internal.AvailablePort; import org.apache.geode.internal.cache.EntryEventImpl; import org.apache.geode.internal.cache.EventID; import org.apache.geode.internal.cache.ha.ThreadIdentifier; import org.apache.geode.test.dunit.Wait; import org.apache.geode.test.dunit.WaitCriterion; import org.apache.geode.test.junit.categories.ClientSubscriptionTest; import org.apache.geode.test.junit.categories.IntegrationTest; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; import java.util.Map; import java.util.Properties; import static org.apache.geode.distributed.ConfigurationProperties.LOCATORS; import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT; import static org.junit.Assert.*; /** * * Tests the functionality of operations of AbstractConnectionProxy & its derived classes. */ @Category({IntegrationTest.class, ClientSubscriptionTest.class}) public class ConnectionProxyJUnitTest { private static final String expectedRedundantErrorMsg = "Could not find any server to create redundant client queue on."; private static final String expectedPrimaryErrorMsg = "Could not find any server to create primary client queue on."; DistributedSystem system; Cache cache; PoolImpl proxy = null; SequenceIdAndExpirationObject seo = null; @Before public void setUp() throws Exception { Properties p = new Properties(); p.setProperty(MCAST_PORT, "0"); p.setProperty(LOCATORS, ""); this.system = DistributedSystem.connect(p); this.cache = CacheFactory.create(system); final String addExpectedPEM = "<ExpectedException action=add>" + expectedPrimaryErrorMsg + "</ExpectedException>"; final String addExpectedREM = "<ExpectedException action=add>" + expectedRedundantErrorMsg + "</ExpectedException>"; system.getLogWriter().info(addExpectedPEM); system.getLogWriter().info(addExpectedREM); } @After public void tearDown() throws Exception { this.cache.close(); final String removeExpectedPEM = "<ExpectedException action=remove>" + expectedPrimaryErrorMsg + "</ExpectedException>"; final String removeExpectedREM = "<ExpectedException action=remove>" + expectedRedundantErrorMsg + "</ExpectedException>"; system.getLogWriter().info(removeExpectedPEM); system.getLogWriter().info(removeExpectedREM); this.system.disconnect(); if (proxy != null) proxy.destroy(); } /** * This test verifies the behaviour of client request when the listener on the server sits * forever. This is done in following steps:<br> * 1)create server<br> * 2)initialize proxy object and create region for client having a CacheListener and make * afterCreate in the listener to wait infinitely<br> * 3)perform a PUT on client by acquiring Connection through proxy<br> * 4)Verify that exception occurs due to infinite wait in the listener<br> * 5)Verify that above exception occurs sometime after the readTimeout configured for the client * <br> * */ @Ignore @Test public void testListenerOnServerSitForever() throws Exception { int port3 = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); Region testRegion = null; CacheServer server = this.cache.addCacheServer(); server.setMaximumTimeBetweenPings(10000); server.setPort(port3); server.start(); try { PoolFactory pf = PoolManager.createFactory(); pf.addServer("localhost", port3); pf.setSubscriptionEnabled(false); pf.setSubscriptionRedundancy(-1); pf.setReadTimeout(2000); pf.setThreadLocalConnections(true); pf.setSocketBufferSize(32768); pf.setRetryAttempts(1); pf.setPingInterval(10000); proxy = (PoolImpl) pf.create("clientPool"); AttributesFactory factory = new AttributesFactory(); factory.setScope(Scope.DISTRIBUTED_ACK); factory.setCacheListener(new CacheListenerAdapter() { public void afterCreate(EntryEvent event) { synchronized (ConnectionProxyJUnitTest.this) { try { ConnectionProxyJUnitTest.this.wait(); } catch (InterruptedException e) { fail("interrupted"); } } } }); RegionAttributes attrs = factory.create(); testRegion = cache.createRegion("testregion", attrs); } catch (Exception ex) { ex.printStackTrace(); fail("Failed to initialize client"); } Connection conn = (proxy).acquireConnection(); long t1 = 0; try { t1 = System.currentTimeMillis(); EntryEventImpl event = new EntryEventImpl((Object) null); try { event.setEventId(new EventID(new byte[] {1}, 1, 1)); PutOp.execute(conn, proxy, testRegion.getFullPath(), "key1", "val1", event, null, false); } finally { event.release(); } fail("Test failed as exception was expected"); } catch (Exception e) { long t2 = System.currentTimeMillis(); long net = (t2 - t1); assertTrue(net / 1000 < 5); } synchronized (ConnectionProxyJUnitTest.this) { ConnectionProxyJUnitTest.this.notify(); } } /** * Tests the DeadServerMonitor when identifying an Endpoint as alive , does not create a * persistent Ping connection ( i.e sends a CLOSE protocol , if the number of connections is zero. */ @Test public void testDeadServerMonitorPingNature1() { int port3 = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); // final int maxWaitTime = 10000; try { PoolFactory pf = PoolManager.createFactory(); pf.addServer("localhost", port3); pf.setSubscriptionEnabled(false); pf.setReadTimeout(2000); pf.setThreadLocalConnections(true); pf.setMinConnections(1); pf.setSocketBufferSize(32768); pf.setRetryAttempts(1); pf.setPingInterval(500); proxy = (PoolImpl) pf.create("clientPool"); } catch (Exception ex) { ex.printStackTrace(); fail("Failed to initialize client"); } try { (proxy).acquireConnection(); } catch (Exception ok) { ok.printStackTrace(); } try { (proxy).acquireConnection(); } catch (Exception ok) { ok.printStackTrace(); } // long start = System.currentTimeMillis(); assertEquals(0, proxy.getConnectedServerCount()); // start the server CacheServer server = null; try { try { server = this.cache.addCacheServer(); server.setMaximumTimeBetweenPings(15000); server.setPort(port3); server.start(); } catch (Exception e) { e.printStackTrace(); fail("Failed to create server"); } WaitCriterion ev = new WaitCriterion() { public boolean done() { return proxy.getConnectedServerCount() == 1; } public String description() { return null; } }; Wait.waitForCriterion(ev, 90 * 1000, 200, true); } finally { if (server != null) { server.stop(); } } } /** * Tests the DeadServerMonitor when identifying an Endpoint as alive , does creates a persistent * Ping connection ( i.e sends a PING protocol , if the number of connections is more than zero. */ @Test public void testDeadServerMonitorPingNature2() { int port3 = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); // final int maxWaitTime = 10000; try { PoolFactory pf = PoolManager.createFactory(); pf.addServer("localhost", port3); pf.setSubscriptionEnabled(false); pf.setReadTimeout(2000); pf.setThreadLocalConnections(true); pf.setMinConnections(1); pf.setSocketBufferSize(32768); pf.setRetryAttempts(1); pf.setPingInterval(500); proxy = (PoolImpl) pf.create("clientPool"); } catch (Exception ex) { ex.printStackTrace(); fail("Failed to initialize client"); } // let LiveServerMonitor detect it as alive as the numConnection is more than zero // long start = System.currentTimeMillis(); assertEquals(0, proxy.getConnectedServerCount()); // start the server CacheServer server = null; try { try { server = this.cache.addCacheServer(); server.setMaximumTimeBetweenPings(15000); server.setPort(port3); server.start(); } catch (Exception e) { e.printStackTrace(); fail("Failed to create server"); } WaitCriterion ev = new WaitCriterion() { public boolean done() { return proxy.getConnectedServerCount() == 1; } public String description() { return null; } }; Wait.waitForCriterion(ev, 90 * 1000, 200, true); } finally { if (server != null) { server.stop(); } } } @Test public void testThreadIdToSequenceIdMapCreation() { int port3 = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); CacheServer server = null; try { try { server = this.cache.addCacheServer(); server.setMaximumTimeBetweenPings(10000); server.setPort(port3); server.start(); } catch (Exception e) { e.printStackTrace(); fail("Failed to create server"); } try { PoolFactory pf = PoolManager.createFactory(); pf.addServer("localhost", port3); pf.setSubscriptionEnabled(true); pf.setSubscriptionRedundancy(-1); proxy = (PoolImpl) pf.create("clientPool"); if (proxy.getThreadIdToSequenceIdMap() == null) { fail(" ThreadIdToSequenceIdMap is null. "); } } catch (Exception ex) { ex.printStackTrace(); fail("Failed to initialize client"); } } finally { if (server != null) { try { Thread.sleep(500); } catch (InterruptedException ie) { fail("interrupted"); } server.stop(); } } } @Test public void testThreadIdToSequenceIdMapExpiryPositive() { int port3 = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); CacheServer server = null; try { try { server = this.cache.addCacheServer(); server.setMaximumTimeBetweenPings(10000); server.setPort(port3); server.start(); } catch (Exception e) { e.printStackTrace(); fail("Failed to create server"); } try { PoolFactory pf = PoolManager.createFactory(); pf.addServer("localhost", port3); pf.setSubscriptionEnabled(true); pf.setSubscriptionRedundancy(-1); pf.setSubscriptionMessageTrackingTimeout(4000); pf.setSubscriptionAckInterval(2000); proxy = (PoolImpl) pf.create("clientPool"); EventID eid = new EventID(new byte[0], 1, 1); if (proxy.verifyIfDuplicate(eid)) { fail(" eid should not be duplicate as it is a new entry"); } verifyExpiry(60 * 1000); if (proxy.verifyIfDuplicate(eid)) { fail(" eid should not be duplicate as the previous entry should have expired "); } } catch (Exception ex) { ex.printStackTrace(); fail("Failed to initialize client"); } } finally { if (server != null) { server.stop(); } } } @Test public void testThreadIdToSequenceIdMapExpiryNegative() { int port3 = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); CacheServer server = null; try { try { server = this.cache.addCacheServer(); server.setMaximumTimeBetweenPings(10000); server.setPort(port3); server.start(); } catch (Exception e) { e.printStackTrace(); fail("Failed to create server"); } try { PoolFactory pf = PoolManager.createFactory(); pf.addServer("localhost", port3); pf.setSubscriptionEnabled(true); pf.setSubscriptionRedundancy(-1); pf.setSubscriptionMessageTrackingTimeout(10000); proxy = (PoolImpl) pf.create("clientPool"); final EventID eid = new EventID(new byte[0], 1, 1); if (proxy.verifyIfDuplicate(eid)) { fail(" eid should not be duplicate as it is a new entry"); } WaitCriterion ev = new WaitCriterion() { public boolean done() { return proxy.verifyIfDuplicate(eid); } public String description() { return null; } }; Wait.waitForCriterion(ev, 20 * 1000, 200, true); } catch (Exception ex) { ex.printStackTrace(); fail("Failed to initialize client"); } } finally { if (server != null) { server.stop(); } } } @Test public void testThreadIdToSequenceIdMapConcurrency() { int port3 = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); CacheServer server = null; try { try { server = this.cache.addCacheServer(); server.setMaximumTimeBetweenPings(10000); server.setPort(port3); server.start(); } catch (Exception e) { e.printStackTrace(); fail("Failed to create server"); } try { PoolFactory pf = PoolManager.createFactory(); pf.addServer("localhost", port3); pf.setSubscriptionEnabled(true); pf.setSubscriptionRedundancy(-1); pf.setSubscriptionMessageTrackingTimeout(5000); pf.setSubscriptionAckInterval(2000); proxy = (PoolImpl) pf.create("clientPool"); final int EVENT_ID_COUNT = 10000; // why 10,000? EventID[] eid = new EventID[EVENT_ID_COUNT]; for (int i = 0; i < EVENT_ID_COUNT; i++) { eid[i] = new EventID(new byte[0], i, i); if (proxy.verifyIfDuplicate(eid[i])) { fail(" eid can never be duplicate, it is being created for the first time! "); } } verifyExpiry(30 * 1000); for (int i = 0; i < EVENT_ID_COUNT; i++) { if (proxy.verifyIfDuplicate(eid[i])) { fail( " eid can not be found to be duplicate since the entry should have expired! " + i); } } } catch (Exception ex) { ex.printStackTrace(); fail("Failed to initialize client"); } } finally { if (server != null) { server.stop(); } } } @Test public void testDuplicateSeqIdLesserThanCurrentSeqIdBeingIgnored() { int port3 = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); CacheServer server = null; try { try { server = this.cache.addCacheServer(); server.setMaximumTimeBetweenPings(10000); server.setPort(port3); server.start(); } catch (Exception e) { e.printStackTrace(); fail("Failed to create server"); } try { PoolFactory pf = PoolManager.createFactory(); pf.addServer("localhost", port3); pf.setSubscriptionEnabled(true); pf.setSubscriptionRedundancy(-1); pf.setSubscriptionMessageTrackingTimeout(100000); proxy = (PoolImpl) pf.create("clientPool"); EventID eid1 = new EventID(new byte[0], 1, 5); if (proxy.verifyIfDuplicate(eid1)) { fail(" eid1 can never be duplicate, it is being created for the first time! "); } EventID eid2 = new EventID(new byte[0], 1, 2); if (!proxy.verifyIfDuplicate(eid2)) { fail(" eid2 should be duplicate, seqId is less than highest (5)"); } EventID eid3 = new EventID(new byte[0], 1, 3); if (!proxy.verifyIfDuplicate(eid3)) { fail(" eid3 should be duplicate, seqId is less than highest (5)"); } assertTrue(!proxy.getThreadIdToSequenceIdMap().isEmpty()); proxy.destroy(); } catch (Exception ex) { ex.printStackTrace(); fail("Failed to initialize client"); } } finally { if (server != null) { server.stop(); } } } @Test public void testCleanCloseOfThreadIdToSeqId() { int port3 = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); CacheServer server = null; try { try { server = this.cache.addCacheServer(); server.setMaximumTimeBetweenPings(10000); server.setPort(port3); server.start(); } catch (Exception e) { e.printStackTrace(); fail("Failed to create server"); } try { PoolFactory pf = PoolManager.createFactory(); pf.addServer("localhost", port3); pf.setSubscriptionEnabled(true); pf.setSubscriptionRedundancy(-1); pf.setSubscriptionMessageTrackingTimeout(100000); proxy = (PoolImpl) pf.create("clientPool"); EventID eid1 = new EventID(new byte[0], 1, 2); if (proxy.verifyIfDuplicate(eid1)) { fail(" eid can never be duplicate, it is being created for the first time! "); } EventID eid2 = new EventID(new byte[0], 1, 3); if (proxy.verifyIfDuplicate(eid2)) { fail(" eid can never be duplicate, since sequenceId is greater "); } if (!proxy.verifyIfDuplicate(eid2)) { fail(" eid had to be a duplicate, since sequenceId is equal "); } EventID eid3 = new EventID(new byte[0], 1, 1); if (!proxy.verifyIfDuplicate(eid3)) { fail(" eid had to be a duplicate, since sequenceId is lesser "); } } catch (Exception ex) { ex.printStackTrace(); fail("Failed to initialize client"); } } finally { if (server != null) { try { Thread.sleep(500); } catch (InterruptedException ie) { fail("interrupted"); } server.stop(); } } } @Test public void testTwoClientsHavingDifferentThreadIdMaps() { int port3 = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); CacheServer server = null; try { try { server = this.cache.addCacheServer(); server.setMaximumTimeBetweenPings(10000); server.setPort(port3); server.start(); } catch (Exception e) { e.printStackTrace(); fail("Failed to create server"); } try { PoolFactory pf = PoolManager.createFactory(); pf.addServer("localhost", port3); pf.setSubscriptionEnabled(true); pf.setSubscriptionRedundancy(-1); pf.setSubscriptionMessageTrackingTimeout(100000); PoolImpl proxy1 = (PoolImpl) pf.create("clientPool1"); try { PoolImpl proxy2 = (PoolImpl) pf.create("clientPool2"); try { Map map1 = proxy1.getThreadIdToSequenceIdMap(); Map map2 = proxy2.getThreadIdToSequenceIdMap(); assertTrue(!(map1 == map2)); } finally { proxy2.destroy(); } } finally { proxy1.destroy(); } } catch (Exception ex) { ex.printStackTrace(); fail("Failed to initialize client"); } } finally { if (server != null) { server.stop(); } } } @Test public void testPeriodicAckSendByClient() { int port = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); CacheServer server = null; try { try { server = this.cache.addCacheServer(); server.setPort(port); server.start(); } catch (Exception e) { e.printStackTrace(); fail("Failed to create server"); } try { PoolFactory pf = PoolManager.createFactory(); pf.addServer("localhost", port); pf.setSubscriptionEnabled(true); pf.setSubscriptionRedundancy(1); pf.setReadTimeout(20000); pf.setSubscriptionMessageTrackingTimeout(15000); pf.setSubscriptionAckInterval(5000); proxy = (PoolImpl) pf.create("clientPool"); EventID eid = new EventID(new byte[0], 1, 1); if (proxy.verifyIfDuplicate(eid)) { fail(" eid should not be duplicate as it is a new entry"); } seo = (SequenceIdAndExpirationObject) proxy.getThreadIdToSequenceIdMap() .get(new ThreadIdentifier(new byte[0], 1)); assertFalse(seo.getAckSend()); // should send the ack to server seo = (SequenceIdAndExpirationObject) proxy.getThreadIdToSequenceIdMap() .get(new ThreadIdentifier(new byte[0], 1)); verifyAckSend(60 * 1000, true); // New update on same threadId eid = new EventID(new byte[0], 1, 2); if (proxy.verifyIfDuplicate(eid)) { fail(" eid should not be duplicate as it is a new entry"); } seo = (SequenceIdAndExpirationObject) proxy.getThreadIdToSequenceIdMap() .get(new ThreadIdentifier(new byte[0], 1)); assertFalse(seo.getAckSend()); // should send another ack to server seo = (SequenceIdAndExpirationObject) proxy.getThreadIdToSequenceIdMap() .get(new ThreadIdentifier(new byte[0], 1)); verifyAckSend(6000, true); // should expire with the this mentioned. verifyExpiry(15 * 1000); } catch (Exception ex) { ex.printStackTrace(); fail("Test testPeriodicAckSendByClient Failed"); } } finally { if (server != null) { server.stop(); } } } // No ack will be send if Redundancy level = 0 @Test public void testNoAckSendByClient() { int port = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); CacheServer server = null; try { try { server = this.cache.addCacheServer(); server.setPort(port); server.start(); } catch (Exception e) { e.printStackTrace(); fail("Failed to create server"); } try { PoolFactory pf = PoolManager.createFactory(); pf.addServer("localhost", port); pf.setSubscriptionEnabled(true); pf.setSubscriptionRedundancy(1); pf.setReadTimeout(20000); pf.setSubscriptionMessageTrackingTimeout(8000); pf.setSubscriptionAckInterval(2000); proxy = (PoolImpl) pf.create("clientPool"); EventID eid = new EventID(new byte[0], 1, 1); if (proxy.verifyIfDuplicate(eid)) { fail(" eid should not be duplicate as it is a new entry"); } seo = (SequenceIdAndExpirationObject) proxy.getThreadIdToSequenceIdMap() .get(new ThreadIdentifier(new byte[0], 1)); assertFalse(seo.getAckSend()); // should not send an ack as redundancy level = 0; seo = (SequenceIdAndExpirationObject) proxy.getThreadIdToSequenceIdMap() .get(new ThreadIdentifier(new byte[0], 1)); verifyAckSend(30 * 1000, false); // should expire without sending an ack as redundancy level = 0. verifyExpiry(90 * 1000); } catch (Exception ex) { ex.printStackTrace(); fail("Test testPeriodicAckSendByClient Failed"); } } finally { if (server != null) { server.stop(); } } } private void verifyAckSend(long timeToWait, final boolean expectedAckSend) { WaitCriterion wc = new WaitCriterion() { public boolean done() { return expectedAckSend == seo.getAckSend(); } public String description() { return "ack flag never became " + expectedAckSend; } }; Wait.waitForCriterion(wc, timeToWait, 1000, true); } private void verifyExpiry(long timeToWait) { WaitCriterion wc = new WaitCriterion() { public boolean done() { return 0 == proxy.getThreadIdToSequenceIdMap().size(); } public String description() { return "Entry never expired"; } }; Wait.waitForCriterion(wc, timeToWait * 2, 200, true); } }