All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.geode.distributed.internal.direct.DirectChannel Maven / Gradle / Ivy

Go to download

Apache Geode provides a database-like consistency model, reliable transaction processing and a shared-nothing architecture to maintain very low latency performance with high concurrency processing

There is a newer version: 1.15.1
Show newest version
/*
 * 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.distributed.internal.direct;

import org.apache.geode.*;
import org.apache.geode.cache.TimeoutException;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.DistributedSystemDisconnectedException;
import org.apache.geode.distributed.internal.*;
import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
import org.apache.geode.distributed.internal.membership.MembershipManager;
import org.apache.geode.i18n.StringId;
import org.apache.geode.internal.net.SocketCreator;
import org.apache.geode.internal.cache.DirectReplyMessage;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.logging.log4j.AlertAppender;
import org.apache.geode.internal.logging.log4j.LocalizedMessage;
import org.apache.geode.internal.logging.log4j.LogMarker;
import org.apache.geode.internal.tcp.*;
import org.apache.geode.internal.util.Breadcrumbs;
import org.apache.geode.internal.util.concurrent.ReentrantSemaphore;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.io.NotSerializableException;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.util.*;
import java.util.concurrent.Semaphore;

/**
 * DirectChannel is used to interact directly with other Direct servers to distribute GemFire
 * messages to other nodes. It is held by a
 * org.apache.geode.internal.cache.distribution.DistributionChannel, which is used by the
 * DistributionManager to send and receive asynchronous messages.
 */
public class DirectChannel {

  private static final Logger logger = LogService.getLogger();

  /** this is the conduit used for communications */
  private final transient TCPConduit conduit;

  private volatile boolean disconnected = true;

  /** This is set to true when completely disconnected (all connections are closed) */
  private volatile boolean disconnectCompleted = true;

  /** this is the DistributionManager, most of the time */
  private final DirectChannelListener receiver;

  private final InetAddress address;

  InternalDistributedMember localAddr;

  /**
   * Callback to set the local address, must be done before this channel is used.
   * 
   * @param localAddr
   * @throws ConnectionException if the conduit has stopped
   */
  public void setLocalAddr(InternalDistributedMember localAddr) {
    this.localAddr = localAddr;
    conduit.setLocalAddr(localAddr);
    if (disconnected) {
      disconnected = false;
      disconnectCompleted = false;
      this.groupOrderedSenderSem = new ReentrantSemaphore(MAX_GROUP_SENDERS);
      this.groupUnorderedSenderSem = new ReentrantSemaphore(MAX_GROUP_SENDERS);
    }
  }

  /**
   * when the initial number of members is known, this method is invoked to ensure that connections
   * to those members can be established in a reasonable amount of time. See bug 39848
   * 
   * @param numberOfMembers
   */
  public void setMembershipSize(int numberOfMembers) {
    conduit.setMaximumHandshakePoolSize(numberOfMembers);
  }

  /**
   * Returns the cancel criterion for the channel, which will note if the channel is abnormally
   * closing
   */
  public CancelCriterion getCancelCriterion() {
    return conduit.getCancelCriterion();
  }

  public DirectChannel(MembershipManager mgr, DirectChannelListener listener, DistributionConfig dc)
      throws ConnectionException {
    this.receiver = listener;

    this.address = initAddress(dc);
    boolean isBindAddress = dc.getBindAddress() != null;
    try {
      int port = Integer.getInteger("tcpServerPort", 0).intValue();
      if (port == 0) {
        port = dc.getTcpPort();
      }
      Properties props = System.getProperties();
      if (props.getProperty("p2p.shareSockets") == null) {
        props.setProperty("p2p.shareSockets", String.valueOf(dc.getConserveSockets()));
      }
      if (dc.getSocketBufferSize() != DistributionConfig.DEFAULT_SOCKET_BUFFER_SIZE) {
        // Note that the system property "p2p.tcpBufferSize" will be
        // overridden by the new "socket-buffer-size".
        props.setProperty("p2p.tcpBufferSize", String.valueOf(dc.getSocketBufferSize()));
      }
      if (props.getProperty("p2p.idleConnectionTimeout") == null) {
        props.setProperty("p2p.idleConnectionTimeout", String.valueOf(dc.getSocketLeaseTime()));
      }
      int[] range = dc.getMembershipPortRange();
      props.setProperty("membership_port_range_start", "" + range[0]);
      props.setProperty("membership_port_range_end", "" + range[1]);

      this.conduit = new TCPConduit(mgr, port, address, isBindAddress, this, props);
      disconnected = false;
      disconnectCompleted = false;
      this.groupOrderedSenderSem = new ReentrantSemaphore(MAX_GROUP_SENDERS);
      this.groupUnorderedSenderSem = new ReentrantSemaphore(MAX_GROUP_SENDERS);
      logger.info(LocalizedMessage.create(
          LocalizedStrings.DirectChannel_GEMFIRE_P2P_LISTENER_STARTED_ON__0, conduit.getAddress()));

    } catch (ConnectionException ce) {
      logger.fatal(LocalizedMessage.create(
          LocalizedStrings.DirectChannel_UNABLE_TO_INITIALIZE_DIRECT_CHANNEL_BECAUSE__0,
          new Object[] {ce.getMessage()}), ce);
      throw ce; // fix for bug 31973
    }
  }


  /**
   * Return how many concurrent operations should be allowed by default. since 6.6, this has been
   * raised to Integer.MAX value from the number of available processors. Setting this to a lower
   * value raises the possibility of a deadlock when serializing a message with PDX objects, because
   * the PDX serialization can trigger further distribution.
   */
  static public final int DEFAULT_CONCURRENCY_LEVEL =
      Integer.getInteger("p2p.defaultConcurrencyLevel", Integer.MAX_VALUE / 2).intValue();

  /**
   * The maximum number of concurrent senders sending a message to a group of recipients.
   */
  static private final int MAX_GROUP_SENDERS =
      Integer.getInteger("p2p.maxGroupSenders", DEFAULT_CONCURRENCY_LEVEL).intValue();
  private Semaphore groupUnorderedSenderSem;
  private Semaphore groupOrderedSenderSem;

  private Semaphore getGroupSem(boolean ordered) {
    if (ordered) {
      return this.groupOrderedSenderSem;
    } else {
      return this.groupUnorderedSenderSem;
    }
  }

  private void acquireGroupSendPermission(boolean ordered) {
    if (this.disconnected) {
      throw new org.apache.geode.distributed.DistributedSystemDisconnectedException(
          LocalizedStrings.DirectChannel_DIRECT_CHANNEL_HAS_BEEN_STOPPED.toLocalizedString());
    }
    // @todo darrel: add some stats
    final Semaphore s = getGroupSem(ordered);
    for (;;) {
      this.conduit.getCancelCriterion().checkCancelInProgress(null);
      boolean interrupted = Thread.interrupted();
      try {
        s.acquire();
        break;
      } catch (InterruptedException ex) {
        interrupted = true;
      } finally {
        if (interrupted) {
          Thread.currentThread().interrupt();
        }
      }
    } // for
    if (this.disconnected) {
      s.release();
      throw new DistributedSystemDisconnectedException(
          LocalizedStrings.DirectChannel_COMMUNICATIONS_DISCONNECTED.toLocalizedString());
    }
  }

  private void releaseGroupSendPermission(boolean ordered) {
    final Semaphore s = getGroupSem(ordered);
    s.release();
  }

  /**
   * Returns true if calling thread owns its own communication resources.
   */
  boolean threadOwnsResources() {
    DM d = getDM();
    if (d != null) {
      return d.getSystem().threadOwnsResources() && !AlertAppender.isThreadAlerting();
    }
    return false;

  }

  /**
   * This is basically just sendToMany, giving us a way to see on the stack whether we are sending
   * to a single member or multiple members, in which case the group-send lock will be held during
   * distribution.
   * 
   * @param mgr - the membership manager
   * @param p_destinations - the list of addresses to send the message to.
   * @param msg - the message to send
   * @param ackWaitThreshold
   * @param ackSAThreshold the severe alert threshold
   * @return number of bytes sent
   * @throws ConnectExceptions if message could not be send to its destination
   * @throws NotSerializableException If the msg cannot be serialized
   */
  private final int sendToOne(final MembershipManager mgr,
      InternalDistributedMember[] p_destinations, final DistributionMessage msg,
      long ackWaitThreshold, long ackSAThreshold)
      throws ConnectExceptions, NotSerializableException {
    return sendToMany(mgr, p_destinations, msg, ackWaitThreshold, ackSAThreshold);
  }


  /**
   * Sends a msg to a list of destinations. This code does some special optimizations to stream
   * large messages
   * 
   * @param mgr - the membership manager
   * @param p_destinations - the list of addresses to send the message to.
   * @param msg - the message to send
   * @param ackWaitThreshold
   * @param ackSAThreshold the severe alert threshold
   * @return number of bytes sent
   * @throws ConnectExceptions if message could not be send to its destination
   * @throws NotSerializableException If the msg cannot be serialized
   */
  private int sendToMany(final MembershipManager mgr, InternalDistributedMember[] p_destinations,
      final DistributionMessage msg, long ackWaitThreshold, long ackSAThreshold)
      throws ConnectExceptions, NotSerializableException {
    InternalDistributedMember destinations[] = p_destinations;

    // Collects connect exceptions that happened during previous attempts to send.
    // These represent members we are not able to distribute to.
    ConnectExceptions failedCe = null;
    // Describes the destinations that we need to retry the send to.
    ConnectExceptions retryInfo = null;
    int bytesWritten = 0;
    boolean retry = false;
    final boolean orderedMsg = msg.orderedDelivery() || Connection.isDominoThread();
    // Connections we actually sent messages to.
    final List totalSentCons = new ArrayList(destinations.length);
    boolean interrupted = false;

    long ackTimeout = 0;
    long ackSDTimeout = 0;
    long startTime = 0;
    final DirectReplyMessage directMsg;
    if (msg instanceof DirectReplyMessage) {
      directMsg = (DirectReplyMessage) msg;
    } else {
      directMsg = null;
    }
    if (directMsg != null || msg.getProcessorId() > 0) {
      ackTimeout = (int) (ackWaitThreshold * 1000);
      if (msg.isSevereAlertCompatible() || ReplyProcessor21.isSevereAlertProcessingForced()) {
        ackSDTimeout = (int) (ackSAThreshold * 1000);
        if (ReplyProcessor21.getShortSevereAlertProcessing()) {
          ackSDTimeout = (int) (ReplyProcessor21.PR_SEVERE_ALERT_RATIO * ackSDTimeout);
        }
      }
    }

    boolean directReply = false;
    if (directMsg != null && directMsg.supportsDirectAck() && threadOwnsResources()) {
      directReply = true;
    }

    // If this is a direct reply message, but we are sending it
    // over the shared socket, tell the message it needs to
    // use a regular reply processor.
    if (!directReply && directMsg != null) {
      directMsg.registerProcessor();
    }

    try {
      do {
        interrupted = interrupted || Thread.interrupted();
        /**
         * Exceptions that happened during one attempt to send
         */
        if (retryInfo != null) {
          // need to retry to each of the guys in the exception
          List retryMembers = retryInfo.getMembers();
          InternalDistributedMember[] retryDest =
              new InternalDistributedMember[retryMembers.size()];
          retryDest = (InternalDistributedMember[]) retryMembers.toArray(retryDest);
          destinations = retryDest;
          retryInfo = null;
          retry = true;
        }
        final List cons = new ArrayList(destinations.length);
        ConnectExceptions ce = getConnections(mgr, msg, destinations, orderedMsg, retry, ackTimeout,
            ackSDTimeout, cons);
        if (directReply && msg.getProcessorId() > 0) { // no longer a direct-reply message?
          directReply = false;
        }
        if (ce != null) {
          if (failedCe != null) {
            failedCe.getMembers().addAll(ce.getMembers());
            failedCe.getCauses().addAll(ce.getCauses());
          } else {
            failedCe = ce;
          }
          ce = null;
        }
        if (cons.isEmpty()) {
          if (failedCe != null) {
            throw failedCe;
          }
          return bytesWritten;
        }

        boolean sendingToGroup = cons.size() > 1;
        Connection permissionCon = null;
        if (sendingToGroup) {
          acquireGroupSendPermission(orderedMsg);
        } else {
          // sending to just one guy
          permissionCon = (Connection) cons.get(0);
          if (permissionCon != null) {
            try {
              permissionCon.acquireSendPermission();
            } catch (ConnectionException conEx) {
              // Set retryInfo and then retry.
              // We want to keep calling TCPConduit.getConnection until it doesn't
              // return a connection.
              retryInfo = new ConnectExceptions();
              retryInfo.addFailure(permissionCon.getRemoteAddress(), conEx);
              continue;
            }
          }
        }

        try {
          if (logger.isDebugEnabled()) {
            logger.debug("{}{}) to {} peers ({}) via tcp/ip",
                (retry ? "Retrying send (" : "Sending ("), msg, cons.size(), cons);
          }
          DMStats stats = getDMStats();
          List sentCons; // used for cons we sent to this time

          final BaseMsgStreamer ms = MsgStreamer.create(cons, msg, directReply, stats);
          try {
            startTime = 0;
            if (ackTimeout > 0) {
              startTime = System.currentTimeMillis();
            }
            ms.reserveConnections(startTime, ackTimeout, ackSDTimeout);

            int result = ms.writeMessage();
            if (bytesWritten == 0) {
              // bytesWritten only needs to be set once.
              // if we have to do a retry we don't want to count
              // each one's bytes.
              bytesWritten = result;
            }
            ce = ms.getConnectExceptions();
            sentCons = ms.getSentConnections();

            totalSentCons.addAll(sentCons);
          } catch (NotSerializableException e) {
            throw e;
          } catch (ToDataException e) {
            throw e;
          } catch (IOException ex) {
            throw new InternalGemFireException(
                LocalizedStrings.DirectChannel_UNKNOWN_ERROR_SERIALIZING_MESSAGE
                    .toLocalizedString(),
                ex);
          } finally {
            try {
              ms.close();
            } catch (IOException e) {
              throw new InternalGemFireException("Unknown error serializing message", e);
            }
          }

          if (ce != null) {
            retryInfo = ce;
            ce = null;
          }

          if (directReply && !sentCons.isEmpty()) {
            long readAckStart = 0;
            if (stats != null) {
              readAckStart = stats.startReplyWait();
            }
            try {
              ce = readAcks(sentCons, startTime, ackTimeout, ackSDTimeout, ce,
                  directMsg.getDirectReplyProcessor());
            } finally {
              if (stats != null) {
                stats.endReplyWait(readAckStart, startTime);
              }
            }
          }
        } finally {
          if (sendingToGroup) {
            releaseGroupSendPermission(orderedMsg);
          } else if (permissionCon != null) {
            permissionCon.releaseSendPermission();
          }
        }
        if (ce != null) {
          if (retryInfo != null) {
            retryInfo.getMembers().addAll(ce.getMembers());
            retryInfo.getCauses().addAll(ce.getCauses());
          } else {
            retryInfo = ce;
          }
          ce = null;
        }
        if (retryInfo != null) {
          this.conduit.getCancelCriterion().checkCancelInProgress(null);
        }
      } while (retryInfo != null);
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
      for (Iterator it = totalSentCons.iterator(); it.hasNext();) {
        Connection con = (Connection) it.next();
        con.setInUse(false, 0, 0, 0, null);
      }
    }
    if (failedCe != null) {
      throw failedCe;
    }
    return bytesWritten;
  }

  private ConnectExceptions readAcks(List sentCons, long startTime, long ackTimeout,
      long ackSDTimeout, ConnectExceptions cumulativeExceptions, DirectReplyProcessor processor) {

    ConnectExceptions ce = cumulativeExceptions;

    for (Iterator it = sentCons.iterator(); it.hasNext();) {
      Connection con = (Connection) it.next();
      // We don't expect replies on shared connections.
      if (con.isSharedResource()) {
        continue;
      }
      int msToWait = (int) (ackTimeout - (System.currentTimeMillis() - startTime));
      // if the wait threshold has already been reached during transmission
      // of the message, set a small wait period just to make sure the
      // acks haven't already come back
      if (msToWait <= 0) {
        msToWait = 10;
      }
      long msInterval = ackSDTimeout;
      if (msInterval <= 0) {
        msInterval = Math.max(ackTimeout, 1000);
      }
      try {
        try {
          con.readAck(msToWait, msInterval, processor);
        } catch (SocketTimeoutException ex) {
          handleAckTimeout(ackTimeout, ackSDTimeout, con, processor);
        }
      } catch (ConnectionException conEx) {
        if (ce == null) {
          ce = new ConnectExceptions();
        }
        ce.addFailure(con.getRemoteAddress(), conEx);
      }
    }
    return ce;
  }

  /**
   * Obtain the connections needed to transmit a message. The connections are put into the cons
   * object (the last parameter)
   * 
   * @param mgr the membership manager
   * @param msg the message to send
   * @param destinations who to send the message to
   * @param preserveOrder true if the msg should ordered
   * @param retry whether this is a retransmission
   * @param ackTimeout the ack warning timeout
   * @param ackSDTimeout the ack severe alert timeout
   * @param cons a list to hold the connections
   * @return null if everything went okay, or a ConnectExceptions object if some connections
   *         couldn't be obtained
   */
  private ConnectExceptions getConnections(MembershipManager mgr, DistributionMessage msg,
      InternalDistributedMember[] destinations, boolean preserveOrder, boolean retry,
      long ackTimeout, long ackSDTimeout, List cons) {
    ConnectExceptions ce = null;
    for (int i = 0; i < destinations.length; i++) {
      InternalDistributedMember destination = destinations[i];
      if (destination == null) {
        continue;
      }
      if (localAddr.equals(destination)) {
        // jgroups does not deliver messages to a sender, so we don't support
        // it here either.
        continue;
      }

      if (!mgr.memberExists(destination) || mgr.shutdownInProgress()
          || mgr.isShunned(destination)) {
        // This should only happen if the member is no longer in the view.
        if (logger.isTraceEnabled(LogMarker.DM)) {
          logger.trace(LogMarker.DM, "Not a member: {}", destination);
        }
        if (ce == null)
          ce = new ConnectExceptions();
        ce.addFailure(destination, new ShunnedMemberException(
            LocalizedStrings.DirectChannel_SHUNNING_0.toLocalizedString(destination)));
      } else {
        try {
          long startTime = 0;
          if (ackTimeout > 0) {
            startTime = System.currentTimeMillis();
          }
          Connection con = conduit.getConnection(destination, preserveOrder, retry, startTime,
              ackTimeout, ackSDTimeout);

          con.setInUse(true, startTime, 0, 0, null); // fix for bug#37657
          cons.add(con);
          if (con.isSharedResource() && msg instanceof DirectReplyMessage) {
            DirectReplyMessage directMessage = (DirectReplyMessage) msg;
            directMessage.registerProcessor();
          }
        } catch (IOException ex) {
          if (ce == null)
            ce = new ConnectExceptions();
          ce.addFailure(destination, ex);
        }
      }
    } // for
    return ce;
  }


  /**
   * Method send.
   * 
   * @param mgr - the membership manager
   * @param destinations - the address(es) to send the message to.
   * @param msg - the message to send
   * @param ackWaitThreshold
   * @param ackSAThreshold severe alert threshold
   * @return number of bytes sent
   * @throws ConnectExceptions if message could not be send to one or more of the
   *         destinations
   * @throws NotSerializableException If the content cannot be serialized
   * @throws ConnectionException if the conduit has stopped
   */
  public int send(MembershipManager mgr, InternalDistributedMember[] destinations,
      DistributionMessage msg, long ackWaitThreshold, long ackSAThreshold)
      throws ConnectExceptions, NotSerializableException {

    if (disconnected) {
      if (logger.isDebugEnabled()) {
        logger.debug("Returning from DirectChannel send because channel is disconnected: {}", msg);
      }
      return 0;
    }
    if (destinations == null) {
      if (logger.isDebugEnabled()) {
        logger.debug("Returning from DirectChannel send because null set passed in: {}", msg);
      }
      return 0;
    }
    if (destinations.length == 0) {
      if (logger.isDebugEnabled()) {
        logger.debug("Returning from DirectChannel send because empty destinations passed in {}",
            msg);
      }
      return 0;
    }

    msg.setSender(localAddr);
    if (destinations.length == 1) {
      return sendToOne(mgr, destinations, msg, ackWaitThreshold, ackSAThreshold);
    } else {
      return sendToMany(mgr, destinations, msg, ackWaitThreshold, ackSAThreshold);
    }
  }



  /**
   * Returns null if no stats available.
   */
  public DMStats getDMStats() {
    DM dm = getDM();
    if (dm != null) {
      return dm.getStats(); // fix for bug#34004
    } else {
      return null;
    }
  }

  /**
   * Returns null if no config is available.
   * 
   * @since GemFire 4.2.2
   */
  public DistributionConfig getDMConfig() {
    DM dm = getDM();
    if (dm != null) {
      return dm.getConfig();
    } else {
      return null;
    }
  }

  /**
   * Returns null if no dm available.
   */
  public DM getDM() {
    return this.receiver.getDM();
  }

  /**
   * 
   * @param ackTimeout ack wait threshold
   * @param ackSATimeout severe alert threshold
   * @param c
   * @param processor
   * @throws ConnectionException
   */
  private void handleAckTimeout(long ackTimeout, long ackSATimeout, Connection c,
      DirectReplyProcessor processor) throws ConnectionException {
    DM dm = getDM();
    Set activeMembers = dm.getDistributionManagerIds();

    // Increment the stat
    dm.getStats().incReplyTimeouts();

    // an alert that will show up in the console
    {
      final StringId msg =
          LocalizedStrings.DirectChannel_0_SECONDS_HAVE_ELAPSED_WHILE_WAITING_FOR_REPLY_FROM_1_ON_2_WHOSE_CURRENT_MEMBERSHIP_LIST_IS_3;
      final Object[] msgArgs = new Object[] {Long.valueOf(ackTimeout / 1000), c.getRemoteAddress(),
          dm.getId(), activeMembers};
      logger.warn(LocalizedMessage.create(msg, msgArgs));
      msgArgs[3] = "(omitted)";
      Breadcrumbs.setProblem(msg, msgArgs);

      if (ReplyProcessor21.THROW_EXCEPTION_ON_TIMEOUT) {
        // init the cause to be a TimeoutException so catchers can determine cause
        TimeoutException cause =
            new TimeoutException(LocalizedStrings.TIMED_OUT_WAITING_FOR_ACKS.toLocalizedString());
        throw new InternalGemFireException(msg.toLocalizedString(msgArgs), cause);
      }
    }

    if (activeMembers.contains(c.getRemoteAddress())) {
      // wait for ack-severe-alert-threshold period first, then wait forever
      if (ackSATimeout > 0) {
        try {
          c.readAck((int) ackSATimeout, ackSATimeout, processor);
          return;
        } catch (SocketTimeoutException e) {
          Object[] args = new Object[] {Long.valueOf((ackSATimeout + ackTimeout) / 1000),
              c.getRemoteAddress(), dm.getId(), activeMembers};
          logger.fatal(LocalizedMessage.create(
              LocalizedStrings.DirectChannel_0_SECONDS_HAVE_ELAPSED_WHILE_WAITING_FOR_REPLY_FROM_1_ON_2_WHOSE_CURRENT_MEMBERSHIP_LIST_IS_3,
              args));
        }
      }
      try {
        c.readAck(0, 0, processor);
      } catch (SocketTimeoutException ex) {
        // this can never happen when called with timeout of 0
        logger.error(LocalizedMessage.create(
            LocalizedStrings.DirectChannel_UNEXPECTED_TIMEOUT_WHILE_WAITING_FOR_ACK_FROM__0,
            c.getRemoteAddress()), ex);
      }
    } else {
      logger.warn(LocalizedMessage.create(
          LocalizedStrings.DirectChannel_VIEW_NO_LONGER_HAS_0_AS_AN_ACTIVE_MEMBER_SO_WE_WILL_NO_LONGER_WAIT_FOR_IT,
          c.getRemoteAddress()));
      processor.memberDeparted(c.getRemoteAddress(), true);
    }
  }


  public void receive(DistributionMessage msg, int bytesRead) {
    if (disconnected) {
      return;
    }
    try {
      receiver.messageReceived(msg);
    } catch (MemberShunnedException e) {
      throw e;
    } catch (CancelException e) {
      // ignore
    } catch (Exception ex) {
      // Don't freak out if the DM is shutting down
      if (!conduit.getCancelCriterion().isCancelInProgress()) {
        logger.fatal(
            LocalizedMessage.create(LocalizedStrings.DirectChannel_WHILE_PULLING_A_MESSAGE), ex);
      }
    }
  }

  public InternalDistributedMember getLocalAddress() {
    return this.localAddr;
  }

  /**
   * Ensure that the TCPConduit class gets loaded.
   * 
   * @see SystemFailure#loadEmergencyClasses()
   */
  public static void loadEmergencyClasses() {
    TCPConduit.loadEmergencyClasses();
  }

  /**
   * Close the Conduit
   * 
   * @see SystemFailure#emergencyClose()
   */
  public void emergencyClose() {
    this.conduit.emergencyClose();
  }

  /**
   * This closes down the Direct connection. Theoretically you can disconnect and, if you need to
   * use the channel again you can and it will automatically reconnect. Reconnection will cause a
   * new local address to be generated.
   */
  public synchronized void disconnect(Exception cause) {
    // this.shutdownCause = cause;
    this.disconnected = true;
    this.disconnectCompleted = false;
    try {
      groupOrderedSenderSem.release();
    } catch (Error e) {
      // GEODE-1076 - already released
    }
    try {
      groupUnorderedSenderSem.release();
    } catch (Error e) {
      // GEODE-1076 - already released
    }
    this.conduit.stop(cause);
    this.disconnectCompleted = true;
  }

  public boolean isOpen() {
    return !disconnectCompleted;
  }

  /** returns the receiver to which this DirectChannel is delivering messages */
  protected DirectChannelListener getReceiver() {
    return receiver;
  }

  /**
   * Returns the port on which this direct channel sends messages
   */
  public int getPort() {
    return this.conduit.getPort();
  }

  /**
   * Returns the conduit over which this channel sends messages
   *
   * @since GemFire 2.1
   */
  public TCPConduit getConduit() {
    return this.conduit;
  }

  private InetAddress initAddress(DistributionConfig dc) {

    String bindAddress = dc.getBindAddress();

    try {
      /*
       * note: had to change the following to make sure the prop wasn't empty in addition to not
       * null for admin.DistributedSystemFactory
       */
      if (bindAddress != null && bindAddress.length() > 0) {
        return InetAddress.getByName(bindAddress);

      } else {
        return SocketCreator.getLocalHost();
      }
    } catch (java.net.UnknownHostException unhe) {
      throw new RuntimeException(unhe);

    }
  }

  public void closeEndpoint(InternalDistributedMember member, String reason) {
    closeEndpoint(member, reason, true);
  }


  /**
   * Closes any connections used to communicate with the given jgroupsAddress.
   */
  public void closeEndpoint(InternalDistributedMember member, String reason,
      boolean notifyDisconnect) {
    TCPConduit tc = this.conduit;
    if (tc != null) {
      tc.removeEndpoint(member, reason, notifyDisconnect);
    }
  }

  /**
   * adds state for thread-owned serial connections to the given member to the parameter
   * result. This can be used to wait for the state to reach the given level in the member's
   * vm.
   * 
   * @param member the member whose state is to be captured
   * @param result the map to add the state to
   * @since GemFire 5.1
   */
  public void getChannelStates(DistributedMember member, Map result) {
    TCPConduit tc = this.conduit;
    if (tc != null) {
      tc.getThreadOwnedOrderedConnectionState(member, result);
    }
  }

  /**
   * wait for the given connections to process the number of messages associated with the connection
   * in the given map
   */
  public void waitForChannelState(DistributedMember member, Map channelState)
      throws InterruptedException {
    if (Thread.interrupted())
      throw new InterruptedException();
    TCPConduit tc = this.conduit;
    if (tc != null) {
      tc.waitForThreadOwnedOrderedConnectionState(member, channelState);
    }
  }

  /**
   * returns true if there are still receiver threads for the given member
   */
  public boolean hasReceiversFor(DistributedMember mbr) {
    return this.conduit.hasReceiversFor(mbr);
  }

  /**
   * cause the channel to be sick
   */
  public void beSick() {
    TCPConduit tc = this.conduit;
    if (tc != null) {
      tc.beSick();
    }
  }

  /**
   * cause the channel to be healthy
   */
  public void beHealthy() {
    TCPConduit tc = this.conduit;
    if (tc != null) {
      tc.beHealthy();
    }
  }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy