/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions copyright 2011-2013 ForgeRock AS */ package org.opends.server.replication.server; import static org.opends.messages.ReplicationMessages.*; import static org.opends.server.loggers.ErrorLogger.logError; import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; import java.io.IOException; import java.util.ArrayList; import java.util.Random; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.opends.messages.Message; import org.opends.server.admin.std.server.MonitorProviderCfg; import org.opends.server.config.ConfigException; import org.opends.server.core.DirectoryServer; import org.opends.server.replication.common.AssuredMode; import org.opends.server.replication.common.ChangeNumber; import org.opends.server.replication.common.RSInfo; import org.opends.server.replication.common.ServerStatus; import org.opends.server.replication.protocol.AckMsg; import org.opends.server.replication.protocol.ChangeTimeHeartbeatMsg; import org.opends.server.replication.protocol.HeartbeatThread; import org.opends.server.replication.protocol.ProtocolVersion; import org.opends.server.replication.protocol.ReplicationMsg; import org.opends.server.replication.protocol.ResetGenerationIdMsg; import org.opends.server.replication.protocol.RoutableMsg; import org.opends.server.replication.protocol.Session; import org.opends.server.replication.protocol.StartECLSessionMsg; import org.opends.server.replication.protocol.StartMsg; import org.opends.server.replication.protocol.StartSessionMsg; import org.opends.server.replication.protocol.TopologyMsg; import org.opends.server.replication.protocol.UpdateMsg; import org.opends.server.replication.protocol.WindowMsg; import org.opends.server.replication.protocol.WindowProbeMsg; import org.opends.server.types.Attribute; import org.opends.server.types.Attributes; import org.opends.server.types.DirectoryException; import org.opends.server.types.InitializationException; import org.opends.server.types.ResultCode; /** * This class defines a server handler : * - that is a MessageHandler (see this class for more details) * - that handles all interaction with a peer server (RS or DS). */ public abstract class ServerHandler extends MessageHandler { /** * Time during which the server will wait for existing thread to stop * during the shutdownWriter. */ private static final int SHUTDOWN_JOIN_TIMEOUT = 30000; /** * Close the session and log the provided error message * Log nothing if message is null. * @param providedSession The provided closing session. * @param providedMsg The provided error message. * @param handler The handler that manages that session. */ static protected void closeSession(Session providedSession, Message providedMsg, ServerHandler handler) { if (providedMsg != null) { if (debugEnabled()) TRACER.debugInfo("In " + ((handler != null) ? handler.toString() : "Replication Server") + " closing session with err=" + providedMsg.toString()); logError(providedMsg); } if (providedSession != null) { // This method is only called when aborting a failing handshake and // not StopMsg should be sent in such situation. StopMsg are only // expected when full handshake has been performed, or at end of // handshake phase 1, when DS was just gathering available RS info providedSession.close(); } } /** * The serverId of the remote server. */ protected int serverId; /** * The session opened with the remote server. */ protected Session session; /** * The serverURL of the remote server. */ protected String serverURL; /** * Number of updates received from the server in assured safe read mode. */ protected int assuredSrReceivedUpdates = 0; /** * Number of updates received from the server in assured safe read mode that * timed out. */ protected AtomicInteger assuredSrReceivedUpdatesTimeout = new AtomicInteger(); /** * Number of updates sent to the server in assured safe read mode. */ protected int assuredSrSentUpdates = 0; /** * Number of updates sent to the server in assured safe read mode that timed * out. */ protected AtomicInteger assuredSrSentUpdatesTimeout = new AtomicInteger(); /** // Number of updates received from the server in assured safe data mode. */ protected int assuredSdReceivedUpdates = 0; /** * Number of updates received from the server in assured safe data mode that * timed out. */ protected AtomicInteger assuredSdReceivedUpdatesTimeout = new AtomicInteger(); /** * Number of updates sent to the server in assured safe data mode. */ protected int assuredSdSentUpdates = 0; /** * Number of updates sent to the server in assured safe data mode that timed * out. */ protected AtomicInteger assuredSdSentUpdatesTimeout = new AtomicInteger(); /** * The associated ServerWriter that sends messages to the remote server. */ protected ServerWriter writer = null; /** * The associated ServerReader that receives messages from the remote server. */ protected ServerReader reader; // window private int rcvWindow; private int rcvWindowSizeHalf; /** * The size of the receiving window. */ protected int maxRcvWindow; /** * Semaphore that the writer uses to control the flow to the remote server. */ protected Semaphore sendWindow; /** * The initial size of the sending window. */ int sendWindowSize; /** * remote generation id. */ protected long generationId = -1; /** * The generation id of the hosting RS. */ protected long localGenerationId = -1; /** * The generation id before processing a new start handshake. */ protected long oldGenerationId = -1; /** * Group id of this remote server. */ protected byte groupId = (byte) -1; /** * The SSL encryption after the negotiation with the peer. */ protected boolean sslEncryption; /** * The time in milliseconds between heartbeats from the replication * server. Zero means heartbeats are off. */ protected long heartbeatInterval = 0; /** * The thread that will send heartbeats. */ HeartbeatThread heartbeatThread = null; /** * Set when ServerWriter is stopping. */ protected boolean shutdownWriter = false; /** * Set when ServerHandler is stopping. */ private AtomicBoolean shuttingDown = new AtomicBoolean(false); /** * Weight of this remote server. */ protected int weight = 1; /** * Creates a new server handler instance with the provided socket. * * @param session The Session used by the ServerHandler to * communicate with the remote entity. * @param queueSize The maximum number of update that will be kept * in memory by this ServerHandler. * @param replicationServerURL The URL of the hosting replication server. * @param replicationServerId The serverId of the hosting replication server. * @param replicationServer The hosting replication server. * @param rcvWindowSize The window size to receive from the remote server. */ public ServerHandler( Session session, int queueSize, String replicationServerURL, int replicationServerId, ReplicationServer replicationServer, int rcvWindowSize) { super(queueSize, replicationServerURL, replicationServerId, replicationServer); this.session = session; this.rcvWindowSizeHalf = rcvWindowSize / 2; this.maxRcvWindow = rcvWindowSize; this.rcvWindow = rcvWindowSize; } /** * Abort a start procedure currently establishing. * @param reason The provided reason. */ protected void abortStart(Message reason) { // We did not recognize the message, close session as what // can happen after is undetermined and we do not want the server to // be disturbed Session localSession = session; if (localSession != null) { closeSession(localSession, reason, this); } if ((replicationServerDomain != null) && replicationServerDomain.hasLock()) replicationServerDomain.release(); // If generation id of domain was changed, set it back to old value // We may have changed it as it was -1 and we received a value >0 from // peer server and the last topo message sent may have failed being // sent: in that case retrieve old value of generation id for // replication server domain if (oldGenerationId != -100) { if (replicationServerDomain!=null) replicationServerDomain.changeGenerationId(oldGenerationId, false); } } /** * Check the protocol window and send WindowMsg if necessary. * * @throws IOException when the session becomes unavailable. */ public synchronized void checkWindow() throws IOException { if (rcvWindow < rcvWindowSizeHalf) { WindowMsg msg = new WindowMsg(rcvWindowSizeHalf); session.publish(msg); rcvWindow += rcvWindowSizeHalf; } } /** * Decrement the protocol window, then check if it is necessary * to send a WindowMsg and send it. * * @throws IOException when the session becomes unavailable. */ public synchronized void decAndCheckWindow() throws IOException { rcvWindow--; checkWindow(); } /** * Set the shut down flag to true and returns the previous value of the flag. * @return The previous value of the shut down flag */ public boolean engageShutdown() { // Use thread safe boolean return shuttingDown.getAndSet(true); } /** * Returns the shutdown flag. * @return The shutdown flag value. */ public boolean shuttingDown() { return shuttingDown.get(); } /** * Finalize the initialization, create reader, writer, heartbeat system * and monitoring system. * @throws DirectoryException When an exception is raised. */ protected void finalizeStart() throws DirectoryException { // FIXME:ECL We should refactor so that a SH always have a session if (session != null) { try { // Disable timeout for next communications session.setSoTimeout(0); } catch(Exception e) { /* do nothing */ } // sendWindow MUST be created before starting the writer sendWindow = new Semaphore(sendWindowSize); writer = new ServerWriter(session, this, replicationServerDomain); reader = new ServerReader(session, this); session.setName("Replication server RS(" + this.getReplicationServerId() + ") session thread to " + this.toString() + " at " + session.getReadableRemoteAddress()); session.start(); try { session.waitForStartup(); } catch (InterruptedException e) { final Message message = ERR_SESSION_STARTUP_INTERRUPTED.get(session.getName()); throw new DirectoryException(ResultCode.OTHER, message, e); } reader.start(); writer.start(); // Create a thread to send heartbeat messages. if (heartbeatInterval > 0) { String threadName = "Replication server RS(" + this.getReplicationServerId() + ") heartbeat publisher to " + this.toString() + " at " + session.getReadableRemoteAddress(); heartbeatThread = new HeartbeatThread(threadName, session, heartbeatInterval / 3); heartbeatThread.start(); } } DirectoryServer.deregisterMonitorProvider(this); DirectoryServer.registerMonitorProvider(this); } /** * Sends a message. * * @param msg * The message to be sent. * @throws IOException * When it occurs while sending the message, */ public void send(ReplicationMsg msg) throws IOException { /* * Some unit tests include a null domain, so avoid logging anything in that * case. */ if (debugEnabled() && replicationServerDomain != null) { TRACER.debugInfo("In " + replicationServerDomain.getReplicationServer() .getMonitorInstanceName() + this + " publishes message:\n" + msg); } session.publish(msg); } /** * Get the age of the older change that has not yet been replicated * to the server handled by this ServerHandler. * @return The age if the older change has not yet been replicated * to the server handled by this ServerHandler. */ public Long getApproxFirstMissingDate() { Long result = (long) 0; // Get the older CN received ChangeNumber olderUpdateCN = getOlderUpdateCN(); if (olderUpdateCN != null) { // If not present in the local RS db, // then approximate with the older update time result = olderUpdateCN.getTime(); } return result; } /** * Get the number of updates received from the server in assured safe data * mode. * @return The number of updates received from the server in assured safe data * mode */ public int getAssuredSdReceivedUpdates() { return assuredSdReceivedUpdates; } /** * Get the number of updates received from the server in assured safe data * mode that timed out. * @return The number of updates received from the server in assured safe data * mode that timed out. */ public AtomicInteger getAssuredSdReceivedUpdatesTimeout() { return assuredSdReceivedUpdatesTimeout; } /** * Get the number of updates sent to the server in assured safe data mode. * @return The number of updates sent to the server in assured safe data mode */ public int getAssuredSdSentUpdates() { return assuredSdSentUpdates; } /** * Get the number of updates sent to the server in assured safe data mode that * timed out. * @return The number of updates sent to the server in assured safe data mode * that timed out. */ public AtomicInteger getAssuredSdSentUpdatesTimeout() { return assuredSdSentUpdatesTimeout; } /** * Get the number of updates received from the server in assured safe read * mode. * @return The number of updates received from the server in assured safe read * mode */ public int getAssuredSrReceivedUpdates() { return assuredSrReceivedUpdates; } /** * Get the number of updates received from the server in assured safe read * mode that timed out. * @return The number of updates received from the server in assured safe read * mode that timed out. */ public AtomicInteger getAssuredSrReceivedUpdatesTimeout() { return assuredSrReceivedUpdatesTimeout; } /** * Get the number of updates sent to the server in assured safe read mode. * @return The number of updates sent to the server in assured safe read mode */ public int getAssuredSrSentUpdates() { return assuredSrSentUpdates; } /** * Get the number of updates sent to the server in assured safe read mode that * timed out. * @return The number of updates sent to the server in assured safe read mode * that timed out. */ public AtomicInteger getAssuredSrSentUpdatesTimeout() { return assuredSrSentUpdatesTimeout; } /** * Returns the Replication Server Domain to which belongs this server handler. * * @return The replication server domain. */ public ReplicationServerDomain getDomain() { return this.replicationServerDomain; } /** * Returns the value of generationId for that handler. * @return The value of the generationId. */ public long getGenerationId() { return generationId; } /** * Gets the group id of the server represented by this object. * @return The group id of the server represented by this object. */ public byte getGroupId() { return groupId; } /** * Get our heartbeat interval. * @return Our heartbeat interval. */ public long getHeartbeatInterval() { return heartbeatInterval; } /** * Get the count of updates received from the server. * @return the count of update received from the server. */ public int getInCount() { return inCount; } /** * Retrieves a set of attributes containing monitor data that should be * returned to the client if the corresponding monitor entry is requested. * * @return A set of attributes containing monitor data that should be * returned to the client if the corresponding monitor entry is * requested. */ @Override public ArrayList<Attribute> getMonitorData() { // Get the generic ones ArrayList<Attribute> attributes = super.getMonitorData(); attributes.add(Attributes.create("server-id", String.valueOf(serverId))); attributes.add(Attributes.create("domain-name", getServiceId())); // Deprecated attributes.add(Attributes.create("max-waiting-changes", String .valueOf(maxQueueSize))); attributes.add(Attributes.create("sent-updates", String .valueOf(getOutCount()))); attributes.add(Attributes.create("received-updates", String .valueOf(getInCount()))); // Assured counters attributes.add(Attributes.create("assured-sr-received-updates", String .valueOf(getAssuredSrReceivedUpdates()))); attributes.add(Attributes.create("assured-sr-received-updates-timeout", String .valueOf(getAssuredSrReceivedUpdatesTimeout()))); attributes.add(Attributes.create("assured-sr-sent-updates", String .valueOf(getAssuredSrSentUpdates()))); attributes.add(Attributes.create("assured-sr-sent-updates-timeout", String .valueOf(getAssuredSrSentUpdatesTimeout()))); attributes.add(Attributes.create("assured-sd-received-updates", String .valueOf(getAssuredSdReceivedUpdates()))); if (!isDataServer()) { attributes.add(Attributes.create("assured-sd-sent-updates", String.valueOf(getAssuredSdSentUpdates()))); attributes.add(Attributes.create("assured-sd-sent-updates-timeout", String.valueOf(getAssuredSdSentUpdatesTimeout()))); } else { attributes.add(Attributes.create("assured-sd-received-updates-timeout", String.valueOf(getAssuredSdReceivedUpdatesTimeout()))); } // Window stats attributes.add(Attributes.create("max-send-window", String .valueOf(sendWindowSize))); attributes.add(Attributes.create("current-send-window", String .valueOf(sendWindow.availablePermits()))); attributes.add(Attributes.create("max-rcv-window", String .valueOf(maxRcvWindow))); attributes.add(Attributes.create("current-rcv-window", String .valueOf(rcvWindow))); // Encryption attributes.add(Attributes.create("ssl-encryption", String .valueOf(session.isEncrypted()))); // Data generation attributes.add(Attributes.create("generation-id", String .valueOf(generationId))); return attributes; } /** * Retrieves the name of this monitor provider. It should be unique among all * monitor providers, including all instances of the same monitor provider. * * @return The name of this monitor provider. */ @Override public abstract String getMonitorInstanceName(); /** * Get the count of updates sent to this server. * @return The count of update sent to this server. */ public int getOutCount() { return outCount; } /** * Gets the protocol version used with this remote server. * @return The protocol version used with this remote server. */ public short getProtocolVersion() { return session.getProtocolVersion(); } /** * get the Server Id. * * @return the ID of the server to which this object is linked */ public int getServerId() { return serverId; } /** * Retrieves the URL for this server handler. * * @return The URL for this server handler, in the form of an address and * port separated by a colon. */ public String getServerURL() { return serverURL; } /** * Return the ServerStatus. * @return The server status. */ protected abstract ServerStatus getStatus(); /** * Increment the number of updates received from the server in assured safe * data mode. */ public void incrementAssuredSdReceivedUpdates() { assuredSdReceivedUpdates++; } /** * Increment the number of updates received from the server in assured safe * data mode that timed out. */ public void incrementAssuredSdReceivedUpdatesTimeout() { assuredSdReceivedUpdatesTimeout.incrementAndGet(); } /** * Increment the number of updates sent to the server in assured safe data * mode. */ public void incrementAssuredSdSentUpdates() { assuredSdSentUpdates++; } /** * Increment the number of updates sent to the server in assured safe data * mode that timed out. */ public void incrementAssuredSdSentUpdatesTimeout() { assuredSdSentUpdatesTimeout.incrementAndGet(); } /** * Increment the number of updates received from the server in assured safe * read mode. */ public void incrementAssuredSrReceivedUpdates() { assuredSrReceivedUpdates++; } /** * Increment the number of updates received from the server in assured safe * read mode that timed out. */ public void incrementAssuredSrReceivedUpdatesTimeout() { assuredSrReceivedUpdatesTimeout.incrementAndGet(); } /** * Increment the number of updates sent to the server in assured safe read * mode. */ public void incrementAssuredSrSentUpdates() { assuredSrSentUpdates++; } /** * Increment the number of updates sent to the server in assured safe read * mode that timed out. */ public void incrementAssuredSrSentUpdatesTimeout() { assuredSrSentUpdatesTimeout.incrementAndGet(); } /** * Increase the counter of update received from the server. */ public void incrementInCount() { inCount++; } /** * Increase the counter of updates sent to the server. */ public void incrementOutCount() { outCount++; } /** * {@inheritDoc} */ @Override public void initializeMonitorProvider(MonitorProviderCfg configuration) throws ConfigException, InitializationException { // Nothing to do for now } /** * Check if the server associated to this ServerHandler is a data server * in the topology. * @return true if the server is a data server. */ public abstract boolean isDataServer(); /** * Check if the server associated to this ServerHandler is a replication * server. * @return true if the server is a replication server. */ public boolean isReplicationServer() { return (!this.isDataServer()); } /** * Lock the domain potentially with a timeout. * * @param timedout * The provided timeout. * @throws DirectoryException * When an exception occurs. * @throws InterruptedException * If the current thread was interrupted while waiting for the lock. */ protected void lockDomain(boolean timedout) throws DirectoryException, InterruptedException { // The handshake phase must be done by blocking any access to structures // keeping info on connected servers, so that one can safely check for // pre-existence of a server, send a coherent snapshot of known topology // to peers, update the local view of the topology... // // For instance a kind of problem could be that while we connect with a // peer RS, a DS is connecting at the same time and we could publish the // connected DSs to the peer RS forgetting this last DS in the TopologyMsg. // // This method and every others that need to read/make changes to the // structures holding topology for the domain should: // - call ReplicationServerDomain.lock() // - read/modify structures // - call ReplicationServerDomain.release() // // More information is provided in comment of ReplicationServerDomain.lock() // If domain already exists, lock it until handshake is finished otherwise // it will be created and locked later in the method if (!timedout) { // !timedout if (!replicationServerDomain.hasLock()) replicationServerDomain.lock(); } else { // timedout /** * Take the lock on the domain. * WARNING: Here we try to acquire the lock with a timeout. This * is for preventing a deadlock that may happen if there are cross * connection attempts (for same domain) from this replication * server and from a peer one: * Here is the scenario: * - RS1 connect thread takes the domain lock and starts * connection to RS2 * - at the same time RS2 connect thread takes his domain lock and * start connection to RS2 * - RS2 listen thread starts processing received * ReplServerStartMsg from RS1 and wants to acquire the lock on * the domain (here) but cannot as RS2 connect thread already has * it * - RS1 listen thread starts processing received * ReplServerStartMsg from RS2 and wants to acquire the lock on * the domain (here) but cannot as RS1 connect thread already has * it * => Deadlock: 4 threads are locked. * So to prevent that in such situation, the listen threads here * will both timeout trying to acquire the lock. The random time * for the timeout should allow on connection attempt to be * aborted whereas the other one should have time to finish in the * same time. * Warning: the minimum time (3s) should be big enough to allow * normal situation connections to terminate. The added random * time should represent a big enough range so that the chance to * have one listen thread timing out a lot before the peer one is * great. When the first listen thread times out, the remote * connect thread should release the lock and allow the peer * listen thread to take the lock it was waiting for and process * the connection attempt. */ Random random = new Random(); int randomTime = random.nextInt(6); // Random from 0 to 5 // Wait at least 3 seconds + (0 to 5 seconds) long timeout = (long) (3000 + ( randomTime * 1000 ) ); boolean noTimeout = replicationServerDomain.tryLock(timeout); if (!noTimeout) { // Timeout Message message = WARN_TIMEOUT_WHEN_CROSS_CONNECTION.get( getServiceId(), serverId, session.getReadableRemoteAddress(), replicationServerId); throw new DirectoryException(ResultCode.OTHER, message); } } } /** * Processes a routable message. * * @param msg The message to be processed. */ public void process(RoutableMsg msg) { if (debugEnabled()) TRACER.debugInfo("In " + replicationServerDomain.getReplicationServer(). getMonitorInstanceName() + this + " processes routable msg received:" + msg); replicationServerDomain.process(msg, this); } /** * Processes a change time heartbeat msg. * * @param msg The message to be processed. */ public void process(ChangeTimeHeartbeatMsg msg) { if (debugEnabled()) TRACER.debugInfo("In " + replicationServerDomain.getReplicationServer(). getMonitorInstanceName() + this + " processes received msg:\n" + msg); replicationServerDomain.processChangeTimeHeartbeatMsg(this, msg); } /** * Process the reception of a WindowProbeMsg message. * * @param windowProbeMsg The message to process. * * @throws IOException When the session becomes unavailable. */ public void process(WindowProbeMsg windowProbeMsg) throws IOException { if (rcvWindow > 0) { // The LDAP server believes that its window is closed // while it is not, this means that some problem happened in the // window exchange procedure ! // lets update the LDAP server with out current window size and hope // that everything will work better in the futur. // TODO also log an error message. WindowMsg msg = new WindowMsg(rcvWindow); session.publish(msg); } else { // Both the LDAP server and the replication server believes that the // window is closed. Lets check the flowcontrol in case we // can now resume operations and send a windowMessage if necessary. checkWindow(); } } /** * Sends the provided TopologyMsg to the peer server. * * @param topoMsg * The TopologyMsg message to be sent. * @throws IOException * When it occurs while sending the message, */ public void sendTopoInfo(TopologyMsg topoMsg) throws IOException { // V1 Rs do not support the TopologyMsg if (getProtocolVersion() > ProtocolVersion.REPLICATION_PROTOCOL_V1) { send(topoMsg); } } /** * Set a new generation ID. * * @param generationId The new generation ID * */ public void setGenerationId(long generationId) { this.generationId = generationId; } /** * Sets the replication server domain associated. * @param rsd The provided replication server domain. */ protected void setReplicationServerDomain(ReplicationServerDomain rsd) { this.replicationServerDomain = rsd; } /** * Sets the window size when used when sending to the remote. * @param size The provided window size. */ protected void setSendWindowSize(int size) { this.sendWindowSize = size; } /** * Requests to shutdown the writer. */ protected void shutdownWriter() { shutdownWriter = true; } /** * Shutdown This ServerHandler. */ public void shutdown() { shutdownWriter(); setConsumerActive(false); super.shutdown(); if (session != null) { session.close(); } /* * Stop the heartbeat thread. */ if (heartbeatThread != null) { heartbeatThread.shutdown(); } DirectoryServer.deregisterMonitorProvider(this); /* * Be sure to wait for ServerWriter and ServerReader death * It does not matter if we try to stop a thread which is us (reader * or writer), but we must not wait for our own thread death. */ try { if ((writer != null) && (!(Thread.currentThread().equals(writer)))) { writer.join(SHUTDOWN_JOIN_TIMEOUT); } if ((reader != null) && (!(Thread.currentThread().equals(reader)))) { reader.join(SHUTDOWN_JOIN_TIMEOUT); } } catch (InterruptedException e) { // don't try anymore to join and return. } if (debugEnabled()) TRACER.debugInfo("SH.shutdowned(" + this + ")"); } /** * Select the next update that must be sent to the server managed by this * ServerHandler. * * @return the next update that must be sent to the server managed by this * ServerHandler. */ public UpdateMsg take() { boolean interrupted = true; UpdateMsg msg = getNextMessage(true); // synchronous:block until msg boolean acquired = false; do { try { acquired = sendWindow.tryAcquire((long) 500, TimeUnit.MILLISECONDS); interrupted = false; } catch (InterruptedException e) { // loop until not interrupted } } while (((interrupted) || (!acquired)) && (!shutdownWriter)); if (msg != null) { incrementOutCount(); if (msg.isAssured()) { if (msg.getAssuredMode() == AssuredMode.SAFE_READ_MODE) { incrementAssuredSrSentUpdates(); } else { if (!isDataServer()) incrementAssuredSdSentUpdates(); } } } return msg; } /** * Creates a RSInfo structure representing this remote RS. * @return The RSInfo structure representing this remote RS */ public RSInfo toRSInfo() { return new RSInfo(serverId, serverURL, generationId, groupId, weight); } /** * Starts the monitoring publisher for the domain if not already started. */ protected void createMonitoringPublisher() { if (!replicationServerDomain.isRunningMonitoringPublisher()) { replicationServerDomain.startMonitoringPublisher(); } } /** * Update the send window size based on the credit specified in the * given window message. * * @param windowMsg The Window Message containing the information * necessary for updating the window size. */ public void updateWindow(WindowMsg windowMsg) { sendWindow.release(windowMsg.getNumAck()); } /** * Log the messages involved in the start handshake. * @param inStartMsg The message received first. * @param outStartMsg The message sent in response. */ protected void logStartHandshakeRCVandSND( StartMsg inStartMsg, StartMsg outStartMsg) { if (debugEnabled()) { TRACER.debugInfo("In " + this.replicationServer.getMonitorInstanceName() + ", " + this.getClass().getSimpleName() + " " + this + ":" + "\nSH START HANDSHAKE RECEIVED:\n" + inStartMsg.toString()+ "\nAND REPLIED:\n" + outStartMsg.toString()); } } /** * Log the messages involved in the start handshake. * @param outStartMsg The message sent first. * @param inStartMsg The message received in response. */ protected void logStartHandshakeSNDandRCV( StartMsg outStartMsg, StartMsg inStartMsg) { if (debugEnabled()) { TRACER.debugInfo("In " + this.replicationServer.getMonitorInstanceName() + ", " + this.getClass().getSimpleName() + " " + this + ":" + "\nSH START HANDSHAKE SENT("+ this + "):\n" + outStartMsg.toString()+ "\nAND RECEIVED:\n" + inStartMsg.toString()); } } /** * Log the messages involved in the Topology handshake. * @param inTopoMsg The message received first. * @param outTopoMsg The message sent in response. */ protected void logTopoHandshakeRCVandSND( TopologyMsg inTopoMsg, TopologyMsg outTopoMsg) { if (debugEnabled()) { TRACER.debugInfo("In " + this.replicationServer.getMonitorInstanceName() + ", " + this.getClass().getSimpleName() + " " + this + ":" + "\nSH TOPO HANDSHAKE RECEIVED:\n" + inTopoMsg.toString() + "\nAND REPLIED:\n" + outTopoMsg.toString()); } } /** * Log the messages involved in the Topology handshake. * @param outTopoMsg The message sent first. * @param inTopoMsg The message received in response. */ protected void logTopoHandshakeSNDandRCV( TopologyMsg outTopoMsg, TopologyMsg inTopoMsg) { if (debugEnabled()) { TRACER.debugInfo("In " + this.replicationServer.getMonitorInstanceName() + ", " + this.getClass().getSimpleName() + " " + this + ":" + "\nSH TOPO HANDSHAKE SENT:\n" + outTopoMsg.toString() + "\nAND RECEIVED:\n" + inTopoMsg.toString()); } } /** * Log the messages involved in the Topology/StartSession handshake. * @param inStartSessionMsg The message received first. * @param outTopoMsg The message sent in response. */ protected void logStartSessionHandshake( StartSessionMsg inStartSessionMsg, TopologyMsg outTopoMsg) { if (debugEnabled()) { TRACER.debugInfo("In " + this.replicationServer.getMonitorInstanceName() + ", " + this.getClass().getSimpleName() + " " + this + " :" + "\nSH SESSION HANDSHAKE RECEIVED:\n" + inStartSessionMsg.toString() + "\nAND REPLIED:\n" + outTopoMsg.toString()); } } /** * Log stop message has been received. */ protected void logStopReceived() { if (debugEnabled()) { TRACER.debugInfo("In " + this.replicationServer.getMonitorInstanceName() + ", " + this.getClass().getSimpleName() + " " + this + " :" + "\nSH SESSION HANDSHAKE RECEIVED A STOP MESSAGE"); } } /** * Log the messages involved in the Topology/StartSession handshake. * @param inStartECLSessionMsg The message received first. */ protected void logStartECLSessionHandshake( StartECLSessionMsg inStartECLSessionMsg) { if (debugEnabled()) { TRACER.debugInfo("In " + this.replicationServer.getMonitorInstanceName() + ", " + this.getClass().getSimpleName() + " " + this + " :" + "\nSH SESSION HANDSHAKE RECEIVED:\n" + inStartECLSessionMsg.toString()); } } /** * Process a Ack message received. * @param ack the message received. */ public void processAck(AckMsg ack) { if (replicationServerDomain!=null) replicationServerDomain.processAck(ack, this); } /** * Get the reference generation id (associated with the changes in the db). * @return the reference generation id. */ public long getReferenceGenId() { long refgenid = -1; if (replicationServerDomain!=null) refgenid = replicationServerDomain.getGenerationId(); return refgenid; } /** * Process a ResetGenerationIdMsg message received. * @param msg the message received. */ public void processResetGenId(ResetGenerationIdMsg msg) { if (replicationServerDomain!=null) replicationServerDomain.resetGenerationId(this, msg); } /** * Put a new update message received. * @param update the update message received. * @throws IOException when it occurs. */ public void put(UpdateMsg update) throws IOException { if (replicationServerDomain!=null) replicationServerDomain.put(update, this); } /** * Stop this handler. */ public void doStop() { if (replicationServerDomain!=null) replicationServerDomain.stopServer(this, false); } }