/* * 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. */ package org.apache.geode.cache.client.internal; import static org.junit.Assert.*; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.SocketTimeoutException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import org.apache.geode.test.junit.categories.ClientServerTest; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.apache.geode.CancelCriterion; import org.apache.geode.LogWriter; import org.apache.geode.cache.client.NoAvailableServersException; import org.apache.geode.cache.client.ServerConnectivityException; import org.apache.geode.cache.client.ServerOperationException; import org.apache.geode.cache.client.internal.pooling.ConnectionManager; import org.apache.geode.distributed.DistributedMember; import org.apache.geode.distributed.internal.InternalDistributedSystem; import org.apache.geode.distributed.internal.ServerLocation; import org.apache.geode.internal.cache.tier.sockets.ServerQueueStatus; import org.apache.geode.internal.logging.InternalLogWriter; import org.apache.geode.internal.logging.LocalLogWriter; import org.apache.geode.test.junit.categories.UnitTest; @Category({UnitTest.class, ClientServerTest.class}) public class OpExecutorImplJUnitTest { DummyManager manager; private LogWriter logger; private DummyEndpointManager endpointManager; private DummyQueueManager queueManager; private RegisterInterestTracker riTracker; protected int borrows; protected int returns; protected int invalidateConnections; protected int exchanges; protected int serverCrashes; protected int getPrimary; protected int getBackups; private CancelCriterion cancelCriterion; @Before public void setUp() { this.logger = new LocalLogWriter(InternalLogWriter.FINEST_LEVEL, System.out); this.endpointManager = new DummyEndpointManager(); this.queueManager = new DummyQueueManager(); this.manager = new DummyManager(); riTracker = new RegisterInterestTracker(); cancelCriterion = new CancelCriterion() { @Override public String cancelInProgress() { return null; } @Override public RuntimeException generateCancelledException(Throwable e) { return null; } }; } @Test public void testExecute() throws Exception { OpExecutorImpl exec = new OpExecutorImpl(manager, queueManager, endpointManager, riTracker, 3, 10, false, cancelCriterion, null); Object result = exec.execute(new Op() { @Override public Object attempt(Connection cnx) throws Exception { return "hello"; } @Override public boolean useThreadLocalConnection() { return true; } }); assertEquals("hello", result); assertEquals(1, borrows); assertEquals(1, returns); assertEquals(0, invalidateConnections); assertEquals(0, serverCrashes); reset(); try { result = exec.execute(new Op() { @Override public Object attempt(Connection cnx) throws Exception { throw new SocketTimeoutException(); } @Override public boolean useThreadLocalConnection() { return true; } }); fail("Should have got an exception"); } catch (ServerConnectivityException expected) { // do nothing } assertEquals(1, borrows); assertEquals(3, exchanges); assertEquals(1, returns); assertEquals(4, invalidateConnections); assertEquals(0, serverCrashes); reset(); try { result = exec.execute(new Op() { @Override public Object attempt(Connection cnx) throws Exception { throw new ServerOperationException("Something didn't work"); } @Override public boolean useThreadLocalConnection() { return true; } }); fail("Should have got an exception"); } catch (ServerOperationException expected) { // do nothing } assertEquals(1, borrows); assertEquals(1, returns); assertEquals(0, invalidateConnections); assertEquals(0, serverCrashes); reset(); try { result = exec.execute(new Op() { @Override public Object attempt(Connection cnx) throws Exception { throw new IOException("Something didn't work"); } @Override public boolean useThreadLocalConnection() { return true; } }); fail("Should have got an exception"); } catch (ServerConnectivityException expected) { // do nothing } assertEquals(1, borrows); assertEquals(3, exchanges); assertEquals(1, returns); assertEquals(4, invalidateConnections); assertEquals(4, serverCrashes); } private void reset() { borrows = 0; returns = 0; invalidateConnections = 0; exchanges = 0; serverCrashes = 0; getPrimary = 0; getBackups = 0; } @Test public void testExecuteOncePerServer() throws Exception { OpExecutorImpl exec = new OpExecutorImpl(manager, queueManager, endpointManager, riTracker, -1, 10, false, cancelCriterion, null); manager.numServers = 5; try { exec.execute(new Op() { @Override public Object attempt(Connection cnx) throws Exception { throw new IOException("Something didn't work"); } @Override public boolean useThreadLocalConnection() { return true; } }); fail("Should have got an exception"); } catch (ServerConnectivityException expected) { // do nothing } assertEquals(1, borrows); assertEquals(4, exchanges); assertEquals(1, returns); assertEquals(6, invalidateConnections); assertEquals(6, serverCrashes); } @Test public void testRetryFailedServers() throws Exception { OpExecutorImpl exec = new OpExecutorImpl(manager, queueManager, endpointManager, riTracker, 10, 10, false, cancelCriterion, null); manager.numServers = 5; try { exec.execute(new Op() { @Override public Object attempt(Connection cnx) throws Exception { throw new IOException("Something didn't work"); } @Override public boolean useThreadLocalConnection() { return true; } }); fail("Should have got an exception"); } catch (ServerConnectivityException expected) { // do nothing } assertEquals(1, borrows); assertEquals(10, exchanges); assertEquals(1, returns); assertEquals(11, invalidateConnections); assertEquals(11, serverCrashes); } @Test public void testExecuteOn() throws Exception { OpExecutorImpl exec = new OpExecutorImpl(manager, queueManager, endpointManager, riTracker, 3, 10, false, cancelCriterion, null); ServerLocation server = new ServerLocation("localhost", -1); Object result = exec.executeOn(server, new Op() { @Override public Object attempt(Connection cnx) throws Exception { return "hello"; } @Override public boolean useThreadLocalConnection() { return true; } }); assertEquals("hello", result); assertEquals(1, borrows); assertEquals(1, returns); assertEquals(0, invalidateConnections); assertEquals(0, serverCrashes); reset(); try { result = exec.executeOn(server, new Op() { @Override public Object attempt(Connection cnx) throws Exception { throw new SocketTimeoutException(); } @Override public boolean useThreadLocalConnection() { return true; } }); fail("Should have got an exception"); } catch (ServerConnectivityException expected) { // do nothing } assertEquals(1, borrows); assertEquals(1, returns); assertEquals(1, invalidateConnections); assertEquals(0, serverCrashes); reset(); try { result = exec.executeOn(server, new Op() { @Override public Object attempt(Connection cnx) throws Exception { throw new ServerOperationException("Something didn't work"); } @Override public boolean useThreadLocalConnection() { return true; } }); fail("Should have got an exception"); } catch (ServerOperationException expected) { // do nothing } assertEquals(1, borrows); assertEquals(1, returns); assertEquals(0, invalidateConnections); assertEquals(0, serverCrashes); reset(); { final String expectedEx = "java.lang.Exception"; final String addExpected = "<ExpectedException action=add>" + expectedEx + "</ExpectedException>"; final String removeExpected = "<ExpectedException action=remove>" + expectedEx + "</ExpectedException>"; logger.info(addExpected); try { result = exec.executeOn(server, new Op() { @Override public Object attempt(Connection cnx) throws Exception { throw new Exception("Something didn't work"); } @Override public boolean useThreadLocalConnection() { return true; } }); fail("Should have got an exception"); } catch (ServerConnectivityException expected) { // do nothing } finally { logger.info(removeExpected); } } assertEquals(1, borrows); assertEquals(1, returns); assertEquals(1, invalidateConnections); assertEquals(1, serverCrashes); } @Test public void testExecuteOnAllQueueServers() { OpExecutorImpl exec = new OpExecutorImpl(manager, queueManager, endpointManager, riTracker, 3, 10, false, cancelCriterion, null); exec.executeOnAllQueueServers(new Op() { @Override public Object attempt(Connection cnx) throws Exception { return "hello"; } @Override public boolean useThreadLocalConnection() { return true; } }); assertEquals(0, invalidateConnections); assertEquals(0, serverCrashes); assertEquals(1, getPrimary); assertEquals(1, getBackups); reset(); queueManager.backups = 3; exec.executeOnAllQueueServers(new Op() { @Override public Object attempt(Connection cnx) throws Exception { throw new SocketTimeoutException(); } @Override public boolean useThreadLocalConnection() { return true; } }); assertEquals(4, invalidateConnections); assertEquals(0, serverCrashes); assertEquals(1, getPrimary); assertEquals(1, getBackups); reset(); queueManager.backups = 3; Object result = exec.executeOnQueuesAndReturnPrimaryResult(new Op() { int i = 0; @Override public Object attempt(Connection cnx) throws Exception { i++; if (i < 15) { throw new IOException(); } return "hello"; } @Override public boolean useThreadLocalConnection() { return true; } }); assertEquals("hello", result); assertEquals(14, serverCrashes); assertEquals(14, invalidateConnections); assertEquals(12, getPrimary); assertEquals(1, getBackups); } @Test public void testThreadLocalConnection() { OpExecutorImpl exec = new OpExecutorImpl(manager, queueManager, endpointManager, riTracker, 3, 10, true, cancelCriterion, null); ServerLocation server = new ServerLocation("localhost", -1); Op op = new Op() { @Override public Object attempt(Connection cnx) throws Exception { // do nothing return cnx; } @Override public boolean useThreadLocalConnection() { return true; } }; exec.execute(op); assertEquals(1, borrows); assertEquals(0, returns); reset(); exec.execute(op); assertEquals(0, borrows); assertEquals(0, returns); reset(); exec.executeOn(server, op); assertEquals(1, borrows); assertEquals(0, returns); reset(); exec.executeOn(server, op); assertEquals(0, borrows); assertEquals(0, returns); exec.execute(op); reset(); assertEquals(0, borrows); assertEquals(0, returns); } private class DummyManager implements ConnectionManager { protected int numServers = Integer.MAX_VALUE; private int currentServer = 0; public DummyManager() {} @Override public void emergencyClose() {} @Override public Connection borrowConnection(long aquireTimeout) { borrows++; return new DummyConnection(new ServerLocation("localhost", currentServer++ % numServers)); } /* * (non-Javadoc) * * @see * org.apache.geode.cache.client.internal.pooling.ConnectionManager#borrowConnection(org.apache. * geode.distributed.internal.ServerLocation, long) */ @Override public Connection borrowConnection(ServerLocation server, long aquireTimeout, boolean onlyUseExistingCnx) { borrows++; return new DummyConnection(server); } @Override public void close(boolean keepAlive) {} @Override public void returnConnection(Connection connection) { returns++; } @Override public void returnConnection(Connection connection, boolean accessed) { returns++; } @Override public void start(ScheduledExecutorService backgroundProcessor) {} @Override public Connection exchangeConnection(Connection conn, Set excludedServers, long aquireTimeout) { if (excludedServers.size() >= numServers) { throw new NoAvailableServersException(); } exchanges++; return new DummyConnection(new ServerLocation("localhost", currentServer++ % numServers)); } @Override public int getConnectionCount() { return 0; } @Override public Connection getConnection(Connection conn) { return conn; } @Override public void activate(Connection conn) {} @Override public void passivate(Connection conn, boolean accessed) {} } private class DummyConnection implements Connection { private ServerLocation server; public DummyConnection(ServerLocation serverLocation) { this.server = serverLocation; } @Override public void close(boolean keepAlive) throws Exception {} @Override public void destroy() { invalidateConnections++; } @Override public boolean isDestroyed() { return false; } @Override public ByteBuffer getCommBuffer() { return null; } @Override public ServerLocation getServer() { return server; } @Override public Socket getSocket() { return null; } @Override public ConnectionStats getStats() { return null; } @Override public int getDistributedSystemId() { return 0; } @Override public Endpoint getEndpoint() { return new Endpoint(null, null, null, null, null); } @Override public ServerQueueStatus getQueueStatus() { return null; } @Override public Object execute(Op op) throws Exception { return op.attempt(this); } @Override public void emergencyClose() {} @Override public short getWanSiteVersion() { return -1; } @Override public void setWanSiteVersion(short wanSiteVersion) {} @Override public InputStream getInputStream() { return null; } @Override public OutputStream getOutputStream() { return null; } @Override public void setConnectionID(long id) {} @Override public long getConnectionID() { return 0; } } private class DummyEndpointManager implements EndpointManager { @Override public void addListener(EndpointListener listener) {} @Override public void close() {} @Override public Endpoint referenceEndpoint(ServerLocation server, DistributedMember memberId) { return null; } @Override public Map getEndpointMap() { return null; } @Override public void removeListener(EndpointListener listener) {} @Override public void serverCrashed(Endpoint endpoint) { serverCrashes++; } @Override public int getConnectedServerCount() { return 0; } @Override public Map getAllStats() { return null; } @Override public String getPoolName() { return null; } } private class DummyQueueManager implements QueueManager { int backups = 0; int currentServer = 0; public QueueConnections getAllConnectionsNoWait() { return getAllConnections(); } @Override public void emergencyClose() {} @Override public QueueConnections getAllConnections() { return new QueueConnections() { @Override public List getBackups() { getBackups++; ArrayList result = new ArrayList(backups); for (int i = 0; i < backups; i++) { result.add(new DummyConnection(new ServerLocation("localhost", currentServer++))); } return result; } @Override public Connection getPrimary() { getPrimary++; return new DummyConnection(new ServerLocation("localhost", currentServer++)); } @Override public QueueConnectionImpl getConnection(Endpoint ep) { return null; } }; } @Override public void close(boolean keepAlive) {} @Override public void start(ScheduledExecutorService background) {} @Override public QueueState getState() { return null; } @Override public InternalPool getPool() { return null; } @Override public void readyForEvents(InternalDistributedSystem system) {} @Override public InternalLogWriter getSecurityLogger() { return null; } @Override public void checkEndpoint(ClientUpdater qc, Endpoint endpoint) {} } }