@Override
  public void addBindings(PersistedType type, String name, String... address) throws Exception {
    Pair<PersistedType, String> key = new Pair<>(type, name);

    long tx = idGenerator.generateID();

    PersistedBindings currentBindings = mapBindings.get(key);
    if (currentBindings != null) {
      jmsJournal.appendDeleteRecordTransactional(tx, currentBindings.getId());
    } else {
      currentBindings = new PersistedBindings(type, name);
    }

    mapBindings.put(key, currentBindings);

    for (String adItem : address) {
      currentBindings.addBinding(adItem);
    }

    long newId = idGenerator.generateID();

    currentBindings.setId(newId);

    jmsJournal.appendAddRecordTransactional(tx, newId, BINDING_RECORD, currentBindings);

    jmsJournal.appendCommitRecord(tx, true);
  }
  @Override
  public void deleteBindings(PersistedType type, String name, String address) throws Exception {
    Pair<PersistedType, String> key = new Pair<>(type, name);

    long tx = idGenerator.generateID();

    PersistedBindings currentBindings = mapBindings.get(key);
    if (currentBindings == null) {
      return;
    } else {
      jmsJournal.appendDeleteRecordTransactional(tx, currentBindings.getId());
    }

    currentBindings.deleteBinding(address);

    if (currentBindings.getBindings().size() == 0) {
      mapBindings.remove(key);
    } else {
      long newId = idGenerator.generateID();
      currentBindings.setId(newId);
      jmsJournal.appendAddRecordTransactional(tx, newId, BINDING_RECORD, currentBindings);
    }

    jmsJournal.appendCommitRecord(tx, true);
  }
  @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;
  }
 /** @param packet */
 private void handleCommitRollback(final ReplicationCommitMessage packet) throws Exception {
   Journal journalToUse = getJournal(packet.getJournalID());
   if (packet.isRollback()) {
     journalToUse.appendRollbackRecord(packet.getTxId(), noSync);
   } else {
     journalToUse.appendCommitRecord(packet.getTxId(), noSync);
   }
 }
  /** @param packet */
  private void handleAppendAddTXRecord(final ReplicationAddTXMessage packet) throws Exception {
    Journal journalToUse = getJournal(packet.getJournalID());

    if (packet.getOperation() == ADD_OPERATION_TYPE.UPDATE) {
      journalToUse.appendUpdateRecordTransactional(
          packet.getTxId(), packet.getId(), packet.getRecordType(), packet.getRecordData());
    } else {
      journalToUse.appendAddRecordTransactional(
          packet.getTxId(), packet.getId(), packet.getRecordType(), packet.getRecordData());
    }
  }
  /**
   * 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;
  }
 /**
  * @param packet
  * @throws Exception
  */
 private void handleAppendAddRecord(final ReplicationAddMessage packet) throws Exception {
   Journal journalToUse = getJournal(packet.getJournalID());
   if (packet.getRecord() == ADD_OPERATION_TYPE.UPDATE) {
     if (ReplicationEndpoint.trace) {
       ActiveMQServerLogger.LOGGER.trace("Endpoint appendUpdate id = " + packet.getId());
     }
     journalToUse.appendUpdateRecord(
         packet.getId(), packet.getJournalRecordType(), packet.getRecordData(), noSync);
   } else {
     if (ReplicationEndpoint.trace) {
       ActiveMQServerLogger.LOGGER.trace("Endpoint append id = " + packet.getId());
     }
     journalToUse.appendAddRecord(
         packet.getId(), packet.getJournalRecordType(), packet.getRecordData(), noSync);
   }
 }
 @Override
 public void deleteDestination(final PersistedType type, final String name) throws Exception {
   PersistedDestination destination = destinations.remove(new Pair<>(type, name));
   if (destination != null) {
     jmsJournal.appendDeleteRecord(destination.getId(), false);
   }
 }
 @Override
 public void deleteConnectionFactory(final String cfName) throws Exception {
   PersistedConnectionFactory oldCF = mapFactories.remove(cfName);
   if (oldCF != null) {
     jmsJournal.appendDeleteRecord(oldCF.getId(), false);
   }
 }
 @Override
 public void storeDestination(final PersistedDestination destination) throws Exception {
   deleteDestination(destination.getType(), destination.getName());
   long id = idGenerator.generateID();
   destination.setId(id);
   jmsJournal.appendAddRecord(id, DESTINATION_RECORD, destination, true);
   destinations.put(new Pair<>(destination.getType(), destination.getName()), destination);
 }
  @Override
  public void start() throws Exception {
    checkAndCreateDir(config.getBindingsLocation(), createDir);

    jmsJournal.start();

    started = true;
  }
 @Override
 public void storeConnectionFactory(final PersistedConnectionFactory connectionFactory)
     throws Exception {
   deleteConnectionFactory(connectionFactory.getName());
   long id = idGenerator.generateID();
   connectionFactory.setId(id);
   jmsJournal.appendAddRecord(id, CF_RECORD, connectionFactory, true);
   mapFactories.put(connectionFactory.getName(), connectionFactory);
 }
  @Override
  public void deleteBindings(PersistedType type, String name) throws Exception {
    Pair<PersistedType, String> key = new Pair<>(type, name);

    PersistedBindings currentBindings = mapBindings.remove(key);

    if (currentBindings != null) {
      jmsJournal.appendDeleteRecord(currentBindings.getId(), true);
    }
  }
  private void finishSynchronization(String liveID) throws Exception {
    for (JournalContent jc : EnumSet.allOf(JournalContent.class)) {
      Journal journal = journalsHolder.remove(jc);
      journal.synchronizationLock();
      try {
        // files should be already in place.
        filesReservedForSync.remove(jc);
        registerJournal(jc.typeByte, journal);
        journal.stop();
        journal.start();
        journal.loadSyncOnly(JournalState.SYNCING_UP_TO_DATE);
      } finally {
        journal.synchronizationUnlock();
      }
    }
    ByteBuffer buffer = ByteBuffer.allocate(4 * 1024);
    for (Entry<Long, ReplicatedLargeMessage> entry : largeMessages.entrySet()) {
      ReplicatedLargeMessage lm = entry.getValue();
      if (lm instanceof LargeServerMessageInSync) {
        LargeServerMessageInSync lmSync = (LargeServerMessageInSync) lm;
        lmSync.joinSyncedData(buffer);
      }
    }

    journalsHolder = null;
    backupQuorum.liveIDSet(liveID);
    activation.setRemoteBackupUpToDate();
    ActiveMQServerLogger.LOGGER.backupServerSynched(server);
    return;
  }
  @Override
  public void postLoad(
      Journal messageJournal,
      ResourceManager resourceManager,
      Map<SimpleString, List<Pair<byte[], Long>>> duplicateIDMap)
      throws Exception {
    for (Queue queue : queues.values()) {
      queue.resume();
    }

    if (System.getProperty("org.apache.activemq.opt.directblast") != null) {
      messageJournal.runDirectJournalBlast();
    }
  }
  @Override
  public void load() throws Exception {
    mapFactories.clear();

    List<RecordInfo> data = new ArrayList<>();

    ArrayList<PreparedTransactionInfo> list = new ArrayList<>();

    jmsJournal.load(data, list, null);

    for (RecordInfo record : data) {
      long id = record.id;

      ActiveMQBuffer buffer = ActiveMQBuffers.wrappedBuffer(record.data);

      byte rec = record.getUserRecordType();

      if (rec == CF_RECORD) {
        PersistedConnectionFactory cf = new PersistedConnectionFactory();
        cf.decode(buffer);
        cf.setId(id);
        mapFactories.put(cf.getName(), cf);
      } else if (rec == DESTINATION_RECORD) {
        PersistedDestination destination = new PersistedDestination();
        destination.decode(buffer);
        destination.setId(id);
        destinations.put(new Pair<>(destination.getType(), destination.getName()), destination);
      } else if (rec == BINDING_RECORD) {
        PersistedBindings bindings = new PersistedBindings();
        bindings.decode(buffer);
        bindings.setId(id);
        Pair<PersistedType, String> key = new Pair<>(bindings.getType(), bindings.getName());
        mapBindings.put(key, bindings);
      } else {
        throw new IllegalStateException("Invalid record type " + rec);
      }
    }
  }
 @Override
 public void stop() throws Exception {
   this.started = false;
   jmsJournal.stop();
 }
 /** @param packet */
 private void handlePrepare(final ReplicationPrepareMessage packet) throws Exception {
   Journal journalToUse = getJournal(packet.getJournalID());
   journalToUse.appendPrepareRecord(packet.getTxId(), packet.getRecordData(), noSync);
 }
  /** @param packet */
  private void handleAppendDeleteTX(final ReplicationDeleteTXMessage packet) throws Exception {
    Journal journalToUse = getJournal(packet.getJournalID());

    journalToUse.appendDeleteRecordTransactional(
        packet.getTxId(), packet.getId(), packet.getRecordData());
  }
 /** @param packet */
 private void handleAppendDelete(final ReplicationDeleteMessage packet) throws Exception {
   Journal journalToUse = getJournal(packet.getJournalID());
   journalToUse.appendDeleteRecord(packet.getId(), noSync);
 }