final void acknowledge(
      ConnectionContext context,
      final TopicMessageStore destination,
      final String clientId,
      final String subscriptionName,
      final MessageId messageId,
      final MessageAck ack)
      throws IOException {

    if (ack.isInTransaction()) {
      if (ack.getTransactionId().isXATransaction()
          || theStore.isConcurrentStoreAndDispatchTransactions() == false) {
        destination.acknowledge(context, clientId, subscriptionName, messageId, ack);
      } else {
        Tx tx = getTx(ack.getTransactionId());
        tx.add(
            new RemoveMessageCommand(context) {
              public MessageAck getMessageAck() {
                return ack;
              }

              public Future<Object> run(ConnectionContext ctx) throws IOException {
                destination.acknowledge(ctx, clientId, subscriptionName, messageId, ack);
                return AbstractMessageStore.FUTURE;
              }
            });
      }
    } else {
      destination.acknowledge(context, clientId, subscriptionName, messageId, ack);
    }
  }
  final void removeAsyncMessage(
      ConnectionContext context, final MessageStore destination, final MessageAck ack)
      throws IOException {

    if (ack.isInTransaction()) {
      if (ack.getTransactionId().isXATransaction()
          || theStore.isConcurrentStoreAndDispatchTransactions() == false) {
        destination.removeAsyncMessage(context, ack);
      } else {
        Tx tx = getTx(ack.getTransactionId());
        tx.add(
            new RemoveMessageCommand(context) {
              @Override
              public MessageAck getMessageAck() {
                return ack;
              }

              @Override
              public Future<Object> run(ConnectionContext ctx) throws IOException {
                destination.removeMessage(ctx, ack);
                return AbstractMessageStore.FUTURE;
              }
            });
      }
    } else {
      destination.removeAsyncMessage(context, ack);
    }
  }
  Future<Object> asyncAddTopicMessage(
      ConnectionContext context, final MessageStore destination, final Message message)
      throws IOException {

    if (message.getTransactionId() != null) {
      if (message.getTransactionId().isXATransaction()
          || theStore.isConcurrentStoreAndDispatchTransactions() == false) {
        destination.addMessage(context, message);
        return AbstractMessageStore.FUTURE;
      } else {
        Tx tx = getTx(message.getTransactionId());
        tx.add(
            new AddMessageCommand(context) {
              @Override
              public Message getMessage() {
                return message;
              }

              @Override
              public Future<Object> run(ConnectionContext ctx) throws IOException {
                return destination.asyncAddTopicMessage(ctx, message);
              }
            });
        return AbstractMessageStore.FUTURE;
      }
    } else {
      return destination.asyncAddTopicMessage(context, message);
    }
  }
 /**
  * @throws IOException
  * @see org.apache.activemq.store.TransactionStore#rollback(TransactionId)
  */
 public void rollback(TransactionId txid) throws IOException {
   if (txid.isXATransaction() || theStore.isConcurrentStoreAndDispatchTransactions() == false) {
     KahaTransactionInfo info = getTransactionInfo(txid);
     theStore.store(new KahaRollbackCommand().setTransactionInfo(info), false, null, null);
     forgetRecoveredAcks(txid);
   } else {
     inflightTransactions.remove(txid);
   }
 }
 /**
  * @throws IOException
  * @see org.apache.activemq.store.TransactionStore#prepare(TransactionId)
  */
 public void prepare(TransactionId txid) throws IOException {
   KahaTransactionInfo info = getTransactionInfo(txid);
   if (txid.isXATransaction() || theStore.isConcurrentStoreAndDispatchTransactions() == false) {
     theStore.store(new KahaPrepareCommand().setTransactionInfo(info), true, null, null);
   } else {
     Tx tx = inflightTransactions.remove(txid);
     if (tx != null) {
       theStore.store(new KahaPrepareCommand().setTransactionInfo(info), true, null, null);
     }
   }
 }
  public void commit(
      TransactionId txid, boolean wasPrepared, Runnable preCommit, Runnable postCommit)
      throws IOException {
    if (txid != null) {
      if (!txid.isXATransaction() && theStore.isConcurrentStoreAndDispatchTransactions()) {
        if (preCommit != null) {
          preCommit.run();
        }
        Tx tx = inflightTransactions.remove(txid);
        if (tx != null) {
          List<Future<Object>> results = tx.commit();
          boolean doneSomething = false;
          for (Future<Object> result : results) {
            try {
              result.get();
            } catch (InterruptedException e) {
              theStore.brokerService.handleIOException(new IOException(e.getMessage()));
            } catch (ExecutionException e) {
              theStore.brokerService.handleIOException(new IOException(e.getMessage()));
            } catch (CancellationException e) {
            }
            if (!result.isCancelled()) {
              doneSomething = true;
            }
          }
          if (postCommit != null) {
            postCommit.run();
          }
          if (doneSomething) {
            KahaTransactionInfo info = getTransactionInfo(txid);
            theStore.store(new KahaCommitCommand().setTransactionInfo(info), true, null, null);
          }
        } else {
          // The Tx will be null for failed over clients - lets run their post commits
          if (postCommit != null) {
            postCommit.run();
          }
        }

      } else {
        KahaTransactionInfo info = getTransactionInfo(txid);
        theStore.store(
            new KahaCommitCommand().setTransactionInfo(info), true, preCommit, postCommit);
        forgetRecoveredAcks(txid);
      }
    } else {
      LOG.error("Null transaction passed on commit");
    }
  }