void exportData(
        prisms.ui.UI ui,
        PrismsSynchronizer sync,
        JsonStreamWriter jsw,
        java.util.HashSet<String> namespaces,
        prisms.ui.UI.DefaultProgressInformer pi,
        boolean global)
        throws java.io.IOException, PrismsRecordException {
      DBRecordKeeper keeper = (DBRecordKeeper) sync.getKeeper();
      String namespace = keeper.getNamespace();
      if (namespaces.contains(namespace)) return;
      namespaces.add(namespace);
      for (PrismsSynchronizer depend : sync.getDepends())
        exportData(ui, depend, jsw, namespaces, pi, global);
      jsw.startObject();
      jsw.startProperty("namespace");
      jsw.writeString(namespace);
      jsw.startProperty("version");
      String v = null;
      SynchronizeImpl impl = null;
      for (SynchronizeImpl imp : sync.getImpls())
        if (prisms.arch.PrismsConfig.compareVersions(imp.getVersion(), v) > 0) {
          impl = imp;
          v = impl.getVersion();
        }

      jsw.writeString(v);
      SyncRecord syncRecord =
          new SyncRecord(
              new PrismsCenter("Export"),
              SyncRecord.Type.AUTOMATIC,
              System.currentTimeMillis(),
              false);
      pi.setProgressText("Exporting " + namespace + " items");
      ui.startTimedTask(pi);
      PrismsSynchronizer.SyncTransaction syncTrans = sync.transact(v, false, syncRecord, false, pi);
      PrismsSynchronizer.PS2ItemWriter itemWriter =
          new PrismsSynchronizer.PS2ItemWriter(
              syncTrans, jsw, new prisms.records.LatestCenterChange[0]);

      int[] centerIDs = global ? keeper.getAllCenterIDs() : new int[] {keeper.getCenterID()};
      SynchronizeImpl.ItemIterator iter = impl.getAllItems(centerIDs, syncRecord.getCenter());
      jsw.startProperty("items");
      jsw.startArray();
      while (iter.hasNext()) itemWriter.writeItem(iter.next());
      for (PrismsCenter center : keeper.getCenters()) itemWriter.writeItem(center);
      if (keeper.getAutoPurger() != null) itemWriter.writeItem(keeper.getAutoPurger());
      jsw.endArray();

      pi.setProgressText("Exporting " + namespace + " changes");
      prisms.util.Sorter<RecordKeeper.ChangeField> sorter;
      sorter = new prisms.util.Sorter<RecordKeeper.ChangeField>();
      sorter.addSort(RecordKeeper.ChangeField.CHANGE_TIME, true);
      prisms.util.Search changeSearch =
          global
              ? null
              : new prisms.records.ChangeSearch.IDRange(
                      Long.valueOf(IDGenerator.getMinID(keeper.getCenterID())),
                      Long.valueOf(IDGenerator.getMaxID(keeper.getCenterID())))
                  .and(new prisms.records.ChangeSearch.LocalOnlySearch(null));
      prisms.util.LongList changeIDs =
          new prisms.util.LongList(keeper.search(changeSearch, sorter));
      long[] batch = new long[changeIDs.size() < 200 ? changeIDs.size() : 200];
      jsw.startProperty("changes");
      jsw.startArray();
      for (int i = 0; i < changeIDs.size(); i += batch.length) {
        if (changeIDs.size() - i < batch.length) batch = new long[changeIDs.size() - i];
        changeIDs.arrayCopy(i, batch, 0, batch.length);
        ChangeRecord[] changes = keeper.getItems(batch);
        for (ChangeRecord change : changes)
          if (change != null
              && (change.type.subjectType instanceof PrismsChange || impl.shouldSend(change)))
            itemWriter.writeChange(change, false);
      }
      jsw.endArray();

      if (global) {
        pi.setProgressText("Exporting " + namespace + " sync records");
        jsw.startProperty("syncRecords");
        jsw.startArray();
        for (PrismsCenter center : keeper.getCenters()) exportSyncRecords(center, keeper, jsw);
        jsw.endArray();

        pi.setProgressText("Exporting " + namespace + " metadata");
        jsw.startProperty("latestChanges");
        jsw.startArray();
        prisms.util.IntList allCenterIDs = new prisms.util.IntList(keeper.getAllCenterIDs());
        for (int i = 0; i < allCenterIDs.size(); i++)
          for (int j = 0; j < allCenterIDs.size(); j++) {
            long changeTime = keeper.getLatestChange(allCenterIDs.get(i), allCenterIDs.get(j));
            if (changeTime > 0) {
              jsw.startObject();
              jsw.startProperty("center");
              jsw.writeNumber(Integer.valueOf(allCenterIDs.get(i)));
              jsw.startProperty("subjectCenter");
              jsw.writeNumber(Integer.valueOf(allCenterIDs.get(i)));
              jsw.startProperty("latestChange");
              jsw.writeNumber(Long.valueOf(changeTime));
              jsw.endObject();
            }
          }
        jsw.endArray();
      }
      jsw.endObject();
    }
    /**
     * Exports all synchronize-enabled PRISMS data to a file to be imported later
     *
     * @param ui The user interface to communicate with
     * @param global Whether to export ALL data available to synchronization instead of just the
     *     local data and local modifications
     * @return Whether the export succeeded
     */
    public boolean exportData(prisms.ui.UI ui, boolean global) {
      prisms.ui.UI.DefaultProgressInformer pi = new prisms.ui.UI.DefaultProgressInformer();
      pi.setProgressText("Configuring PRISMS applications");
      ui.startTimedTask(pi);
      try {
        for (PrismsApplication app : theApps) {
          if (!app.isConfigured()) {
            pi.setProgressText("Configuring " + app.getName());
            // Configure the application. Can't be done except by the PrismsServer
            prisms.util.PrismsServiceConnector conn =
                new prisms.util.PrismsServiceConnector(
                    theApps[0].getEnvironment().getIDs().getLocalInstance().location,
                    app.getName(),
                    app.getClients()[0].getName(),
                    "System");
            try {
              conn.getConnector()
                  .setTrustManager(
                      new javax.net.ssl.X509TrustManager() {
                        public void checkClientTrusted(
                            java.security.cert.X509Certificate[] chain, String authType)
                            throws java.security.cert.CertificateException {}

                        public void checkServerTrusted(
                            java.security.cert.X509Certificate[] chain, String authType)
                            throws java.security.cert.CertificateException {}

                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                          return null;
                        }
                      });
            } catch (java.security.GeneralSecurityException e) {
              log.error("Could not set trust manager for service connector", e);
            }
            conn.setUserSource(theApps[0].getEnvironment().getUserSource());
            log.debug("Application " + app.getName() + " is not yet configured. Configuring.");
            try {
              conn.init();
              conn.logout(true);
            } catch (java.io.IOException e) {
              if (!app.isConfigured()) {
                ui.error(
                    "Could not configure application "
                        + app.getName()
                        + ". Export data cannot proceed.");
                log.error("Could not configure application " + app.getName(), e);
                return false;
              }
            }
            if (!app.isConfigured()) {
              ui.error(
                  "Could not configure application "
                      + app.getName()
                      + ". Export data cannot proceed.");
              log.error("Could not configure application " + app.getName());
              return false;
            }
          }
        }
        return exportData(ui, pi, global);
      } finally {
        pi.setDone();
      }
    }
    boolean exportData(prisms.ui.UI ui, prisms.ui.UI.DefaultProgressInformer pi, boolean global) {
      java.io.File exportFile =
          new java.io.File(
              theApps[0].getEnvironment().getLogger().getExposedDir() + ".exportedData.dat");
      if (exportFile.exists() && !prisms.util.FileSegmentizerOutputStream.delete(exportFile)) {
        ui.error(
            "Could not delete data exported on "
                + prisms.util.PrismsUtils.print(exportFile.lastModified()));
        log.error(
            "Could not delete data exported on "
                + prisms.util.PrismsUtils.print(exportFile.lastModified()));
        return false;
      }
      prisms.util.FileSegmentizerOutputStream fileStream = null;
      prisms.util.ExportStream exportStream;
      java.io.OutputStreamWriter streamWriter;
      JsonStreamWriter jsw;
      try {
        fileStream = new prisms.util.FileSegmentizerOutputStream(exportFile);
        exportStream = new prisms.util.ExportStream(fileStream);
        streamWriter = new java.io.OutputStreamWriter(exportStream);
        // streamWriter = new java.io.OutputStreamWriter(fileStream);
        jsw = new JsonStreamWriter(streamWriter);
      } catch (java.io.IOException e) {
        ui.error("Could not write data for export: " + e);
        log.error("Could not write data for export", e);
        if (fileStream != null)
          try {
            fileStream.close();
          } catch (java.io.IOException e2) {
          }
        prisms.util.FileSegmentizerOutputStream.delete(exportFile);
        return false;
      }
      boolean success = false;
      try {
        jsw.startObject();

        jsw.startProperty("exportTime");
        jsw.writeNumber(Long.valueOf(System.currentTimeMillis()));

        jsw.startProperty("instance");
        jsw.writeNumber(Integer.valueOf(theApps[0].getEnvironment().getIDs().getCenterID()));

        jsw.startProperty("hashing");
        Hashing hashing = theApps[0].getEnvironment().getUserSource().getHashing();
        jsw.startObject();
        jsw.startProperty("multiples");
        jsw.startArray();
        for (long h : hashing.getPrimaryMultiples()) jsw.writeNumber(Long.valueOf(h));
        jsw.endArray();
        jsw.startProperty("modulos");
        jsw.startArray();
        for (long h : hashing.getPrimaryModulos()) jsw.writeNumber(Long.valueOf(h));
        jsw.endArray();
        jsw.endObject();

        jsw.startProperty("data");
        jsw.startArray();
        java.util.HashSet<String> namespaces = new java.util.HashSet<String>();
        for (PrismsApplication app : theApps) {
          for (prisms.arch.event.PrismsProperty<?> property : app.getGlobalProperties()) {
            if (PrismsSynchronizer.class.isAssignableFrom(property.getType())) {
              PrismsSynchronizer sync = (PrismsSynchronizer) app.getGlobalProperty(property);
              if (sync == null || !(sync.getKeeper() instanceof DBRecordKeeper)) continue;
              exportData(ui, sync, jsw, namespaces, pi, global);
            }
          }
        }
        jsw.endArray();
        jsw.startProperty("passwords");
        jsw.startArray();
        for (User user : theApps[0].getEnvironment().getUserSource().getActiveUsers()) {
          prisms.arch.ds.UserSource.Password pwd =
              theApps[0].getEnvironment().getUserSource().getPassword(user);
          if (pwd == null) continue;
          jsw.startObject();
          jsw.startProperty("userName");
          jsw.writeString(user.getName());
          jsw.startProperty("passwordData");
          jsw.startArray();
          for (long h : pwd.hash) jsw.writeNumber(Long.valueOf(h));
          jsw.endArray();
          jsw.endObject();
        }
        jsw.endArray();
        jsw.endObject();
        jsw.close();
        streamWriter.close();
        fileStream.close();
        success = true;
        ui.info(
            "Data has been exported. On server restart after rebuild,"
                + " local data will be imported.");
        log.info(
            "Instance "
                + theApps[0].getEnvironment().getIDs().getLocalInstance().location
                + ": Data has been exported to "
                + exportFile.getCanonicalPath());
      } catch (java.io.IOException e) {
        ui.error("Data export failed: " + e);
        log.error("Data export failed", e);
      } catch (prisms.records.PrismsRecordException e) {
        ui.error("Data export failed: " + e);
        log.error("Data export failed", e);
      } finally {
        if (!success) {
          try {
            fileStream.close();
          } catch (java.io.IOException e2) {
          }
          prisms.util.FileSegmentizerOutputStream.delete(exportFile);
        }
      }
      return success;
    }