예제 #1
0
  /**
   * @param packet the packet to intercept
   * @return the name of the interceptor that returned <code>false</code> or <code>null</code> if no
   *     interceptors returned <code>false</code>.
   */
  public static String invokeInterceptors(
      final Packet packet,
      final List<Interceptor> interceptors,
      final RemotingConnection connection) {
    if (interceptors != null) {
      for (final Interceptor interceptor : interceptors) {
        try {
          boolean callNext = interceptor.intercept(packet, connection);

          if (HornetQClientLogger.LOGGER.isDebugEnabled()) {
            // use a StringBuilder for speed since this may be executed a lot
            StringBuilder msg = new StringBuilder();
            msg.append("Invocation of interceptor ")
                .append(interceptor.getClass().getName())
                .append(" on ")
                .append(packet)
                .append(" returned ")
                .append(callNext);
            HornetQClientLogger.LOGGER.debug(msg.toString());
          }

          if (!callNext) {
            return interceptor.getClass().getName();
          }
        } catch (final Throwable e) {
          HornetQClientLogger.LOGGER.errorCallingInterceptor(e, interceptor);
        }
      }
    }

    return null;
  }
예제 #2
0
  public static void dumpSessionCreationStacks() {
    HornetQClientLogger.LOGGER.dumpingSessionStacks();

    for (DelegatingSession session : DelegatingSession.sessions) {
      HornetQClientLogger.LOGGER.dumpingSessionStack(session.creationStack);
    }
  }
예제 #3
0
  // This must never called by more than one thread concurrently
  public boolean send(final Packet packet, final boolean flush, final boolean batch) {
    if (invokeInterceptors(packet, interceptors, connection) != null) {
      return false;
    }

    synchronized (sendLock) {
      packet.setChannelID(id);

      if (isTrace) {
        HornetQClientLogger.LOGGER.trace(
            "Sending packet nonblocking " + packet + " on channeID=" + id);
      }

      HornetQBuffer buffer = packet.encode(connection);

      lock.lock();

      try {
        if (failingOver) {
          // TODO - don't hardcode this timeout
          try {
            failoverCondition.await(10000, TimeUnit.MILLISECONDS);
          } catch (InterruptedException e) {
            throw new HornetQInterruptedException(e);
          }
        }

        // Sanity check
        if (transferring) {
          throw new IllegalStateException("Cannot send a packet while channel is doing failover");
        }

        if (resendCache != null && packet.isRequiresConfirmations()) {
          resendCache.add(packet);
        }
      } finally {
        lock.unlock();
      }

      if (isTrace) {
        HornetQClientLogger.LOGGER.trace("Writing buffer for channelID=" + id);
      }

      // The actual send must be outside the lock, or with OIO transport, the write can block if the
      // tcp
      // buffer is full, preventing any incoming buffers being handled and blocking failover
      connection.getTransportConnection().write(buffer, flush, batch);

      return true;
    }
  }
예제 #4
0
  private void clearUpTo(final int lastReceivedCommandID) {
    final int numberToClear = 1 + lastReceivedCommandID - firstStoredCommandID;

    if (numberToClear == -1) {
      throw HornetQClientMessageBundle.BUNDLE.invalidCommandID(lastReceivedCommandID);
    }

    int sizeToFree = 0;

    for (int i = 0; i < numberToClear; i++) {
      final Packet packet = resendCache.poll();

      if (packet == null) {
        HornetQClientLogger.LOGGER.cannotFindPacketToClear(
            lastReceivedCommandID, firstStoredCommandID);
        firstStoredCommandID = lastReceivedCommandID + 1;
        return;
      }

      if (packet.getType() != PacketImpl.PACKETS_CONFIRMED) {
        sizeToFree += packet.getPacketSize();
      }

      if (commandConfirmationHandler != null) {
        commandConfirmationHandler.commandConfirmed(packet);
      }
    }

    firstStoredCommandID += numberToClear;
  }
 public void readBytes(final byte[] dst, final int dstIndex, final int length) {
   try {
     int nReadBytes = getStream().read(dst, dstIndex, length);
     if (nReadBytes < length) {
       HornetQClientLogger.LOGGER.compressedLargeMessageError(length, nReadBytes);
     }
   } catch (Exception e) {
     throw new IllegalStateException(e.getMessage(), e);
   }
 }
예제 #6
0
  public void replayCommands(final int otherLastConfirmedCommandID) {
    if (resendCache != null) {
      if (isTrace) {
        HornetQClientLogger.LOGGER.trace("Replaying commands on channelID=" + id);
      }
      clearUpTo(otherLastConfirmedCommandID);

      for (final Packet packet : resendCache) {
        doWrite(packet);
      }
    }
  }
예제 #7
0
  @Override
  protected void finalize() throws Throwable {
    // In some scenarios we have seen the JDK finalizing the DelegatingSession while the call to
    // session.close() was still in progress
    //
    if (!closed && !session.isClosed()) {
      HornetQClientLogger.LOGGER.clientSessionNotClosed(
          creationStack, System.identityHashCode(this));

      close();
    }

    super.finalize();
  }
예제 #8
0
/**
 * A ChannelImpl
 *
 * @author <a href="mailto:[email protected]">Tim Fox</a>
 */
public final class ChannelImpl implements Channel {
  public enum CHANNEL_ID {
    /** Used for core protocol management. */
    PING(0),
    /** Session creation and attachment. */
    SESSION(1),
    /** Replication, i.e. for backups that do not share the journal. */
    REPLICATION(2),
    /** Channels [0-9] are reserved for the system, user channels must be greater than that. */
    USER(10);

    public final long id;

    CHANNEL_ID(long id) {
      this.id = id;
    }

    protected static String idToString(long code) {
      for (CHANNEL_ID channel : EnumSet.allOf(CHANNEL_ID.class)) {
        if (channel.id == code) return channel.toString();
      }
      return Long.toString(code);
    }
  }

  private static final boolean isTrace = HornetQClientLogger.LOGGER.isTraceEnabled();

  private volatile long id;

  private ChannelHandler handler;

  private Packet response;

  private final java.util.Queue<Packet> resendCache;

  private volatile int firstStoredCommandID;

  private final AtomicInteger lastConfirmedCommandID = new AtomicInteger(-1);

  private volatile CoreRemotingConnection connection;

  private volatile boolean closed;

  private final Lock lock = new ReentrantLock();

  private final Condition sendCondition = lock.newCondition();

  private final Condition failoverCondition = lock.newCondition();

  private final Object sendLock = new Object();

  private final Object sendBlockingLock = new Object();

  private boolean failingOver;

  private final int confWindowSize;

  private int receivedBytes;

  private CommandConfirmationHandler commandConfirmationHandler;

  private volatile boolean transferring;

  private final List<Interceptor> interceptors;

  public ChannelImpl(
      final CoreRemotingConnection connection,
      final long id,
      final int confWindowSize,
      final List<Interceptor> interceptors) {
    this.connection = connection;

    this.id = id;

    this.confWindowSize = confWindowSize;

    if (confWindowSize != -1) {
      resendCache = new ConcurrentLinkedQueue<Packet>();
    } else {
      resendCache = null;
    }

    this.interceptors = interceptors;
  }

  public boolean supports(final byte packetType) {
    int version = connection.getClientVersion();

    switch (packetType) {
      case PacketImpl.CLUSTER_TOPOLOGY_V2:
        return version >= 122;
      case PacketImpl.DISCONNECT_CONSUMER:
        return version >= 124;
      case PacketImpl.CLUSTER_TOPOLOGY_V3:
        return version >= 125;
      case PacketImpl.NODE_ANNOUNCE_V2:
        return version >= 125;
      case PacketImpl.DISCONNECT_V2:
        return version >= 125;
      default:
        return true;
    }
  }

  public long getID() {
    return id;
  }

  public int getLastConfirmedCommandID() {
    return lastConfirmedCommandID.get();
  }

  public Lock getLock() {
    return lock;
  }

  public int getConfirmationWindowSize() {
    return confWindowSize;
  }

  public void returnBlocking() {
    lock.lock();

    try {
      response = new HornetQExceptionMessage(HornetQClientMessageBundle.BUNDLE.unblockingACall());

      sendCondition.signal();
    } finally {
      lock.unlock();
    }
  }

  public boolean sendAndFlush(final Packet packet) {
    return send(packet, true, false);
  }

  public boolean send(final Packet packet) {
    return send(packet, false, false);
  }

  public boolean sendBatched(final Packet packet) {
    return send(packet, false, true);
  }

  public void setTransferring(boolean transferring) {
    this.transferring = transferring;
  }

  // This must never called by more than one thread concurrently
  public boolean send(final Packet packet, final boolean flush, final boolean batch) {
    if (invokeInterceptors(packet, interceptors, connection) != null) {
      return false;
    }

    synchronized (sendLock) {
      packet.setChannelID(id);

      if (isTrace) {
        HornetQClientLogger.LOGGER.trace(
            "Sending packet nonblocking " + packet + " on channeID=" + id);
      }

      HornetQBuffer buffer = packet.encode(connection);

      lock.lock();

      try {
        if (failingOver) {
          // TODO - don't hardcode this timeout
          try {
            failoverCondition.await(10000, TimeUnit.MILLISECONDS);
          } catch (InterruptedException e) {
            throw new HornetQInterruptedException(e);
          }
        }

        // Sanity check
        if (transferring) {
          throw new IllegalStateException("Cannot send a packet while channel is doing failover");
        }

        if (resendCache != null && packet.isRequiresConfirmations()) {
          resendCache.add(packet);
        }
      } finally {
        lock.unlock();
      }

      if (isTrace) {
        HornetQClientLogger.LOGGER.trace("Writing buffer for channelID=" + id);
      }

      // The actual send must be outside the lock, or with OIO transport, the write can block if the
      // tcp
      // buffer is full, preventing any incoming buffers being handled and blocking failover
      connection.getTransportConnection().write(buffer, flush, batch);

      return true;
    }
  }

  /**
   * Due to networking issues or server issues the server may take longer to answer than expected..
   * the client may timeout the call throwing an exception and the client could eventually retry
   * another call, but the server could then answer a previous command issuing a
   * class-cast-exception. The expectedPacket will be used to filter out undesirable packets that
   * would belong to previous calls.
   */
  public Packet sendBlocking(final Packet packet, byte expectedPacket) throws HornetQException {
    String interceptionResult = invokeInterceptors(packet, interceptors, connection);

    if (interceptionResult != null) {
      // if we don't throw an exception here the client might not unblock
      throw HornetQClientMessageBundle.BUNDLE.interceptorRejectedPacket(interceptionResult);
    }

    if (closed) {
      throw HornetQClientMessageBundle.BUNDLE.connectionDestroyed();
    }

    if (connection.getBlockingCallTimeout() == -1) {
      throw new IllegalStateException(
          "Cannot do a blocking call timeout on a server side connection");
    }

    // Synchronized since can't be called concurrently by more than one thread and this can occur
    // E.g. blocking acknowledge() from inside a message handler at some time as other operation on
    // main thread
    synchronized (sendBlockingLock) {
      packet.setChannelID(id);

      final HornetQBuffer buffer = packet.encode(connection);

      lock.lock();

      try {
        if (failingOver) {
          try {
            if (connection.getBlockingCallFailoverTimeout() < 0) {
              while (failingOver) {
                failoverCondition.await();
              }
            } else {
              if (!failoverCondition.await(
                  connection.getBlockingCallFailoverTimeout(), TimeUnit.MILLISECONDS)) {
                HornetQClientLogger.LOGGER.debug("timed-out waiting for failover condition");
              }
            }
          } catch (InterruptedException e) {
            throw new HornetQInterruptedException(e);
          }
        }

        response = null;

        if (resendCache != null && packet.isRequiresConfirmations()) {
          resendCache.add(packet);
        }

        connection.getTransportConnection().write(buffer, false, false);

        long toWait = connection.getBlockingCallTimeout();

        long start = System.currentTimeMillis();

        while (!closed
            && (response == null
                || (response.getType() != PacketImpl.EXCEPTION
                    && response.getType() != expectedPacket))
            && toWait > 0) {
          try {
            sendCondition.await(toWait, TimeUnit.MILLISECONDS);
          } catch (InterruptedException e) {
            throw new HornetQInterruptedException(e);
          }

          if (response != null
              && response.getType() != PacketImpl.EXCEPTION
              && response.getType() != expectedPacket) {
            HornetQClientLogger.LOGGER.packetOutOfOrder(response, new Exception("trace"));
          }

          if (closed) {
            break;
          }

          final long now = System.currentTimeMillis();

          toWait -= now - start;

          start = now;
        }

        if (response == null) {
          throw HornetQClientMessageBundle.BUNDLE.timedOutSendingPacket(packet.getType());
        }

        if (response.getType() == PacketImpl.EXCEPTION) {
          final HornetQExceptionMessage mem = (HornetQExceptionMessage) response;

          HornetQException e = mem.getException();

          e.fillInStackTrace();

          throw e;
        }
      } finally {
        lock.unlock();
      }

      return response;
    }
  }

  /**
   * @param packet the packet to intercept
   * @return the name of the interceptor that returned <code>false</code> or <code>null</code> if no
   *     interceptors returned <code>false</code>.
   */
  public static String invokeInterceptors(
      final Packet packet,
      final List<Interceptor> interceptors,
      final RemotingConnection connection) {
    if (interceptors != null) {
      for (final Interceptor interceptor : interceptors) {
        try {
          boolean callNext = interceptor.intercept(packet, connection);

          if (HornetQClientLogger.LOGGER.isDebugEnabled()) {
            // use a StringBuilder for speed since this may be executed a lot
            StringBuilder msg = new StringBuilder();
            msg.append("Invocation of interceptor ")
                .append(interceptor.getClass().getName())
                .append(" on ")
                .append(packet)
                .append(" returned ")
                .append(callNext);
            HornetQClientLogger.LOGGER.debug(msg.toString());
          }

          if (!callNext) {
            return interceptor.getClass().getName();
          }
        } catch (final Throwable e) {
          HornetQClientLogger.LOGGER.errorCallingInterceptor(e, interceptor);
        }
      }
    }

    return null;
  }

  public void setCommandConfirmationHandler(final CommandConfirmationHandler handler) {
    if (confWindowSize < 0) {
      final String msg =
          "You can't set confirmationHandler on a connection with confirmation-window-size < 0."
              + " Look at the documentation for more information.";
      throw new IllegalStateException(msg);
    }
    commandConfirmationHandler = handler;
  }

  public void setHandler(final ChannelHandler handler) {
    this.handler = handler;
  }

  public ChannelHandler getHandler() {
    return handler;
  }

  public void close() {
    if (closed) {
      return;
    }

    if (!connection.isDestroyed() && !connection.removeChannel(id)) {
      throw HornetQClientMessageBundle.BUNDLE.noChannelToClose(id);
    }

    if (failingOver) {
      unlock();
    }
    closed = true;
  }

  public void transferConnection(final CoreRemotingConnection newConnection) {
    // Needs to synchronize on the connection to make sure no packets from
    // the old connection get processed after transfer has occurred
    synchronized (connection.getTransferLock()) {
      connection.removeChannel(id);

      // And switch it

      final CoreRemotingConnection rnewConnection = newConnection;

      rnewConnection.putChannel(id, this);

      connection = rnewConnection;

      transferring = true;
    }
  }

  public void replayCommands(final int otherLastConfirmedCommandID) {
    if (resendCache != null) {
      if (isTrace) {
        HornetQClientLogger.LOGGER.trace("Replaying commands on channelID=" + id);
      }
      clearUpTo(otherLastConfirmedCommandID);

      for (final Packet packet : resendCache) {
        doWrite(packet);
      }
    }
  }

  public void lock() {
    lock.lock();

    failingOver = true;

    lock.unlock();
  }

  public void unlock() {
    lock.lock();

    failingOver = false;

    failoverCondition.signalAll();

    lock.unlock();
  }

  public CoreRemotingConnection getConnection() {
    return connection;
  }

  // Needs to be synchronized since can be called by remoting service timer thread too for timeout
  // flush
  public synchronized void flushConfirmations() {
    if (resendCache != null && receivedBytes != 0) {
      receivedBytes = 0;

      final Packet confirmed = new PacketsConfirmedMessage(lastConfirmedCommandID.get());

      confirmed.setChannelID(id);

      doWrite(confirmed);
    }
  }

  public void confirm(final Packet packet) {
    if (resendCache != null && packet.isRequiresConfirmations()) {
      lastConfirmedCommandID.incrementAndGet();

      receivedBytes += packet.getPacketSize();

      if (receivedBytes >= confWindowSize) {
        receivedBytes = 0;

        final Packet confirmed = new PacketsConfirmedMessage(lastConfirmedCommandID.get());

        confirmed.setChannelID(id);

        doWrite(confirmed);
      }
    }
  }

  public void clearCommands() {
    if (resendCache != null) {
      lastConfirmedCommandID.set(-1);

      firstStoredCommandID = 0;

      resendCache.clear();
    }
  }

  public void handlePacket(final Packet packet) {
    if (packet.getType() == PacketImpl.PACKETS_CONFIRMED) {
      if (resendCache != null) {
        final PacketsConfirmedMessage msg = (PacketsConfirmedMessage) packet;

        clearUpTo(msg.getCommandID());
      }

      if (!connection.isClient()) {
        handler.handlePacket(packet);
      }

      return;
    } else {
      if (packet.isResponse()) {
        confirm(packet);

        lock.lock();

        try {
          response = packet;
          sendCondition.signal();
        } finally {
          lock.unlock();
        }
      } else if (handler != null) {
        handler.handlePacket(packet);
      }
    }
  }

  private void doWrite(final Packet packet) {
    final HornetQBuffer buffer = packet.encode(connection);

    connection.getTransportConnection().write(buffer, false, false);
  }

  private void clearUpTo(final int lastReceivedCommandID) {
    final int numberToClear = 1 + lastReceivedCommandID - firstStoredCommandID;

    if (numberToClear == -1) {
      throw HornetQClientMessageBundle.BUNDLE.invalidCommandID(lastReceivedCommandID);
    }

    int sizeToFree = 0;

    for (int i = 0; i < numberToClear; i++) {
      final Packet packet = resendCache.poll();

      if (packet == null) {
        HornetQClientLogger.LOGGER.cannotFindPacketToClear(
            lastReceivedCommandID, firstStoredCommandID);
        firstStoredCommandID = lastReceivedCommandID + 1;
        return;
      }

      if (packet.getType() != PacketImpl.PACKETS_CONFIRMED) {
        sizeToFree += packet.getPacketSize();
      }

      if (commandConfirmationHandler != null) {
        commandConfirmationHandler.commandConfirmed(packet);
      }
    }

    firstStoredCommandID += numberToClear;
  }

  @Override
  public String toString() {
    return "Channel[id=" + CHANNEL_ID.idToString(id) + ", handler=" + handler + "]";
  }
}
예제 #9
0
  /**
   * Due to networking issues or server issues the server may take longer to answer than expected..
   * the client may timeout the call throwing an exception and the client could eventually retry
   * another call, but the server could then answer a previous command issuing a
   * class-cast-exception. The expectedPacket will be used to filter out undesirable packets that
   * would belong to previous calls.
   */
  public Packet sendBlocking(final Packet packet, byte expectedPacket) throws HornetQException {
    String interceptionResult = invokeInterceptors(packet, interceptors, connection);

    if (interceptionResult != null) {
      // if we don't throw an exception here the client might not unblock
      throw HornetQClientMessageBundle.BUNDLE.interceptorRejectedPacket(interceptionResult);
    }

    if (closed) {
      throw HornetQClientMessageBundle.BUNDLE.connectionDestroyed();
    }

    if (connection.getBlockingCallTimeout() == -1) {
      throw new IllegalStateException(
          "Cannot do a blocking call timeout on a server side connection");
    }

    // Synchronized since can't be called concurrently by more than one thread and this can occur
    // E.g. blocking acknowledge() from inside a message handler at some time as other operation on
    // main thread
    synchronized (sendBlockingLock) {
      packet.setChannelID(id);

      final HornetQBuffer buffer = packet.encode(connection);

      lock.lock();

      try {
        if (failingOver) {
          try {
            if (connection.getBlockingCallFailoverTimeout() < 0) {
              while (failingOver) {
                failoverCondition.await();
              }
            } else {
              if (!failoverCondition.await(
                  connection.getBlockingCallFailoverTimeout(), TimeUnit.MILLISECONDS)) {
                HornetQClientLogger.LOGGER.debug("timed-out waiting for failover condition");
              }
            }
          } catch (InterruptedException e) {
            throw new HornetQInterruptedException(e);
          }
        }

        response = null;

        if (resendCache != null && packet.isRequiresConfirmations()) {
          resendCache.add(packet);
        }

        connection.getTransportConnection().write(buffer, false, false);

        long toWait = connection.getBlockingCallTimeout();

        long start = System.currentTimeMillis();

        while (!closed
            && (response == null
                || (response.getType() != PacketImpl.EXCEPTION
                    && response.getType() != expectedPacket))
            && toWait > 0) {
          try {
            sendCondition.await(toWait, TimeUnit.MILLISECONDS);
          } catch (InterruptedException e) {
            throw new HornetQInterruptedException(e);
          }

          if (response != null
              && response.getType() != PacketImpl.EXCEPTION
              && response.getType() != expectedPacket) {
            HornetQClientLogger.LOGGER.packetOutOfOrder(response, new Exception("trace"));
          }

          if (closed) {
            break;
          }

          final long now = System.currentTimeMillis();

          toWait -= now - start;

          start = now;
        }

        if (response == null) {
          throw HornetQClientMessageBundle.BUNDLE.timedOutSendingPacket(packet.getType());
        }

        if (response.getType() == PacketImpl.EXCEPTION) {
          final HornetQExceptionMessage mem = (HornetQExceptionMessage) response;

          HornetQException e = mem.getException();

          e.fillInStackTrace();

          throw e;
        }
      } finally {
        lock.unlock();
      }

      return response;
    }
  }