@Override
  public void fail(final ActiveMQException me, String scaleDownTargetNodeID) {
    synchronized (failLock) {
      if (destroyed) {
        return;
      }

      destroyed = true;
    }

    ActiveMQClientLogger.LOGGER.connectionFailureDetected(me.getMessage(), me.getType());

    try {
      transportConnection.forceClose();
    } catch (Throwable e) {
      ActiveMQClientLogger.LOGGER.warn(e.getMessage(), e);
    }

    // Then call the listeners
    callFailureListeners(me, scaleDownTargetNodeID);

    callClosingListeners();

    internalClose();

    for (Channel channel : channels.values()) {
      channel.returnBlocking(me);
    }
  }
  private void internalClose() {
    // We close the underlying transport connection
    getTransportConnection().close();

    for (Channel channel : channels.values()) {
      channel.close();
    }
  }
 // We flush any confirmations on the connection - this prevents idle bridges for example
 // sitting there with many unacked messages
 @Override
 public void flush() {
   synchronized (transferLock) {
     for (Channel channel : channels.values()) {
       channel.flushConfirmations();
     }
   }
 }
  private void doBufferReceived(final Packet packet) {
    if (ChannelImpl.invokeInterceptors(packet, incomingInterceptors, this) != null) {
      return;
    }

    synchronized (transferLock) {
      final Channel channel = channels.get(packet.getChannelID());

      if (channel != null) {
        channel.handlePacket(packet);
      }
    }
  }
  @Override
  public synchronized void stop() throws Exception {
    if (!started) {
      return;
    }

    // Channel may be null if there isn't a connection to a live server
    if (channel != null) {
      channel.close();
    }

    for (ReplicatedLargeMessage largeMessage : largeMessages.values()) {
      largeMessage.releaseResources();
    }
    largeMessages.clear();

    for (Entry<JournalContent, Map<Long, JournalSyncFile>> entry :
        filesReservedForSync.entrySet()) {
      for (JournalSyncFile filesReserved : entry.getValue().values()) {
        filesReserved.close();
      }
    }

    filesReservedForSync.clear();
    if (journals != null) {
      for (Journal j : journals) {
        if (j instanceof FileWrapperJournal) j.stop();
      }
    }

    for (ConcurrentMap<Integer, Page> map : pageIndex.values()) {
      for (Page page : map.values()) {
        try {
          page.sync();
          page.close();
        } catch (Exception e) {
          ActiveMQServerLogger.LOGGER.errorClosingPageOnReplication(e);
        }
      }
    }
    pageManager.stop();

    pageIndex.clear();
    final CountDownLatch latch = new CountDownLatch(1);
    executor.execute(
        new Runnable() {

          @Override
          public void run() {
            latch.countDown();
          }
        });
    latch.await(30, TimeUnit.SECONDS);

    // Storage needs to be the last to stop
    storageManager.stop();

    started = false;
  }
 private void handleCheckForFailover(CheckFailoverMessage failoverMessage) {
   String nodeID = failoverMessage.getNodeID();
   boolean okToFailover =
       nodeID == null
           || !(server.getHAPolicy().canScaleDown()
               && !server.hasScaledDown(new SimpleString(nodeID)));
   channel1.send(new CheckFailoverReplyMessage(okToFailover));
 }
  public ConnectionEntry createConnectionEntry(
      final Acceptor acceptorUsed, final Connection connection) {
    final Configuration config = server.getConfiguration();

    Executor connectionExecutor = server.getExecutorFactory().getExecutor();

    final CoreRemotingConnection rc =
        new RemotingConnectionImpl(
            ServerPacketDecoder.INSTANCE,
            connection,
            incomingInterceptors,
            outgoingInterceptors,
            config.isAsyncConnectionExecutionEnabled() ? connectionExecutor : null,
            server.getNodeID());

    Channel channel1 = rc.getChannel(CHANNEL_ID.SESSION.id, -1);

    ChannelHandler handler = new ActiveMQPacketHandler(this, server, channel1, rc);

    channel1.setHandler(handler);

    long ttl = ActiveMQClient.DEFAULT_CONNECTION_TTL;

    if (config.getConnectionTTLOverride() != -1) {
      ttl = config.getConnectionTTLOverride();
    }

    final ConnectionEntry entry =
        new ConnectionEntry(rc, connectionExecutor, System.currentTimeMillis(), ttl);

    final Channel channel0 = rc.getChannel(ChannelImpl.CHANNEL_ID.PING.id, -1);

    channel0.setHandler(new LocalChannelHandler(config, entry, channel0, acceptorUsed, rc));

    server
        .getClusterManager()
        .addClusterChannelHandler(
            rc.getChannel(CHANNEL_ID.CLUSTER.id, -1), acceptorUsed, rc, server.getActivation());

    return entry;
  }
  @Override
  public void handlePacket(final Packet packet) {
    PacketImpl response = new ReplicationResponseMessage();
    final byte type = packet.getType();

    try {
      if (!started) {
        return;
      }

      if (type == PacketImpl.REPLICATION_APPEND) {
        handleAppendAddRecord((ReplicationAddMessage) packet);
      } else if (type == PacketImpl.REPLICATION_APPEND_TX) {
        handleAppendAddTXRecord((ReplicationAddTXMessage) packet);
      } else if (type == PacketImpl.REPLICATION_DELETE) {
        handleAppendDelete((ReplicationDeleteMessage) packet);
      } else if (type == PacketImpl.REPLICATION_DELETE_TX) {
        handleAppendDeleteTX((ReplicationDeleteTXMessage) packet);
      } else if (type == PacketImpl.REPLICATION_PREPARE) {
        handlePrepare((ReplicationPrepareMessage) packet);
      } else if (type == PacketImpl.REPLICATION_COMMIT_ROLLBACK) {
        handleCommitRollback((ReplicationCommitMessage) packet);
      } else if (type == PacketImpl.REPLICATION_PAGE_WRITE) {
        handlePageWrite((ReplicationPageWriteMessage) packet);
      } else if (type == PacketImpl.REPLICATION_PAGE_EVENT) {
        handlePageEvent((ReplicationPageEventMessage) packet);
      } else if (type == PacketImpl.REPLICATION_LARGE_MESSAGE_BEGIN) {
        handleLargeMessageBegin((ReplicationLargeMessageBeginMessage) packet);
      } else if (type == PacketImpl.REPLICATION_LARGE_MESSAGE_WRITE) {
        handleLargeMessageWrite((ReplicationLargeMessageWriteMessage) packet);
      } else if (type == PacketImpl.REPLICATION_LARGE_MESSAGE_END) {
        handleLargeMessageEnd((ReplicationLargeMessageEndMessage) packet);
      } else if (type == PacketImpl.REPLICATION_START_FINISH_SYNC) {
        response = handleStartReplicationSynchronization((ReplicationStartSyncMessage) packet);
      } else if (type == PacketImpl.REPLICATION_SYNC_FILE) {
        handleReplicationSynchronization((ReplicationSyncFileMessage) packet);
      } else if (type == PacketImpl.REPLICATION_SCHEDULED_FAILOVER) {
        handleLiveStopping((ReplicationLiveIsStoppingMessage) packet);
      } else if (type == PacketImpl.BACKUP_REGISTRATION_FAILED) {
        handleFatalError((BackupReplicationStartFailedMessage) packet);
      } else {
        ActiveMQServerLogger.LOGGER.invalidPacketForReplication(packet);
      }
    } catch (ActiveMQException e) {
      ActiveMQServerLogger.LOGGER.errorHandlingReplicationPacket(e, packet);
      response = new ActiveMQExceptionMessage(e);
    } catch (Exception e) {
      ActiveMQServerLogger.LOGGER.errorHandlingReplicationPacket(e, packet);
      response =
          new ActiveMQExceptionMessage(ActiveMQMessageBundle.BUNDLE.replicationUnhandledError(e));
    }
    channel.send(response);
  }
  @Override
  public void disconnect(String scaleDownNodeID, final boolean criticalError) {
    Channel channel0 = getChannel(ChannelImpl.CHANNEL_ID.PING.id, -1);

    // And we remove all channels from the connection, this ensures no more packets will be
    // processed after this
    // method is
    // complete

    Set<Channel> allChannels = new HashSet<>(channels.values());

    if (!criticalError) {
      removeAllChannels();
    } else {
      // We can't hold a lock if a critical error is happening...
      // as other threads will be holding the lock while hanging on IO
      channels.clear();
    }

    // Now we are 100% sure that no more packets will be processed we can flush then send the
    // disconnect

    if (!criticalError) {
      for (Channel channel : allChannels) {
        channel.flushConfirmations();
      }
    }
    Packet disconnect;

    if (channel0.supports(PacketImpl.DISCONNECT_V2)) {
      disconnect = new DisconnectMessage_V2(nodeID, scaleDownNodeID);
    } else {
      disconnect = new DisconnectMessage(nodeID);
    }
    channel0.sendAndFlush(disconnect);
  }
  private void handleReattachSession(final ReattachSessionMessage request) {
    Packet response = null;

    try {

      if (!server.isStarted()) {
        response = new ReattachSessionResponseMessage(-1, false);
      }

      logger.debug("Reattaching request from " + connection.getRemoteAddress());

      ServerSessionPacketHandler sessionHandler =
          protocolManager.getSessionHandler(request.getName());

      // HORNETQ-720 XXX ataylor?
      if (
      /*!server.checkActivate() || */ sessionHandler == null) {
        response = new ReattachSessionResponseMessage(-1, false);
      } else {
        if (sessionHandler.getChannel().getConfirmationWindowSize() == -1) {
          // Even though session exists, we can't reattach since confi window size == -1,
          // i.e. we don't have a resend cache for commands, so we just close the old session
          // and let the client recreate

          ActiveMQServerLogger.LOGGER.reattachRequestFailed(connection.getRemoteAddress());

          sessionHandler.closeListeners();
          sessionHandler.close();

          response = new ReattachSessionResponseMessage(-1, false);
        } else {
          // Reconnect the channel to the new connection
          int serverLastConfirmedCommandID =
              sessionHandler.transferConnection(connection, request.getLastConfirmedCommandID());

          response = new ReattachSessionResponseMessage(serverLastConfirmedCommandID, true);
        }
      }
    } catch (Exception e) {
      ActiveMQServerLogger.LOGGER.failedToReattachSession(e);

      response = new ActiveMQExceptionMessage(new ActiveMQInternalErrorException());
    }

    channel1.send(response);
  }
  private void handleCreateSession(final CreateSessionMessage request) {
    boolean incompatibleVersion = false;
    Packet response;
    try {
      Version version = server.getVersion();
      if (!version.isCompatible(request.getVersion())) {
        throw ActiveMQMessageBundle.BUNDLE.incompatibleClientServer();
      }

      if (!server.isStarted()) {
        throw ActiveMQMessageBundle.BUNDLE.serverNotStarted();
      }

      // XXX HORNETQ-720 Taylor commented out this test. Should be verified.
      /*if (!server.checkActivate())
      {
         throw new ActiveMQException(ActiveMQException.SESSION_CREATION_REJECTED,
                                    "Server will not accept create session requests");
      }*/

      if (connection.getClientVersion() == 0) {
        connection.setClientVersion(request.getVersion());
      } else if (connection.getClientVersion() != request.getVersion()) {
        ActiveMQServerLogger.LOGGER.incompatibleVersionAfterConnect(
            request.getVersion(), connection.getClientVersion());
      }

      Channel channel =
          connection.getChannel(request.getSessionChannelID(), request.getWindowSize());

      ActiveMQPrincipal activeMQPrincipal = null;

      if (request.getUsername() == null) {
        activeMQPrincipal = connection.getDefaultActiveMQPrincipal();
      }

      ServerSession session =
          server.createSession(
              request.getName(),
              activeMQPrincipal == null ? request.getUsername() : activeMQPrincipal.getUserName(),
              activeMQPrincipal == null ? request.getPassword() : activeMQPrincipal.getPassword(),
              request.getMinLargeMessageSize(),
              connection,
              request.isAutoCommitSends(),
              request.isAutoCommitAcks(),
              request.isPreAcknowledge(),
              request.isXA(),
              request.getDefaultAddress(),
              new CoreSessionCallback(request.getName(), protocolManager, channel, connection),
              true);

      ServerSessionPacketHandler handler =
          new ServerSessionPacketHandler(session, server.getStorageManager(), channel);
      channel.setHandler(handler);

      // TODO - where is this removed?
      protocolManager.addSessionHandler(request.getName(), handler);

      response = new CreateSessionResponseMessage(server.getVersion().getIncrementingVersion());
    } catch (ActiveMQException e) {
      if (e.getType() == ActiveMQExceptionType.INCOMPATIBLE_CLIENT_SERVER_VERSIONS) {
        incompatibleVersion = true;
        logger.debug("Sending ActiveMQException after Incompatible client", e);
      } else {
        ActiveMQServerLogger.LOGGER.failedToCreateSession(e);
      }

      response = new ActiveMQExceptionMessage(e);
    } catch (Exception e) {
      ActiveMQServerLogger.LOGGER.failedToCreateSession(e);

      response = new ActiveMQExceptionMessage(new ActiveMQInternalErrorException());
    }

    // send the exception to the client and destroy
    // the connection if the client and server versions
    // are not compatible
    if (incompatibleVersion) {
      channel1.sendAndFlush(response);
    } else {
      channel1.send(response);
    }
  }
    public void handlePacket(final Packet packet) {
      if (packet.getType() == PacketImpl.PING) {
        Ping ping = (Ping) packet;

        if (config.getConnectionTTLOverride() == -1) {
          // Allow clients to specify connection ttl
          entry.ttl = ping.getConnectionTTL();
        }

        // Just send a ping back
        channel0.send(packet);
      } else if (packet.getType() == PacketImpl.SUBSCRIBE_TOPOLOGY
          || packet.getType() == PacketImpl.SUBSCRIBE_TOPOLOGY_V2) {
        SubscribeClusterTopologyUpdatesMessage msg =
            (SubscribeClusterTopologyUpdatesMessage) packet;

        if (packet.getType() == PacketImpl.SUBSCRIBE_TOPOLOGY_V2) {
          channel0
              .getConnection()
              .setClientVersion(
                  ((SubscribeClusterTopologyUpdatesMessageV2) msg).getClientVersion());
        }

        final ClusterTopologyListener listener =
            new ClusterTopologyListener() {
              @Override
              public void nodeUP(final TopologyMember topologyMember, final boolean last) {
                try {
                  final Pair<TransportConfiguration, TransportConfiguration> connectorPair =
                      BackwardsCompatibilityUtils.getTCPair(
                          channel0.getConnection().getClientVersion(), topologyMember);

                  final String nodeID = topologyMember.getNodeId();
                  // Using an executor as most of the notifications on the Topology
                  // may come from a channel itself
                  // What could cause deadlocks
                  entry.connectionExecutor.execute(
                      new Runnable() {
                        public void run() {
                          if (channel0.supports(PacketImpl.CLUSTER_TOPOLOGY_V3)) {
                            channel0.send(
                                new ClusterTopologyChangeMessage_V3(
                                    topologyMember.getUniqueEventID(),
                                    nodeID,
                                    topologyMember.getBackupGroupName(),
                                    topologyMember.getScaleDownGroupName(),
                                    connectorPair,
                                    last));
                          } else if (channel0.supports(PacketImpl.CLUSTER_TOPOLOGY_V2)) {
                            channel0.send(
                                new ClusterTopologyChangeMessage_V2(
                                    topologyMember.getUniqueEventID(),
                                    nodeID,
                                    topologyMember.getBackupGroupName(),
                                    connectorPair,
                                    last));
                          } else {
                            channel0.send(
                                new ClusterTopologyChangeMessage(nodeID, connectorPair, last));
                          }
                        }
                      });
                } catch (RejectedExecutionException ignored) {
                  // this could happen during a shutdown and we don't care, if we lost a nodeDown
                  // during a shutdown
                  // what can we do anyways?
                }
              }

              @Override
              public void nodeDown(final long uniqueEventID, final String nodeID) {
                // Using an executor as most of the notifications on the Topology
                // may come from a channel itself
                // What could cause deadlocks
                try {
                  entry.connectionExecutor.execute(
                      new Runnable() {
                        public void run() {
                          if (channel0.supports(PacketImpl.CLUSTER_TOPOLOGY_V2)) {
                            channel0.send(
                                new ClusterTopologyChangeMessage_V2(uniqueEventID, nodeID));
                          } else {
                            channel0.send(new ClusterTopologyChangeMessage(nodeID));
                          }
                        }
                      });
                } catch (RejectedExecutionException ignored) {
                  // this could happen during a shutdown and we don't care, if we lost a nodeDown
                  // during a shutdown
                  // what can we do anyways?
                }
              }

              @Override
              public String toString() {
                return "Remote Proxy on channel "
                    + Integer.toHexString(System.identityHashCode(this));
              }
            };

        if (acceptorUsed.getClusterConnection() != null) {
          acceptorUsed.getClusterConnection().addClusterTopologyListener(listener);

          rc.addCloseListener(
              new CloseListener() {
                public void connectionClosed() {
                  acceptorUsed.getClusterConnection().removeClusterTopologyListener(listener);
                }
              });
        } else {
          // if not clustered, we send a single notification to the client containing the node-id
          // where the server is connected to
          // This is done this way so Recovery discovery could also use the node-id for
          // non-clustered setups
          entry.connectionExecutor.execute(
              new Runnable() {
                public void run() {
                  String nodeId = server.getNodeID().toString();
                  Pair<TransportConfiguration, TransportConfiguration> emptyConfig =
                      new Pair<TransportConfiguration, TransportConfiguration>(null, null);
                  if (channel0.supports(PacketImpl.CLUSTER_TOPOLOGY_V2)) {
                    channel0.send(
                        new ClusterTopologyChangeMessage_V2(
                            System.currentTimeMillis(), nodeId, null, emptyConfig, true));
                  } else {
                    channel0.send(new ClusterTopologyChangeMessage(nodeId, emptyConfig, true));
                  }
                }
              });
        }
      }
    }