/**
   * Reserves files (with the given fileID) in the specified journal, and places a {@link
   * FileWrapperJournal} in place to store messages while synchronization is going on.
   *
   * @param packet
   * @throws Exception
   * @return if the incoming packet indicates the synchronization is finished then return an
   *     acknowledgement otherwise return an empty response
   */
  private ReplicationResponseMessageV2 handleStartReplicationSynchronization(
      final ReplicationStartSyncMessage packet) throws Exception {
    ReplicationResponseMessageV2 replicationResponseMessage = new ReplicationResponseMessageV2();
    if (activation.isRemoteBackupUpToDate()) {
      throw ActiveMQMessageBundle.BUNDLE.replicationBackupUpToDate();
    }

    synchronized (this) {
      if (!started) return replicationResponseMessage;

      if (packet.isSynchronizationFinished()) {
        finishSynchronization(packet.getNodeID());
        replicationResponseMessage.setSynchronizationIsFinishedAcknowledgement(true);
        return replicationResponseMessage;
      }

      switch (packet.getDataType()) {
        case LargeMessages:
          for (long msgID : packet.getFileIds()) {
            createLargeMessage(msgID, true);
          }
          break;
        case JournalBindings:
        case JournalMessages:
          if (wantedFailBack && !packet.isServerToFailBack()) {
            ActiveMQServerLogger.LOGGER.autoFailBackDenied();
          }

          final JournalContent journalContent =
              SyncDataType.getJournalContentType(packet.getDataType());
          final Journal journal = journalsHolder.get(journalContent);

          if (packet.getNodeID() != null) {
            // At the start of replication, we still do not know which is the nodeID that the live
            // uses.
            // This is the point where the backup gets this information.
            backupQuorum.liveIDSet(packet.getNodeID());
          }
          Map<Long, JournalSyncFile> mapToFill = filesReservedForSync.get(journalContent);

          for (Entry<Long, JournalFile> entry :
              journal.createFilesForBackupSync(packet.getFileIds()).entrySet()) {
            mapToFill.put(entry.getKey(), new JournalSyncFile(entry.getValue()));
          }
          FileWrapperJournal syncJournal = new FileWrapperJournal(journal);
          registerJournal(journalContent.typeByte, syncJournal);
          break;
        default:
          throw ActiveMQMessageBundle.BUNDLE.replicationUnhandledDataType();
      }
    }

    return replicationResponseMessage;
  }
  @Override
  public int moveMessages(
      final int flushLimit,
      final String filterStr,
      final String otherQueueName,
      final boolean rejectDuplicates)
      throws Exception {
    checkStarted();

    clearIO();
    try {
      Filter filter = FilterImpl.createFilter(filterStr);

      Binding binding = postOffice.getBinding(new SimpleString(otherQueueName));

      if (binding == null) {
        throw ActiveMQMessageBundle.BUNDLE.noQueueFound(otherQueueName);
      }

      int retValue =
          queue.moveReferences(flushLimit, filter, binding.getAddress(), rejectDuplicates);

      return retValue;
    } finally {
      blockOnIO();
    }
  }
    public void connectionCreated(
        final ActiveMQComponent component, final Connection connection, final String protocol) {
      if (connections.putIfAbsent(connection.getID(), (NettyServerConnection) connection) != null) {
        throw ActiveMQMessageBundle.BUNDLE.connectionExists(connection.getID());
      }

      listener.connectionCreated(component, connection, protocol);
    }
  public void compareJournalInformation(final JournalLoadInformation[] journalInformation)
      throws ActiveMQException {
    if (!activation.isRemoteBackupUpToDate()) {
      throw ActiveMQMessageBundle.BUNDLE.journalsNotInSync();
    }

    if (journalLoadInformation == null
        || journalLoadInformation.length != journalInformation.length) {
      throw ActiveMQMessageBundle.BUNDLE.replicationTooManyJournals();
    }

    for (int i = 0; i < journalInformation.length; i++) {
      if (!journalInformation[i].equals(journalLoadInformation[i])) {
        ActiveMQServerLogger.LOGGER.journalcomparisonMismatch(
            journalParametersToString(journalInformation));
        throw ActiveMQMessageBundle.BUNDLE.replicationTooManyJournals();
      }
    }
  }
  @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);
  }
  /**
   * Receives 'raw' journal/page/large-message data from live server for synchronization of logs.
   *
   * @param msg
   * @throws Exception
   */
  private synchronized void handleReplicationSynchronization(ReplicationSyncFileMessage msg)
      throws Exception {
    Long id = Long.valueOf(msg.getId());
    byte[] data = msg.getData();
    SequentialFile channel1;
    switch (msg.getFileType()) {
      case LARGE_MESSAGE:
        {
          ReplicatedLargeMessage largeMessage = lookupLargeMessage(id, false);
          if (!(largeMessage instanceof LargeServerMessageInSync)) {
            ActiveMQServerLogger.LOGGER.largeMessageIncompatible();
            return;
          }
          LargeServerMessageInSync largeMessageInSync = (LargeServerMessageInSync) largeMessage;
          channel1 = largeMessageInSync.getSyncFile();
          break;
        }
      case PAGE:
        {
          Page page = getPage(msg.getPageStore(), (int) msg.getId());
          channel1 = page.getFile();
          break;
        }
      case JOURNAL:
        {
          JournalSyncFile journalSyncFile =
              filesReservedForSync.get(msg.getJournalContent()).get(id);
          FileChannel channel2 = journalSyncFile.getChannel();
          if (data == null) {
            channel2.close();
            return;
          }
          channel2.write(ByteBuffer.wrap(data));
          return;
        }
      default:
        throw ActiveMQMessageBundle.BUNDLE.replicationUnhandledFileType(msg.getFileType());
    }

    if (data == null) {
      channel1.close();
      return;
    }

    if (!channel1.isOpen()) {
      channel1.open();
    }
    channel1.writeDirect(ByteBuffer.wrap(data), true);
  }
  @Override
  public boolean changeMessagePriority(final long messageID, final int newPriority)
      throws Exception {
    checkStarted();

    clearIO();
    try {
      if (newPriority < 0 || newPriority > 9) {
        throw ActiveMQMessageBundle.BUNDLE.invalidNewPriority(newPriority);
      }
      return queue.changeReferencePriority(messageID, (byte) newPriority);
    } finally {
      blockOnIO();
    }
  }
  public QueueInfo(
      final SimpleString routingName,
      final SimpleString clusterName,
      final SimpleString address,
      final SimpleString filterString,
      final long id,
      final int distance) {
    if (routingName == null) {
      throw ActiveMQMessageBundle.BUNDLE.routeNameIsNull();
    }
    if (clusterName == null) {
      throw ActiveMQMessageBundle.BUNDLE.clusterNameIsNull();
    }
    if (address == null) {
      throw ActiveMQMessageBundle.BUNDLE.addressIsNull();
    }

    this.routingName = routingName;
    this.clusterName = clusterName;
    this.address = address;
    this.filterString = filterString;
    this.id = id;
    this.distance = distance;
  }
  @Override
  public int changeMessagesPriority(final String filterStr, final int newPriority)
      throws Exception {
    checkStarted();

    clearIO();
    try {
      if (newPriority < 0 || newPriority > 9) {
        throw ActiveMQMessageBundle.BUNDLE.invalidNewPriority(newPriority);
      }
      Filter filter = FilterImpl.createFilter(filterStr);

      return queue.changeReferencesPriority(filter, (byte) newPriority);
    } finally {
      blockOnIO();
    }
  }
  @Override
  public boolean moveMessage(
      final long messageID, final String otherQueueName, final boolean rejectDuplicates)
      throws Exception {
    checkStarted();

    clearIO();
    try {
      Binding binding = postOffice.getBinding(new SimpleString(otherQueueName));

      if (binding == null) {
        throw ActiveMQMessageBundle.BUNDLE.noQueueFound(otherQueueName);
      }

      return queue.moveReference(messageID, binding.getAddress(), rejectDuplicates);
    } finally {
      blockOnIO();
    }
  }
  @Override
  public void onNotification(Notification notification) {
    if (!(notification.getType() instanceof CoreNotificationType)) return;

    CoreNotificationType type = (CoreNotificationType) notification.getType();

    TypedProperties props = notification.getProperties();

    switch (type) {
      case BINDING_ADDED:
        {
          if (!props.containsProperty(ManagementHelper.HDR_BINDING_TYPE)) {
            throw ActiveMQMessageBundle.BUNDLE.bindingTypeNotSpecified();
          }

          Integer bindingType = props.getIntProperty(ManagementHelper.HDR_BINDING_TYPE);

          if (bindingType == BindingType.DIVERT_INDEX) {
            return;
          }

          SimpleString address = props.getSimpleStringProperty(ManagementHelper.HDR_ADDRESS);

          destinations.add(address.toString());

          break;
        }
      case BINDING_REMOVED:
        {
          SimpleString address = props.getSimpleStringProperty(ManagementHelper.HDR_ADDRESS);
          destinations.remove(address.toString());
          break;
        }
      default:
        // ignore all others
        break;
    }
  }
  @Override
  public boolean page(
      ServerMessage message,
      final Transaction tx,
      RouteContextList listCtx,
      final ReadLock managerLock)
      throws Exception {

    if (!running) {
      throw new IllegalStateException("PagingStore(" + getStoreName() + ") not initialized");
    }

    boolean full = isFull();

    if (addressFullMessagePolicy == AddressFullMessagePolicy.DROP
        || addressFullMessagePolicy == AddressFullMessagePolicy.FAIL) {
      if (full) {
        if (!printedDropMessagesWarning) {
          printedDropMessagesWarning = true;

          ActiveMQServerLogger.LOGGER.pageStoreDropMessages(storeName, sizeInBytes.get(), maxSize);
        }

        if (message.isLargeMessage()) {
          ((LargeServerMessage) message).deleteFile();
        }

        if (addressFullMessagePolicy == AddressFullMessagePolicy.FAIL) {
          throw ActiveMQMessageBundle.BUNDLE.addressIsFull(address.toString());
        }

        // Address is full, we just pretend we are paging, and drop the data
        return true;
      } else {
        return false;
      }
    } else if (addressFullMessagePolicy == AddressFullMessagePolicy.BLOCK) {
      return false;
    }

    // We need to ensure a read lock, as depage could change the paging state
    lock.readLock().lock();

    try {
      // First check done concurrently, to avoid synchronization and increase throughput
      if (!paging) {
        return false;
      }
    } finally {
      lock.readLock().unlock();
    }

    managerLock.lock();
    try {
      lock.writeLock().lock();

      try {
        if (!paging) {
          return false;
        }

        if (!message.isDurable()) {
          // The address should never be transient when paging (even for non-persistent messages
          // when paging)
          // This will force everything to be persisted
          message.forceAddress(address);
        }

        final long transactionID = tx == null ? -1 : tx.getID();
        PagedMessage pagedMessage =
            new PagedMessageImpl(message, routeQueues(tx, listCtx), transactionID);

        if (message.isLargeMessage()) {
          ((LargeServerMessage) message).setPaged();
        }

        int bytesToWrite = pagedMessage.getEncodeSize() + Page.SIZE_RECORD;

        if (currentPageSize.addAndGet(bytesToWrite) > pageSize
            && currentPage.getNumberOfMessages() > 0) {
          // Make sure nothing is currently validating or using currentPage
          openNewPage();
          currentPageSize.addAndGet(bytesToWrite);
        }

        if (tx != null) {
          installPageTransaction(tx, listCtx);
        }

        // the apply counter will make sure we write a record on journal
        // especially on the case for non transactional sends and paging
        // doing this will give us a possibility of recovering the page counters
        applyPageCounters(tx, getCurrentPage(), listCtx);

        currentPage.write(pagedMessage);

        if (tx == null && syncNonTransactional && message.isDurable()) {
          sync();
        }

        if (isTrace) {
          ActiveMQServerLogger.LOGGER.trace(
              "Paging message "
                  + pagedMessage
                  + " on pageStore "
                  + this.getStoreName()
                  + " pageId="
                  + currentPage.getPageId());
        }

        return true;
      } finally {
        lock.writeLock().unlock();
      }
    } finally {
      managerLock.unlock();
    }
  }
  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);
    }
  }