/*
* 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.internal.cache.tier.sockets;
import static org.apache.geode.distributed.ConfigurationProperties.*;
import java.io.EOFException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.BindException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLException;
import org.apache.logging.log4j.Logger;
import org.apache.geode.CancelException;
import org.apache.geode.SystemFailure;
import org.apache.geode.ToDataException;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.RegionDestroyedException;
import org.apache.geode.cache.client.internal.PoolImpl;
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.cache.wan.GatewayTransportFilter;
import org.apache.geode.distributed.internal.*;
import org.apache.geode.internal.net.SocketCreator;
import org.apache.geode.internal.SystemTimer;
import org.apache.geode.internal.cache.BucketAdvisor;
import org.apache.geode.internal.cache.BucketAdvisor.BucketProfile;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.internal.cache.InternalCache;
import org.apache.geode.internal.cache.PartitionedRegion;
import org.apache.geode.internal.cache.partitioned.AllBucketProfilesUpdateMessage;
import org.apache.geode.internal.cache.tier.Acceptor;
import org.apache.geode.internal.cache.tier.CachedRegionHelper;
import org.apache.geode.internal.cache.wan.GatewayReceiverStats;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.logging.LoggingThreadGroup;
import org.apache.geode.internal.logging.log4j.LocalizedMessage;
import org.apache.geode.internal.security.IntegratedSecurityService;
import org.apache.geode.internal.security.SecurableCommunicationChannel;
import org.apache.geode.internal.security.SecurityService;
import org.apache.geode.internal.net.SocketCreatorFactory;
import org.apache.geode.internal.tcp.ConnectionTable;
import org.apache.geode.internal.util.ArrayUtils;
/**
* Implements the acceptor thread on the bridge server. Accepts connections from the edge and starts
* up threads to process requests from these.
*
* @since GemFire 2.0.2
*/
@SuppressWarnings("deprecation")
public class AcceptorImpl extends Acceptor implements Runnable {
private static final Logger logger = LogService.getLogger();
private static final boolean isJRockit = System.getProperty("java.vm.name").contains("JRockit");
protected final CacheServerStats stats;
private final int maxConnections;
private final int maxThreads;
private final ThreadPoolExecutor pool;
/**
* A pool used to process handshakes.
*/
private final ThreadPoolExecutor hsPool;
/** The port on which this acceptor listens for client connections */
private final int localPort;
/** The server socket that handles requests for connections */
private ServerSocket serverSock = null;
/** The GemFire cache served up by this acceptor */
protected final InternalCache cache;
/** Caches region information */
private final CachedRegionHelper crHelper;
/** A lock to prevent close from occurring while creating a ServerConnection */
private final Object syncLock = new Object();
/**
* THE selector for the bridge server; null if no selector.
*/
private final Selector selector;
// private final Selector tmpSel;
/**
* Used for managing direct byte buffer for client comms; null if no selector.
*/
private final LinkedBlockingQueue commBufferQueue;
/**
* Used to timeout accepted sockets that we are waiting for the handshake packet
*/
private final SystemTimer hsTimer;
/**
* A queue used to feed register requests to the selector; null if no selector.
*/
private final LinkedBlockingQueue selectorQueue;
/**
* All the objects currently registered with selector.
*/
private final HashSet selectorRegistrations;
/**
* tcpNoDelay setting for outgoing sockets
*/
private final boolean tcpNoDelay;
/**
* The name of a system property that sets the hand shake timeout (in milliseconds). This is how
* long a client will wait to hear back from a server.
*/
public static final String HANDSHAKE_TIMEOUT_PROPERTY_NAME = "BridgeServer.handShakeTimeout";
/**
* The default value of the {@link #HANDSHAKE_TIMEOUT_PROPERTY_NAME} system property.
*/
public static final int DEFAULT_HANDSHAKE_TIMEOUT_MS = 59000;
/** Test value for handshake timeout */
protected static final int handShakeTimeout =
Integer.getInteger(HANDSHAKE_TIMEOUT_PROPERTY_NAME, DEFAULT_HANDSHAKE_TIMEOUT_MS).intValue();
/**
* The name of a system property that sets the accept timeout (in milliseconds). This is how long
* a server will wait to get its first byte from a client it has just accepted.
*/
public static final String ACCEPT_TIMEOUT_PROPERTY_NAME = "BridgeServer.acceptTimeout";
/**
* The default value of the {@link #ACCEPT_TIMEOUT_PROPERTY_NAME} system property.
*/
public static final int DEFAULT_ACCEPT_TIMEOUT_MS = 9900;
/** Test value for accept timeout */
private final int acceptTimeout =
Integer.getInteger(ACCEPT_TIMEOUT_PROPERTY_NAME, DEFAULT_ACCEPT_TIMEOUT_MS).intValue();
/** The mininum value of max-connections */
public static final int MINIMUM_MAX_CONNECTIONS = 16;
/** The buffer size for server-side sockets. */
private final int socketBufferSize;
/** Notifies clients of updates */
private CacheClientNotifier clientNotifier;
/**
* The default value of the {@link ServerSocket} {@link #BACKLOG_PROPERTY_NAME}system property
*/
private static final int DEFAULT_BACKLOG = 1000;
/** The system property name for setting the {@link ServerSocket}backlog */
public static final String BACKLOG_PROPERTY_NAME = "BridgeServer.backlog";
/**
* Current number of ServerConnection instances that are CLIENT_TO_SERVER cons.
*/
public final AtomicInteger clientServerCnxCount = new AtomicInteger();
/** Has this acceptor been shut down */
private volatile boolean shutdown = false;
/** The thread that runs the acceptor */
private Thread thread = null;
/** The thread that runs the selector loop if any */
private Thread selectorThread = null;
/**
* Controls updates to {@link #allSCs}
*/
private final Object allSCsLock = new Object();
/**
* List of ServerConnection.
*
* Instances added when constructed; removed when terminated.
*
* guarded.By {@link #allSCsLock}
*/
private final HashSet allSCs = new HashSet();
/**
* List of ServerConnections, for {@link #emergencyClose()}
*
* guarded.By {@link #allSCsLock}
*/
private volatile ServerConnection allSCList[] = new ServerConnection[0];
/**
* The ip address or host name this acceptor is to bind to; <code>null</code> or "" indicates it
* will listen on all local addresses.
*
* @since GemFire 5.7
*/
private final String bindHostName;
/**
* A listener for connect/disconnect events
*/
private final ConnectionListener connectionListener;
/** The client health monitor tracking connections for this acceptor */
private ClientHealthMonitor healthMonitor;
/** bridge's setting of notifyBySubscription */
private final boolean notifyBySubscription;
/**
* The AcceptorImpl identifier, used to identify the clients connected to this Acceptor.
*/
private long acceptorId;
private static boolean isAuthenticationRequired;
private static boolean isIntegratedSecurity;
private static boolean isPostAuthzCallbackPresent;
private boolean isGatewayReceiver;
private List<GatewayTransportFilter> gatewayTransportFilters;
private final SocketCreator socketCreator;
private SecurityService securityService = IntegratedSecurityService.getSecurityService();
/**
* Initializes this acceptor thread to listen for connections on the given port.
*
* @param port The port on which this acceptor listens for connections. If <code>0</code>, a
* random port will be chosen.
* @param bindHostName The ip address or host name this acceptor listens on for connections. If
* <code>null</code> or "" then all local addresses are used
* @param socketBufferSize The buffer size for server-side sockets
* @param maximumTimeBetweenPings The maximum time between client pings. This value is used by the
* <code>ClientHealthMonitor</code> to monitor the health of this server's clients.
* @param internalCache The GemFire cache whose contents is served to clients
* @param maxConnections the maximum number of connections allowed in the server pool
* @param maxThreads the maximum number of threads allowed in the server pool
*
* @see SocketCreator#createServerSocket(int, int, InetAddress)
* @see ClientHealthMonitor
* @since GemFire 5.7
*/
public AcceptorImpl(int port, String bindHostName, boolean notifyBySubscription,
int socketBufferSize, int maximumTimeBetweenPings, InternalCache internalCache,
int maxConnections, int maxThreads, int maximumMessageCount, int messageTimeToLive,
ConnectionListener listener, List overflowAttributesList, boolean isGatewayReceiver,
List<GatewayTransportFilter> transportFilter, boolean tcpNoDelay) throws IOException {
this.bindHostName = calcBindHostName(internalCache, bindHostName);
this.connectionListener = listener == null ? new ConnectionListenerAdapter() : listener;
this.notifyBySubscription = notifyBySubscription;
this.isGatewayReceiver = isGatewayReceiver;
this.gatewayTransportFilters = transportFilter;
{
int tmp_maxConnections = maxConnections;
if (tmp_maxConnections < MINIMUM_MAX_CONNECTIONS) {
tmp_maxConnections = MINIMUM_MAX_CONNECTIONS;
}
this.maxConnections = tmp_maxConnections;
}
{
int tmp_maxThreads = maxThreads;
if (maxThreads == CacheServer.DEFAULT_MAX_THREADS) {
// consult system properties for 5.0.2 backwards compatibility
if (DEPRECATED_SELECTOR) {
tmp_maxThreads = DEPRECATED_SELECTOR_POOL_SIZE;
}
}
if (tmp_maxThreads < 0) {
tmp_maxThreads = 0;
} else if (tmp_maxThreads > this.maxConnections) {
tmp_maxThreads = this.maxConnections;
}
boolean isWindows = false;
String os = System.getProperty("os.name");
if (os != null) {
if (os.indexOf("Windows") != -1) {
isWindows = true;
}
}
if (tmp_maxThreads > 0 && isWindows) {
// bug #40472 and JDK bug 6230761 - NIO can't be used with IPv6 on Windows
if (getBindAddress() instanceof Inet6Address) {
logger.warn(LocalizedMessage
.create(LocalizedStrings.AcceptorImpl_IGNORING_MAX_THREADS_DUE_TO_JROCKIT_NIO_BUG));
tmp_maxThreads = 0;
}
// bug #40198 - Selector.wakeup() hangs if VM starts to exit
if (isJRockit) {
logger.warn(LocalizedMessage
.create(LocalizedStrings.AcceptorImpl_IGNORING_MAX_THREADS_DUE_TO_WINDOWS_IPV6_BUG));
tmp_maxThreads = 0;
}
}
this.maxThreads = tmp_maxThreads;
}
{
Selector tmp_s = null;
// Selector tmp2_s = null;
LinkedBlockingQueue tmp_q = null;
LinkedBlockingQueue tmp_commQ = null;
HashSet tmp_hs = null;
SystemTimer tmp_timer = null;
if (isSelector()) {
tmp_s = Selector.open(); // no longer catch ex to fix bug 36907
// tmp2_s = Selector.open(); // workaround for bug 39624
tmp_q = new LinkedBlockingQueue();
tmp_commQ = new LinkedBlockingQueue();
tmp_hs = new HashSet(512);
tmp_timer = new SystemTimer(internalCache.getDistributedSystem(), true);
}
this.selector = tmp_s;
// this.tmpSel = tmp2_s;
this.selectorQueue = tmp_q;
this.commBufferQueue = tmp_commQ;
this.selectorRegistrations = tmp_hs;
this.hsTimer = tmp_timer;
this.tcpNoDelay = tcpNoDelay;
}
{
if (!isGatewayReceiver) {
// If configured use SSL properties for cache-server
this.socketCreator =
SocketCreatorFactory.getSocketCreatorForComponent(SecurableCommunicationChannel.SERVER);
} else {
this.socketCreator = SocketCreatorFactory
.getSocketCreatorForComponent(SecurableCommunicationChannel.GATEWAY);
}
final GemFireCacheImpl gc;
if (getCachedRegionHelper() != null) {
gc = (GemFireCacheImpl) getCachedRegionHelper().getCache();
} else {
gc = null;
}
final int backLog = Integer.getInteger(BACKLOG_PROPERTY_NAME, DEFAULT_BACKLOG).intValue();
final long tilt = System.currentTimeMillis() + 120 * 1000;
if (isSelector()) {
if (this.socketCreator.useSSL()) {
throw new IllegalArgumentException(
LocalizedStrings.AcceptorImpl_SELECTOR_THREAD_POOLING_CAN_NOT_BE_USED_WITH_CLIENTSERVER_SSL_THE_SELECTOR_CAN_BE_DISABLED_BY_SETTING_MAXTHREADS0
.toLocalizedString());
}
ServerSocketChannel channel = ServerSocketChannel.open();
this.serverSock = channel.socket();
this.serverSock.setReuseAddress(true);
// Set the receive buffer size before binding the socket so that large
// buffers will be allocated on accepted sockets (see
// java.net.ServerSocket.setReceiverBufferSize javadocs)
this.serverSock.setReceiveBufferSize(socketBufferSize);
// fix for bug 36617. If BindException is thrown, retry after
// sleeping. The server may have been stopped and then
// immediately restarted, which sometimes results in a bind exception
for (;;) {
try {
this.serverSock.bind(new InetSocketAddress(getBindAddress(), port), backLog);
break;
} catch (SocketException b) {
if (!treatAsBindException(b) || System.currentTimeMillis() > tilt) {
throw b;
}
}
boolean interrupted = Thread.interrupted();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
interrupted = true;
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
if (gc != null) {
gc.getCancelCriterion().checkCancelInProgress(null);
}
} // for
} // isSelector
else { // !isSelector
// fix for bug 36617. If BindException is thrown, retry after
// sleeping. The server may have been stopped and then
// immediately restarted, which sometimes results in a bind exception
for (;;) {
try {
this.serverSock = this.socketCreator.createServerSocket(port, backLog, getBindAddress(),
this.gatewayTransportFilters, socketBufferSize);
break;
} catch (SocketException e) {
if (!treatAsBindException(e) || System.currentTimeMillis() > tilt) {
throw e;
}
}
boolean interrupted = Thread.interrupted();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
interrupted = true;
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
if (gc != null) {
gc.getCancelCriterion().checkCancelInProgress(null);
}
} // for
} // !isSelector
if (port == 0) {
port = this.serverSock.getLocalPort();
}
{
InternalDistributedSystem ds = InternalDistributedSystem.getConnectedInstance();
if (ds != null) {
DM dm = ds.getDistributionManager();
if (dm != null && dm.getDistributionManagerId().getPort() == 0
&& (dm instanceof LonerDistributionManager)) {
// a server with a loner distribution manager - update it's port number
((LonerDistributionManager) dm).updateLonerPort(port);
}
}
}
this.localPort = port;
String sockName = this.serverSock.getLocalSocketAddress().toString();
logger.info(LocalizedMessage.create(
LocalizedStrings.AcceptorImpl_CACHE_SERVER_CONNECTION_LISTENER_BOUND_TO_ADDRESS_0_WITH_BACKLOG_1,
new Object[] {sockName, Integer.valueOf(backLog)}));
if (isGatewayReceiver) {
this.stats = GatewayReceiverStats.createGatewayReceiverStats(sockName);
} else {
this.stats = new CacheServerStats(sockName);
}
}
this.cache = internalCache;
this.crHelper = new CachedRegionHelper(this.cache);
this.clientNotifier = CacheClientNotifier.getInstance(cache, this.stats, maximumMessageCount,
messageTimeToLive, connectionListener, overflowAttributesList, isGatewayReceiver);
this.socketBufferSize = socketBufferSize;
// Create the singleton ClientHealthMonitor
this.healthMonitor = ClientHealthMonitor.getInstance(internalCache, maximumTimeBetweenPings,
this.clientNotifier.getStats());
{
ThreadPoolExecutor tmp_pool = null;
String gName = "ServerConnection "
// + serverSock.getInetAddress()
+ "on port " + this.localPort;
final ThreadGroup socketThreadGroup = LoggingThreadGroup.createThreadGroup(gName, logger);
ThreadFactory socketThreadFactory = new ThreadFactory() {
int connNum = -1;
public Thread newThread(final Runnable command) {
int tnum;
synchronized (this) {
tnum = ++connNum;
}
String tName = socketThreadGroup.getName() + " Thread " + tnum;
getStats().incConnectionThreadsCreated();
Runnable r = new Runnable() {
public void run() {
try {
command.run();
} catch (CancelException e) { // bug 39463
// ignore
} finally {
ConnectionTable.releaseThreadsSockets();
}
}
};
return new Thread(socketThreadGroup, r, tName);
}
};
try {
if (isSelector()) {
tmp_pool = new PooledExecutorWithDMStats(new LinkedBlockingQueue(), this.maxThreads,
getStats().getCnxPoolHelper(), socketThreadFactory, Integer.MAX_VALUE);
} else {
tmp_pool = new ThreadPoolExecutor(MINIMUM_MAX_CONNECTIONS, this.maxConnections, 0L,
TimeUnit.MILLISECONDS, new SynchronousQueue(), socketThreadFactory);
}
} catch (IllegalArgumentException poolInitException) {
this.stats.close();
this.serverSock.close();
throw poolInitException;
}
this.pool = tmp_pool;
}
{
ThreadPoolExecutor tmp_hsPool = null;
String gName = "Handshaker " + serverSock.getInetAddress() + ":" + this.localPort;
final ThreadGroup socketThreadGroup = LoggingThreadGroup.createThreadGroup(gName, logger);
ThreadFactory socketThreadFactory = new ThreadFactory() {
int connNum = -1;
public Thread newThread(Runnable command) {
int tnum;
synchronized (this) {
tnum = ++connNum;
}
String tName = socketThreadGroup.getName() + " Thread " + tnum;
getStats().incAcceptThreadsCreated();
return new Thread(socketThreadGroup, command, tName);
}
};
try {
final BlockingQueue bq = new SynchronousQueue();
final RejectedExecutionHandler reh = new RejectedExecutionHandler() {
public void rejectedExecution(Runnable r, ThreadPoolExecutor pool) {
try {
bq.put(r);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt(); // preserve the state
throw new RejectedExecutionException(
LocalizedStrings.AcceptorImpl_INTERRUPTED.toLocalizedString(), ex);
}
}
};
tmp_hsPool = new ThreadPoolExecutor(1, HANDSHAKE_POOL_SIZE, 60, TimeUnit.SECONDS, bq,
socketThreadFactory, reh);
} catch (IllegalArgumentException poolInitException) {
this.stats.close();
this.serverSock.close();
this.pool.shutdownNow();
throw poolInitException;
}
this.hsPool = tmp_hsPool;
}
isAuthenticationRequired = this.securityService.isClientSecurityRequired();
isIntegratedSecurity = this.securityService.isIntegratedSecurity();
String postAuthzFactoryName =
this.cache.getDistributedSystem().getProperties().getProperty(SECURITY_CLIENT_ACCESSOR_PP);
isPostAuthzCallbackPresent =
(postAuthzFactoryName != null && postAuthzFactoryName.length() > 0) ? true : false;
}
public long getAcceptorId() {
return this.acceptorId;
}
public CacheServerStats getStats() {
return this.stats;
}
/**
* Returns true if this acceptor is using a selector to detect client events.
*/
public boolean isSelector() {
return this.maxThreads > 0;
}
/**
* This system property is only used if max-threads == 0. This is for 5.0.2 backwards
* compatibility.
*
* @deprecated since 5.1 use cache-server max-threads instead
*/
@Deprecated
private static final boolean DEPRECATED_SELECTOR = Boolean.getBoolean("BridgeServer.SELECTOR");
/**
* This system property is only used if max-threads == 0. This is for 5.0.2 backwards
* compatibility.
*
* @deprecated since 5.1 use cache-server max-threads instead
*/
@Deprecated
private final static int DEPRECATED_SELECTOR_POOL_SIZE =
Integer.getInteger("BridgeServer.SELECTOR_POOL_SIZE", 16).intValue();
private final static int HANDSHAKE_POOL_SIZE =
Integer.getInteger("BridgeServer.HANDSHAKE_POOL_SIZE", 4).intValue();
@Override
public void start() throws IOException {
ThreadGroup tg = LoggingThreadGroup.createThreadGroup(
"Acceptor " + this.serverSock.getInetAddress() + ":" + this.localPort, logger);
thread = new Thread(tg, this, "Cache Server Acceptor " + this.serverSock.getInetAddress() + ":"
+ this.localPort + " local port: " + this.serverSock.getLocalPort());
this.acceptorId = thread.getId();
// This thread should not be a daemon to keep BridgeServers created
// in code from exiting immediately.
thread.start();
if (isSelector()) {
Runnable r = new Runnable() {
public void run() {
AcceptorImpl.this.runSelectorLoop();
}
};
this.selectorThread =
new Thread(tg, r, "Cache Server Selector " + this.serverSock.getInetAddress() + ":"
+ this.localPort + " local port: " + this.serverSock.getLocalPort());
this.selectorThread.start();
}
GemFireCacheImpl myCache = (GemFireCacheImpl) cache;
Set<PartitionedRegion> prs = myCache.getPartitionedRegions();
for (PartitionedRegion pr : prs) {
Map<Integer, BucketAdvisor.BucketProfile> profiles =
new HashMap<Integer, BucketAdvisor.BucketProfile>();
// get all local real bucket advisors
Map<Integer, BucketAdvisor> advisors = pr.getRegionAdvisor().getAllBucketAdvisors();
for (Map.Entry<Integer, BucketAdvisor> entry : advisors.entrySet()) {
BucketAdvisor advisor = entry.getValue();
// addLocally
BucketProfile bp = (BucketProfile) advisor.createProfile();
advisor.updateServerBucketProfile(bp);
// advisor.basicAddClientProfile(bp);
profiles.put(entry.getKey(), bp);
}
Set receipients = new HashSet();
receipients = pr.getRegionAdvisor().adviseAllPRNodes();
// send it to all in one messgae
ReplyProcessor21 reply = AllBucketProfilesUpdateMessage.send(receipients,
pr.getDistributionManager(), pr.getPRId(), profiles, true);
if (reply != null) {
reply.waitForRepliesUninterruptibly();
}
}
}
public void registerSC(ServerConnection sc) {
synchronized (this.syncLock) {
if (!isRunning()) {
finishCon(sc);
return;
}
}
getSelectorQueue().offer(sc);
wakeupSelector();
}
/** wake up the selector thread */
private void wakeupSelector() {
Selector s = getSelector();
if (s != null && s.isOpen()) {
this.selector.wakeup();
}
}
public void unregisterSC(ServerConnection sc) {
// removed syncLock synchronization to fix bug 37104
synchronized (this.allSCsLock) {
this.allSCs.remove(sc);
Iterator it = this.allSCs.iterator();
ServerConnection again[] = new ServerConnection[this.allSCs.size()];
for (int i = 0; i < again.length; i++) {
again[i] = (ServerConnection) it.next();
}
this.allSCList = again;
}
if (!isRunning()) {
return;
}
// just need to wake the selector up so it will notice our socket was closed
wakeupSelector();
}
private void finishCon(ServerConnection sc) {
if (sc != null) {
sc.handleTermination();
}
}
private void drainSelectorQueue() {
ServerConnection sc = (ServerConnection) this.selectorQueue.poll();
CancelException cce = null;
while (sc != null) {
try {
finishCon(sc);
} catch (CancelException e) {
if (cce == null) {
cce = e;
}
}
sc = (ServerConnection) this.selectorQueue.poll();
}
Iterator it = selectorRegistrations.iterator();
while (it.hasNext()) {
try {
finishCon((ServerConnection) it.next());
} catch (CancelException e) {
if (cce == null) {
cce = e;
}
}
} // while
if (cce != null) {
throw cce;
}
}
/**
* break any potential circularity in {@link #loadEmergencyClasses()}
*/
private static volatile boolean emergencyClassesLoaded = false;
/**
* Ensure that the CachedRegionHelper and ServerConnection classes get loaded.
*
* @see SystemFailure#loadEmergencyClasses()
*/
public static void loadEmergencyClasses() {
if (emergencyClassesLoaded)
return;
emergencyClassesLoaded = true;
CachedRegionHelper.loadEmergencyClasses();
ServerConnection.loadEmergencyClasses();
}
/**
* @see SystemFailure#emergencyClose()
*/
public void emergencyClose() {
ServerSocket ss = this.serverSock;
if (ss != null) {
try {
ss.close();
} catch (IOException e) {
// ignore
}
}
// this.selector.close(); might NOT be safe
this.crHelper.setShutdown(true);
// TODO I'm worried about a fat lock to acquire this synchronization
// synchronized (this.allSCsLock)
{
ServerConnection snap[] = this.allSCList;
for (int i = 0; i < snap.length; i++) {
snap[i].emergencyClose(); // part of cleanup()
}
}
}
private boolean isRegisteredObjectClosed(ServerConnection sc) {
return sc.isClosed();
}
private int checkRegisteredKeys(int count) {
int result = count;
CancelException cce = null;
if (count > 0) {
Iterator it = this.selectorRegistrations.iterator();
while (it.hasNext()) {
ServerConnection sc = (ServerConnection) it.next();
if (isRegisteredObjectClosed(sc)) {
result--;
it.remove();
try {
finishCon(sc);
} catch (CancelException e) {
if (cce == null) {
cce = e;
}
}
}
} // while
}
if (cce != null) {
throw cce;
}
return result;
}
private static final boolean WORKAROUND_SELECTOR_BUG =
Boolean.getBoolean("CacheServer.NIO_SELECTOR_WORKAROUND");
private Selector tmpSel;
private void checkForStuckKeys() {
if (!WORKAROUND_SELECTOR_BUG)
return;
if (tmpSel == null) {
try {
tmpSel = Selector.open();
} catch (IOException ignore) {
logger.warn(LocalizedMessage
.create(LocalizedStrings.AcceptorImpl_COULD_NOT_CHECK_FOR_STUCK_KEYS, ignore));
return;
}
}
// logger.info("DEBUG: checking for stuck keys");
Iterator it = (new ArrayList(this.selector.keys())).iterator();
while (it.hasNext()) {
SelectionKey sk = (SelectionKey) it.next();
ServerConnection sc = (ServerConnection) sk.attachment();
if (sc == null)
continue;
try {
sk.cancel();
this.selector.selectNow(); // clear the cancelled key
SelectionKey tmpsk = sc.getSelectableChannel().register(this.tmpSel,
SelectionKey.OP_WRITE | SelectionKey.OP_READ);
try {
// it should always be writable
int events = this.tmpSel.selectNow();
if (events == 0) {
logger.info(LocalizedMessage
.create(LocalizedStrings.AcceptorImpl_STUCK_SELECTION_KEY_DETECTED_ON_0, sc));
tmpsk.cancel();
tmpSel.selectNow(); // clear canceled key
sc.registerWithSelector2(this.selector);
} else {
if (tmpsk.isValid() && tmpsk.isReadable()) {
// logger.info("DEBUG detected read event on " + sc);
try {
tmpsk.cancel();
this.tmpSel.selectNow(); // clear canceled key
this.selectorRegistrations.remove(sc);
registeredKeys--;
sc.makeBlocking();
// we need to say we are processing a message
// so that that client health monitor will not
// kill us while we wait for a thread in the thread pool.
// This is also be used to determine how long we are
// in the thread pool queue and to cancel operations that
// have waited too long in the queue.
sc.setProcessingMessage();
} catch (ClosedChannelException ignore) {
finishCon(sc);
continue;
} catch (IOException ex) {
finishCon(sc);
if (isRunning()) {
logger.warn(LocalizedMessage
.create(LocalizedStrings.AcceptorImpl_UNEXPECTED_EXCEPTION, ex));
}
continue;
}
try {
AcceptorImpl.this.stats.incThreadQueueSize();
AcceptorImpl.this.pool.execute(sc);
} catch (RejectedExecutionException rejected) {
finishCon(sc);
AcceptorImpl.this.stats.decThreadQueueSize();
if (!isRunning()) {
break;
}
logger.warn(LocalizedMessage
.create(LocalizedStrings.AcceptorImpl_UNEXPECTED_EXCEPTION, rejected));
}
} else if (tmpsk.isValid() && tmpsk.isWritable()) {
// this is expected
tmpsk.cancel();
this.tmpSel.selectNow(); // clear canceled key
sc.registerWithSelector2(this.selector);
} else if (!tmpsk.isValid()) {
tmpsk.cancel();
this.tmpSel.selectNow(); // clear canceled key
sc.registerWithSelector2(this.selector);
}
}
} catch (IOException ex) {
if (isRunning() && this.selector.isOpen() && this.tmpSel.isOpen()) {
logger.warn(
LocalizedMessage.create(LocalizedStrings.AcceptorImpl_UNEXPECTED_EXCEPTION, ex));
try {
tmpsk.cancel();
tmpSel.selectNow(); // clear canceled key
} catch (IOException ex2) {
if (isRunning() && this.selector.isOpen() && this.tmpSel.isOpen()) {
logger.warn(LocalizedMessage
.create(LocalizedStrings.AcceptorImpl_UNEXPECTED_EXCEPTION, ex2));
}
}
}
}
} catch (ClosedChannelException ignore) { // fix for bug 39650
// just ignore this channel and try the next one
finishCon(sc);
continue;
} catch (IOException ex) {
if (isRunning() && this.selector.isOpen() && this.tmpSel.isOpen()) {
logger.warn(
LocalizedMessage.create(LocalizedStrings.AcceptorImpl_UNEXPECTED_EXCEPTION, ex));
}
} catch (NullPointerException npe) { // fix bug 39644
if (isRunning() && this.selector.isOpen() && this.tmpSel.isOpen()) {
logger.warn(
LocalizedMessage.create(LocalizedStrings.AcceptorImpl_UNEXPECTED_EXCEPTION, npe));
}
}
}
}
private int registeredKeys = 0;
public void runSelectorLoop() {
// int zeroEventsCount = 0;
try {
logger.info(LocalizedMessage.create(LocalizedStrings.AcceptorImpl_SELECTOR_ENABLED));
while (this.selector.isOpen() && !Thread.currentThread().isInterrupted()) {
{
SystemFailure.checkFailure();
// this.cache.getDistributedSystem().getCancelCriterion().checkCancelInProgress(null);
if (((GemFireCacheImpl) this.cache).isClosed()) { // bug 38834
break; // TODO should just ask cache's CancelCriterion
}
if (this.cache.getCancelCriterion().isCancelInProgress()) {
break;
}
ServerConnection sc;
registeredKeys = checkRegisteredKeys(registeredKeys);
if (registeredKeys == 0) {
// do blocking wait on queue until we get some guys registered
// with the selector
sc = (ServerConnection) this.selectorQueue.take();
} else {
// we already have some guys registered so just do a poll on queue
sc = (ServerConnection) this.selectorQueue.poll();
}
while (sc != null) {
try {
sc.registerWithSelector2(this.selector);
registeredKeys++;
this.selectorRegistrations.add(sc);
} catch (ClosedChannelException cce) {
// for bug bug 38474
finishCon(sc);
} catch (IOException ex) {
finishCon(sc);
logger.warn(LocalizedMessage.create(LocalizedStrings.AcceptorImpl_IGNORING, ex));
} catch (RuntimeException ex) {
finishCon(sc);
logger.warn(LocalizedMessage.create(LocalizedStrings.AcceptorImpl_IGNORING, ex));
}
sc = (ServerConnection) this.selectorQueue.poll();
}
}
if (registeredKeys == 0) {
continue;
}
int events = this.selector.select();
// select() could have returned due to wakeup() during close of cache
if (this.cache.getCancelCriterion().isCancelInProgress()) {
break;
}
if (events == 0) {
// zeroEventsCount++;
// if (zeroEventsCount > 0) {
// zeroEventsCount = 0;
checkForStuckKeys();
// try {
// this.selector.close(); // this selector is sick!
// } catch (IOException ignore) {
// }
// this.selector = Selector.open();
// {
// Iterator it = selectorRegistrations.iterator();
// while (it.hasNext()) {
// ServerConnection sc = (ServerConnection)it.next();
// sc.registerWithSelector2(this.selector);
// }
// }
// }
// ArrayList al = new ArrayList();
// Iterator keysIt = this.selector.keys().iterator();
// while (keysIt.hasNext()) {
// SelectionKey sk = (SelectionKey)keysIt.next();
// al.add(sk.attachment());
// sk.cancel();
// }
// events = this.selector.selectNow();
// Iterator alIt = al.iterator();
// while (alIt.hasNext()) {
// ServerConnection sc = (ServerConnection)alIt.next();
// sc.registerWithSelector2(this.selector);
// }
// events = this.selector.select();
// } else {
// zeroEventsCount = 0;
}
while (events > 0) {
int cancelCount = 0;
Set sk = this.selector.selectedKeys();
if (sk == null) {
// something really bad has happened I'm not even sure this is possible
// but lhughes so an NPE during close one time so perhaps it can happen
// during selector close.
events = 0;
break;
}
Iterator keysIterator = sk.iterator();
while (keysIterator.hasNext()) {
SelectionKey key = (SelectionKey) keysIterator.next();
// Remove the key from the selector's selectedKeys
keysIterator.remove();
final ServerConnection sc = (ServerConnection) key.attachment();
try {
if (key.isValid() && key.isReadable()) {
// this is the only event we currently register for
try {
key.cancel();
this.selectorRegistrations.remove(sc);
registeredKeys--;
cancelCount++;
sc.makeBlocking();
// we need to say we are processing a message
// so that that client health monitor will not
// kill us while we wait for a thread in the thread pool.
// This is also be used to determine how long we are
// in the thread pool queue and to cancel operations that
// have waited too long in the queue.
sc.setProcessingMessage();
} catch (ClosedChannelException ignore) {
finishCon(sc);
continue;
} catch (IOException ex) {
finishCon(sc);
if (isRunning()) {
logger.warn(
LocalizedMessage.create(LocalizedStrings.AcceptorImpl_UNEXPECTED, ex));
}
continue;
}
try {
AcceptorImpl.this.stats.incThreadQueueSize();
AcceptorImpl.this.pool.execute(sc);
} catch (RejectedExecutionException rejected) {
finishCon(sc);
AcceptorImpl.this.stats.decThreadQueueSize();
if (!isRunning()) {
break;
}
logger.warn(
LocalizedMessage.create(LocalizedStrings.AcceptorImpl_UNEXPECTED, rejected));
}
// } else if (key.isValid() && key.isConnectable()) {
// logger.info("DEBUG isConnectable and isValid key=" + key);
// finishCon(sc);
} else {
finishCon(sc);
if (key.isValid()) {
logger.warn(LocalizedMessage.create(
LocalizedStrings.AcceptorImpl_IGNORING_EVENT_ON_SELECTOR_KEY__0, key));
// } else {
// logger.info("DEBUG !isValid key=" + key);
}
}
} catch (CancelledKeyException ex) { // fix for bug 37739
finishCon(sc);
}
}
if (cancelCount > 0 && this.selector.isOpen()) {
// we need to do a select to cause the cancel to be unregisters.
events = this.selector.selectNow();
} else {
events = 0;
}
}
}
} catch (InterruptedException ex) {
// allow this thread to die
Thread.currentThread().interrupt();
} catch (ClosedSelectorException ex) {
// allow this thread to exit
} catch (IOException ex) {
logger.warn(LocalizedMessage.create(LocalizedStrings.AcceptorImpl_UNEXPECTED, ex));
} finally {
try {
drainSelectorQueue();
} finally {
// note that if this method was called by close then the
// following call is a noop since the first thing it does
// is call isRunning.
close(); // make sure this is called to fix bug 37749
}
}
}
@Override
public int getPort() {
return localPort;
}
public InetAddress getServerInetAddr() {
return this.serverSock.getInetAddress();
}
/**
* The work loop of this acceptor
*
* @see #accept
*/
public void run() {
try {
accept();
} catch (CancelException e) { // bug 39462
// ignore
} finally {
try {
if (this.serverSock != null) {
this.serverSock.close();
}
} catch (IOException ignore) {
}
if (this.stats != null) {
this.stats.close();
}
}
}
public Selector getSelector() {
return this.selector;
}
public BlockingQueue getSelectorQueue() {
return this.selectorQueue;
}
protected boolean loggedAcceptError = false;
protected static void closeSocket(Socket s) {
if (s != null) {
try {
s.close();
} catch (IOException ignore) {
}
}
}
/**
* {@linkplain ServerSocket#accept Listens}for a client to connect and then creates a new
* {@link ServerConnection}to handle messages from that client.
*/
@Override
public void accept() {
while (isRunning()) {
if (SystemFailure.getFailure() != null) {
// Allocate no objects here!
ServerSocket s = serverSock;
if (s != null) {
try {
s.close();
} catch (IOException e) {
// don't care
}
}
SystemFailure.checkFailure(); // throws
}
// moved this check out of the try. If we are cancelled then we need
// to break out of this while loop.
crHelper.checkCancelInProgress(null); // throws
Socket s = null;
try {
s = serverSock.accept();
crHelper.checkCancelInProgress(null); // throws
// Optionally enable SO_KEEPALIVE in the OS network protocol.
s.setKeepAlive(SocketCreator.ENABLE_TCP_KEEP_ALIVE);
// The synchronization below was added to prevent close from being
// called
// while a ServerConnection is being instantiated. This should prevent
// the
// following exception:
// [severe 2004/12/15 18:49:17.671 PST gemfire2 Server connection from
// balrog.gemstone.com:58478-0x6ce1 nid=0x1334aa] Uncaught exception in
// thread Server connection from balrog.gemstone.com:58478
// java.lang.NullPointerException
// at
// org.apache.geode.internal.cache.tier.sockets.ServerConnection.run(ServerConnection.java:107)
synchronized (this.syncLock) {
if (!isRunning()) {
closeSocket(s);
break;
}
}
this.socketCreator.configureServerSSLSocket(s);
this.loggedAcceptError = false;
handOffNewClientConnection(s);
} catch (InterruptedIOException e) { // Solaris only
closeSocket(s);
if (isRunning()) {
if (logger.isDebugEnabled()) {
logger.debug("Aborted due to interrupt: {}", e);
}
}
} catch (IOException e) {
if (isRunning()) {
if (e instanceof SSLException) {
try {
// Try to send a proper rejection message
ServerHandShakeProcessor.refuse(s.getOutputStream(), e.toString(),
HandShake.REPLY_EXCEPTION_AUTHENTICATION_FAILED);
} catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Bridge server: Unable to write SSL error");
}
}
}
}
closeSocket(s);
if (isRunning()) {
if (!this.loggedAcceptError) {
this.loggedAcceptError = true;
logger.error(LocalizedMessage.create(
LocalizedStrings.AcceptorImpl_CACHE_SERVER_UNEXPECTED_IOEXCEPTION_FROM_ACCEPT, e));
}
// Why sleep?
// try {Thread.sleep(3000);} catch (InterruptedException ie) {}
}
} catch (CancelException e) {
closeSocket(s);
throw e;
} catch (Exception e) {
closeSocket(s);
if (isRunning()) {
logger.fatal(LocalizedMessage
.create(LocalizedStrings.AcceptorImpl_CACHE_SERVER_UNEXPECTED_EXCEPTION, e));
}
}
}
}
/**
* Hand off a new client connection to the thread pool that processes handshakes. If all the
* threads in this pool are busy then the hand off will block until a thread is available. This
* blocking is good because it will throttle the rate at which we create new connections.
*/
private void handOffNewClientConnection(final Socket s) {
try {
this.stats.incAcceptsInProgress();
this.hsPool.execute(new Runnable() {
public void run() {
boolean finished = false;
try {
handleNewClientConnection(s);
finished = true;
} catch (RegionDestroyedException rde) {
// aborted due to disconnect - bug 42273
if (rde.getMessage().indexOf("HARegion") == -1) {
throw rde;
}
} catch (CancelException e) {
// aborted due to shutdown - bug 37318
} catch (java.nio.channels.AsynchronousCloseException expected) {
// this is expected when our TimerTask times out an accepted socket
} catch (IOException | ToDataException ex) { // added ToDataException to fix bug 44659
if (isRunning()) {
if (!AcceptorImpl.this.loggedAcceptError) {
AcceptorImpl.this.loggedAcceptError = true;
if (ex instanceof SocketTimeoutException) {
logger.warn(LocalizedMessage.create(
LocalizedStrings.AcceptorImpl_CACHE_SERVER_FAILED_ACCEPTING_CLIENT_CONNECTION_DUE_TO_SOCKET_TIMEOUT));
} else {
logger.warn(LocalizedMessage.create(
LocalizedStrings.AcceptorImpl_CACHE_SERVER_FAILED_ACCEPTING_CLIENT_CONNECTION__0,
ex), ex);
}
}
}
} finally {
if (!finished) {
closeSocket(s);
}
if (isRunning()) {
AcceptorImpl.this.stats.decAcceptsInProgress();
}
}
}
});
} catch (RejectedExecutionException rejected) {
closeSocket(s);
if (isRunning()) {
this.stats.decAcceptsInProgress();
logger.warn(LocalizedMessage.create(LocalizedStrings.AcceptorImpl_UNEXPECTED, rejected));
}
}
}
public ByteBuffer takeCommBuffer() {
ByteBuffer result = (ByteBuffer) this.commBufferQueue.poll();
if (result == null) {
result = ByteBuffer.allocateDirect(this.socketBufferSize);
}
return result;
}
public void releaseCommBuffer(ByteBuffer bb) {
if (bb == null) { // fix for bug 37107
return;
}
if (isRunning()) {
this.commBufferQueue.offer(bb);
}
}
public void incClientServerCnxCount() {
this.clientServerCnxCount.incrementAndGet();
}
public void decClientServerCnxCount() {
this.clientServerCnxCount.decrementAndGet();
}
public int getClientServerCnxCount() {
return this.clientServerCnxCount.get();
}
protected void handleNewClientConnection(final Socket s) throws IOException {
// Read the first byte. If this socket is being used for 'client to server'
// communication, create a ServerConnection. If this socket is being used
// for 'server to client' communication, send it to the CacheClientNotifier
// for processing.
byte communicationMode;
if (isSelector()) {
ByteBuffer bb = ByteBuffer.allocateDirect(1);
final SocketChannel sc = s.getChannel();
sc.configureBlocking(false);
// try to read the byte first in non-blocking mode
int res = sc.read(bb);
sc.configureBlocking(true);
if (res < 0) {
throw new EOFException();
} else if (res == 0) {
// now do a blocking read so setup a timer to close the socket if the
// the read takes too long
SystemTimer.SystemTimerTask st = new SystemTimer.SystemTimerTask() {
@Override
public void run2() {
logger.warn(LocalizedMessage.create(
LocalizedStrings.AcceptorImpl_CACHE_SERVER_TIMED_OUT_WAITING_FOR_HANDSHAKE_FROM__0,
s.getRemoteSocketAddress()));
closeSocket(s);
}
};
this.hsTimer.schedule(st, this.acceptTimeout);
res = sc.read(bb);
if ((!st.cancel()) || res <= 0) {
throw new EOFException();
}
}
communicationMode = bb.get(0);
if (logger.isTraceEnabled()) {
logger.trace("read communications mode(1) ", communicationMode);
}
} else {
s.setSoTimeout(this.acceptTimeout);
communicationMode = (byte) s.getInputStream().read();
if (logger.isTraceEnabled()) {
logger.trace("read communications mode(2) ", communicationMode);
}
if (communicationMode == -1) {
throw new EOFException();
}
s.setSoTimeout(0);
}
s.setTcpNoDelay(this.tcpNoDelay);
if (communicationMode == CLIENT_TO_SERVER || communicationMode == GATEWAY_TO_GATEWAY
|| communicationMode == MONITOR_TO_SERVER
|| communicationMode == CLIENT_TO_SERVER_FOR_QUEUE) {
String communicationModeStr = "";
switch (communicationMode) {
case CLIENT_TO_SERVER:
communicationModeStr = "client";
break;
case GATEWAY_TO_GATEWAY:
communicationModeStr = "gateway";
break;
case MONITOR_TO_SERVER:
communicationModeStr = "monitor";
break;
case CLIENT_TO_SERVER_FOR_QUEUE:
communicationModeStr = "clientToServerForQueue";
break;
}
if (logger.isDebugEnabled()) {
logger.debug("Bridge server: Initializing {} communication socket: {}",
communicationModeStr, s);
}
if (communicationMode != CLIENT_TO_SERVER_FOR_QUEUE) {
int curCnt = this.getClientServerCnxCount();
if (curCnt >= this.maxConnections) {
logger.warn(LocalizedMessage.create(
LocalizedStrings.AcceptorImpl_REJECTED_CONNECTION_FROM_0_BECAUSE_CURRENT_CONNECTION_COUNT_OF_1_IS_GREATER_THAN_OR_EQUAL_TO_THE_CONFIGURED_MAX_OF_2,
new Object[] {s.getInetAddress(), Integer.valueOf(curCnt),
Integer.valueOf(this.maxConnections)}));
// if (s != null) (cannot be null)
{
try {
ServerHandShakeProcessor.refuse(s.getOutputStream(),
LocalizedStrings.AcceptorImpl_EXCEEDED_MAX_CONNECTIONS_0
.toLocalizedString(Integer.valueOf(this.maxConnections)));
} catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("rejection message failed", ex);
}
}
closeSocket(s);
}
return;
}
}
ServerConnection serverConn = new ServerConnection(s, this.cache, this.crHelper, this.stats,
AcceptorImpl.handShakeTimeout, this.socketBufferSize, communicationModeStr,
communicationMode, this);
synchronized (this.allSCsLock) {
this.allSCs.add(serverConn);
ServerConnection snap[] = this.allSCList; // avoid volatile read
this.allSCList = (ServerConnection[]) ArrayUtils.insert(snap, snap.length, serverConn);
}
if (communicationMode != CLIENT_TO_SERVER_FOR_QUEUE) {
incClientServerCnxCount();
}
if (isSelector()) {
serverConn.registerWithSelector();
} else {
try {
pool.execute(serverConn);
} catch (RejectedExecutionException rejected) {
if (!isRunning()) {
return;
}
logger.warn(LocalizedMessage.create(
LocalizedStrings.AcceptorImpl_REJECTED_CONNECTION_FROM_0_BECAUSE_REQUEST_REJECTED_BY_POOL,
new Object[] {serverConn}));
try {
ServerHandShakeProcessor.refuse(s.getOutputStream(),
LocalizedStrings.AcceptorImpl_EXCEEDED_MAX_CONNECTIONS_0
.toLocalizedString(Integer.valueOf(this.maxConnections)));
} catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("rejection message failed", ex);
}
}
serverConn.cleanup();
}
}
} else if (communicationMode == PRIMARY_SERVER_TO_CLIENT) {
if (logger.isDebugEnabled()) {
logger.debug(
":Bridge server: Initializing primary server-to-client communication socket: {}", s);
}
// try {
AcceptorImpl.this.clientNotifier.registerClient(s, true, this.acceptorId,
this.notifyBySubscription);
} else if (communicationMode == SECONDARY_SERVER_TO_CLIENT) {
if (logger.isDebugEnabled()) {
logger.debug(
":Bridge server: Initializing secondary server-to-client communication socket: {}", s);
}
AcceptorImpl.this.clientNotifier.registerClient(s, false, this.acceptorId,
this.notifyBySubscription);
} else {
throw new IOException("Acceptor received unknown communication mode: " + communicationMode);
}
}
@Override
public boolean isRunning() {
return !this.shutdown;
}
@Override
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "REC_CATCH_EXCEPTION",
justification = "Allow this thread to die")
public void close() {
if (!isRunning()) {
return;
}
try {
synchronized (syncLock) {
this.shutdown = true;
logger.info(LocalizedMessage.create(
LocalizedStrings.AcceptorImpl_CACHE_SERVER_ON_PORT_0_IS_SHUTTING_DOWN, this.localPort));
if (this.thread != null) {
this.thread.interrupt();
}
this.serverSock.close();
crHelper.setShutdown(true); // set this before shutting down the pool
if (isSelector()) {
this.hsTimer.cancel();
if (this.tmpSel != null) {
try {
this.tmpSel.close();
} catch (IOException ignore) {
}
}
try {
wakeupSelector();
this.selector.close();
} catch (IOException ignore) {
}
if (this.selectorThread != null) {
this.selectorThread.interrupt();
}
this.commBufferQueue.clear();
}
ClientHealthMonitor.shutdownInstance();
shutdownSCs();
this.clientNotifier.shutdown(this.acceptorId);
this.pool.shutdown();
if (!this.pool.awaitTermination(PoolImpl.SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS)) {
logger.warn(LocalizedMessage
.create(LocalizedStrings.PoolImpl_TIMEOUT_WAITING_FOR_BACKGROUND_TASKS_TO_COMPLETE));
this.pool.shutdownNow();
}
this.hsPool.shutdownNow();
this.stats.close();
GemFireCacheImpl myCache = (GemFireCacheImpl) cache;
if (!myCache.forcedDisconnect()) {
Set<PartitionedRegion> prs = myCache.getPartitionedRegions();
for (PartitionedRegion pr : prs) {
Map<Integer, BucketAdvisor.BucketProfile> profiles =
new HashMap<Integer, BucketAdvisor.BucketProfile>();
// get all local real bucket advisors
Map<Integer, BucketAdvisor> advisors = pr.getRegionAdvisor().getAllBucketAdvisors();
for (Map.Entry<Integer, BucketAdvisor> entry : advisors.entrySet()) {
BucketAdvisor advisor = entry.getValue();
BucketProfile bp = (BucketProfile) advisor.createProfile();
advisor.updateServerBucketProfile(bp);
profiles.put(entry.getKey(), bp);
}
Set receipients = new HashSet();
receipients = pr.getRegionAdvisor().adviseAllPRNodes();
// send it to all in one messgae
ReplyProcessor21 reply = AllBucketProfilesUpdateMessage.send(receipients,
pr.getDistributionManager(), pr.getPRId(), profiles, true);
if (reply != null) {
reply.waitForRepliesUninterruptibly();
}
if (logger.isDebugEnabled()) {
logger.debug("sending messages to all peers for removing this server..");
}
}
}
} // synchronized
} catch (InterruptedException e) {
if (!this.shutdown) { // GEODE-1103: expect an interrupt during shutdown
logger.warn(LocalizedMessage.create(LocalizedStrings.AcceptorImpl_UNEXPECTED), e);
}
} catch (Exception e) {/* ignore */
logger.warn(LocalizedMessage.create(LocalizedStrings.AcceptorImpl_UNEXPECTED), e);
}
}
private void shutdownSCs() {
// added to fix part 2 of bug 37351.
synchronized (this.allSCsLock) {
ServerConnection snap[] = this.allSCList;
for (int i = 0; i < snap.length; i++) {
snap[i].cleanup();
}
}
}
// protected InetAddress getBindAddress() {
// return this.bindAddress;
// }
// /**
// * Calculates the bind address based on gemfire.properties.
// * Returns null if no bind address is configured.
// * @since GemFire 5.7
// */
// public static InetAddress calcBindAddress(Cache cache) throws IOException {
// InternalDistributedSystem system = (InternalDistributedSystem)cache
// .getDistributedSystem();
// DistributionConfig config = system.getConfig();
// InetAddress address = null;
// // Get the server-bind-address. If it is not null, use it.
// // If it is null, get the bind-address. If it is not null, use it.
// // Otherwise set default.
// String serverBindAddress = config.getServerBindAddress();
// if (serverBindAddress != null && serverBindAddress.length() > 0) {
// address = InetAddress.getByName(serverBindAddress);
// } else {
// String bindAddress = config.getBindAddress();
// if (bindAddress != null && bindAddress.length() > 0) {
// address = InetAddress.getByName(bindAddress);
// }
// }
// return address;
// }
/**
* @param bindName the ip address or host name that this acceptor should bind to. If null or ""
* then calculate it.
* @return the ip address or host name this acceptor will listen on. An "" if all local addresses
* will be listened to.
*
* @since GemFire 5.7
*/
private static String calcBindHostName(Cache cache, String bindName) {
if (bindName != null && !bindName.equals("")) {
return bindName;
}
InternalDistributedSystem system = (InternalDistributedSystem) cache.getDistributedSystem();
DistributionConfig config = system.getConfig();
String hostName = null;
// Get the server-bind-address. If it is not null, use it.
// If it is null, get the bind-address. If it is not null, use it.
// Otherwise set default.
String serverBindAddress = config.getServerBindAddress();
if (serverBindAddress != null && serverBindAddress.length() > 0) {
hostName = serverBindAddress;
} else {
String bindAddress = config.getBindAddress();
if (bindAddress != null && bindAddress.length() > 0) {
hostName = bindAddress;
}
}
return hostName;
}
private InetAddress getBindAddress() throws IOException {
if (this.bindHostName == null || "".equals(this.bindHostName)) {
return null; // pick default local address
} else {
return InetAddress.getByName(this.bindHostName);
}
}
/**
* Gets the address that this bridge server can be contacted on from external processes.
*
* @since GemFire 5.7
*/
public String getExternalAddress() {
String result = this.bindHostName;
boolean needCanonicalHostName = false;
if (result == null || "".equals(result)) {
needCanonicalHostName = true;
} else {
// check to see if we are listening on all local addresses
ServerSocket ss = this.serverSock;
if (ss != null && ss.getLocalSocketAddress() instanceof InetSocketAddress) {
InetSocketAddress isa = (InetSocketAddress) ss.getLocalSocketAddress();
InetAddress ssAddr = isa.getAddress();
if (ssAddr != null) {
if (ssAddr.isAnyLocalAddress()) {
needCanonicalHostName = true;
}
}
}
}
if (needCanonicalHostName) {
try {
result = SocketCreator.getLocalHost().getCanonicalHostName();
} catch (UnknownHostException ex) {
throw new IllegalStateException("getLocalHost failed with " + ex);
}
}
return result;
}
/**
* This method finds a client notifier and returns it. It is used to propagate interest
* registrations to other servers
*
* @return the instance that provides client notification
*/
public CacheClientNotifier getCacheClientNotifier() {
return this.clientNotifier;
}
public CachedRegionHelper getCachedRegionHelper() {
return this.crHelper;
}
public ClientHealthMonitor getClientHealthMonitor() {
return healthMonitor;
}
public ConnectionListener getConnectionListener() {
return connectionListener;
}
public boolean isGatewayReceiver() {
return this.isGatewayReceiver;
}
public List<GatewayTransportFilter> getGatewayTransportFilters() {
return this.gatewayTransportFilters;
}
// IBM J9 sometimes reports "listen failed" instead of BindException
// see bug #40589
public static boolean treatAsBindException(SocketException se) {
if (se instanceof BindException) {
return true;
}
final String msg = se.getMessage();
return (msg != null && msg.contains("Invalid argument: listen failed"));
}
public static boolean isAuthenticationRequired() {
return isAuthenticationRequired;
}
public static boolean isIntegratedSecurity() {
return isIntegratedSecurity;
}
public static boolean isPostAuthzCallbackPresent() {
return isPostAuthzCallbackPresent;
}
public Set<ServerConnection> getAllServerConnections() {
return Collections.unmodifiableSet(allSCs);
}
/**
* This method returns a thread safe structure which can be iterated over without worrying about
* ConcurrentModificationException. JMX MBeans/Commands need to iterate over this list to get
* client info.
*
*/
public ServerConnection[] getAllServerConnectionList() {
return this.allSCList;
}
}