public void rotateLogicalLogs() {
   for (XaDataSource dataSource : dataSources.values()) {
     try {
       dataSource.rotateLogicalLog();
     } catch (IOException e) {
       msgLog.logMessage("Couldn't rotate logical log for " + dataSource.getName(), e);
     }
   }
 }
  /** Recover all datasources */
  public void recover(Iterator<List<TxLog.Record>> knownDanglingRecordList) {
    // contains NonCompletedTransaction that needs to be committed
    List<NonCompletedTransaction> commitList = new ArrayList<NonCompletedTransaction>();

    // contains Xids that should be rolledback
    final List<Xid> rollbackList = new LinkedList<Xid>();

    // key = Resource(branchId) value = XAResource
    final Map<Resource, XaDataSource> resourceMap = new HashMap<Resource, XaDataSource>();
    buildRecoveryInfo(commitList, rollbackList, resourceMap, knownDanglingRecordList);
    // invoke recover on all xa resources found
    final List<Xid> recoveredXidsList = new LinkedList<Xid>();

    try {

      for (XaDataSource xaDataSource : dataSources.values()) {
        XAResource xaRes = xaDataSource.getXaConnection().getXaResource();
        Xid xids[] = xaRes.recover(XAResource.TMNOFLAGS);

        for (Xid xid : xids) {
          if (XidImpl.isThisTm(xid.getGlobalTransactionId())) {
            // linear search
            if (rollbackList.contains(xid)) {
              msgLog.logMessage("TM: Found pre commit " + xid + " rolling back ... ", true);
              rollbackList.remove(xid);
              xaRes.rollback(xid);
            } else {
              Resource resource = new Resource(xid.getBranchQualifier());
              if (!resourceMap.containsKey(resource)) {
                resourceMap.put(resource, xaDataSource);
              }
              recoveredXidsList.add(xid);
            }
          } else {
            msgLog.warn("Unknown xid: " + xid);
          }
        }
      }

      // sort the commit list after sequence number
      Collections.sort(commitList);

      // go through and commit
      for (NonCompletedTransaction nct : commitList) {
        int seq = nct.getSequenceNumber();
        Xid xids[] = nct.getXids();
        msgLog.debug("Marked as commit tx-seq[" + seq + "] branch length: " + xids.length);
        for (Xid xid : xids) {
          if (!recoveredXidsList.contains(xid)) {
            msgLog.debug(
                "Tx-seq["
                    + seq
                    + "]["
                    + xid
                    + "] not found in recovered xid list, "
                    + "assuming already committed");
            continue;
          }
          recoveredXidsList.remove(xid);
          Resource resource = new Resource(xid.getBranchQualifier());
          if (!resourceMap.containsKey(resource)) {
            final TransactionFailureException ex =
                new TransactionFailureException("Couldn't find XAResource for " + xid);
            throw logAndReturn("TM: recovery error", ex);
          }
          msgLog.debug("TM: Committing tx " + xid);
          resourceMap.get(resource).getXaConnection().getXaResource().commit(xid, false);
        }
      }

      // rollback the rest
      for (Xid xid : recoveredXidsList) {
        Resource resource = new Resource(xid.getBranchQualifier());
        if (!resourceMap.containsKey(resource)) {
          final TransactionFailureException ex =
              new TransactionFailureException("Couldn't find XAResource for " + xid);
          throw logAndReturn("TM: recovery error", ex);
        }
        msgLog.debug("TM: no match found for " + xid + " removing");
        resourceMap.get(resource).getXaConnection().getXaResource().rollback(xid);
      }
      if (rollbackList.size() > 0) {
        msgLog.debug(
            "TxLog contained unresolved "
                + "xids that needed rollback. They couldn't be matched to "
                + "any of the XAResources recover list. "
                + "Assuming "
                + rollbackList.size()
                + " transactions already rolled back.");
      }

      // Rotate the logs of the participated data sources, making sure that
      // done-records are written so that even if the tm log gets truncated,
      // which it will be after this recovery, that transaction information
      // doesn't get lost.
      for (XaDataSource participant : MapUtil.reverse(resourceMap).keySet()) {
        participant.rotateLogicalLog();
      }
    } catch (IOException | XAException e) {
      throw logAndReturn(
          "TM: recovery failed", new TransactionFailureException("Recovery failed.", e));
    }
  }