/**
  * Reads the exported file and returns the instance ID stored therein
  *
  * @param exposedDir The exposed logging directory to look for the exported data in
  * @return The instance ID stored in the export file or -1 if the file did not exist or could
  *     not be parsed as an export file
  */
 public int getInstanceID(String exposedDir) {
   java.io.File importFile = new java.io.File(exposedDir + ".exportedData.dat");
   if (!importFile.exists()) return -1;
   prisms.util.FileSegmentizerInputStream fileStream = null;
   prisms.util.ImportStream importStream;
   java.io.InputStreamReader streamReader;
   JsonSerialReader jsr;
   try {
     fileStream = new prisms.util.FileSegmentizerInputStream(importFile);
     importStream = new prisms.util.ImportStream(fileStream);
     streamReader = new java.io.InputStreamReader(importStream);
     jsr = new JsonSerialReader(streamReader);
   } catch (java.io.IOException e) {
     log.error("Could not read data for import", e);
     if (fileStream != null)
       try {
         fileStream.close();
       } catch (java.io.IOException e2) {
       }
     return -1;
   }
   boolean success = false;
   try {
     jsr.startObject();
     if (!"exportTime".equals(jsr.getNextProperty())) {
       log.error("Expected exportTime in exported data");
       return -1;
     }
     theExportDataTime = jsr.parseLong();
     if (System.currentTimeMillis() - theExportDataTime > theDataAgeThreshold) {
       log.error(
           "Exported data is too old ("
               + prisms.util.PrismsUtils.print(theExportDataTime)
               + "). Import canceled.");
       return -1;
     }
     if (!"instance".equals(jsr.getNextProperty())) return -1;
     int ret = jsr.parseInt();
     success = true;
     theJsonReader = jsr;
     theFileReader = streamReader;
     return ret;
   } catch (Exception e) {
     log.error("Could not read exported data", e);
     return -1;
   } finally {
     if (!success)
       try {
         fileStream.close();
       } catch (java.io.IOException e) {
       }
   }
 }
    /**
     * Imports data stored in an export file
     *
     * @param apps The applications in the PRISMS environment
     * @return Whether the import succeeded
     */
    public boolean importData(PrismsApplication[] apps) {
      try {
        java.util.HashMap<String, PrismsSynchronizer> syncs;
        java.util.HashMap<String, PrismsApplication> appsByNS;
        syncs = new java.util.HashMap<String, PrismsSynchronizer>();
        appsByNS = new java.util.HashMap<String, PrismsApplication>();
        for (PrismsApplication app : apps) {
          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;
              syncs.put(((DBRecordKeeper) sync.getKeeper()).getNamespace(), sync);
              appsByNS.put(((DBRecordKeeper) sync.getKeeper()).getNamespace(), app);
            }
          }
        }
        if (!theJsonReader.goToProperty("data")) {
          log.error("No \"data\" property found in exported data");
          return false;
        }

        JsonSerialReader.StructState rootState = theJsonReader.startArray();
        JsonSerialReader.JsonParseItem item;
        StringBuilder message = new StringBuilder();
        message
            .append("Imported PRISMS data from ")
            .append(prisms.util.PrismsUtils.print(theExportDataTime))
            .append(" (");
        prisms.util.PrismsUtils.printTimeLength(
            System.currentTimeMillis() - theExportDataTime, message, false);
        message.append(" old).");
        for (item = theJsonReader.getNextItem(true, false);
            item instanceof JsonSerialReader.ObjectItem;
            item = theJsonReader.getNextItem(true, false)) {
          JsonSerialReader.StructState syncState = theJsonReader.save();
          try {
            /* The current state is now just past the beginning of one synchronizer's exported data */
            if (!"namespace".equals(theJsonReader.getNextProperty())) {
              message.append("\n\tNamespace expected in sync data set");
              continue;
            }
            String namespace = theJsonReader.parseString();
            PrismsSynchronizer sync = syncs.get(namespace);
            if (sync == null) {
              message.append("\n\tNo synchronizer loaded with namespace " + namespace);
              continue;
            }
            message.append("\n\tImporting data for namespace \"").append(namespace).append('"');
            String version;
            if (!"version".equals(theJsonReader.getNextProperty())) version = null;
            else version = theJsonReader.parseString();
            boolean preAI = ((DBRecordKeeper) sync.getKeeper()).hasAbsoluteIntegrity();
            ((DBRecordKeeper) sync.getKeeper()).setAbsoluteIntegrity(true);
            try {
              importData(appsByNS.get(namespace), sync, version, theJsonReader, message);
            } catch (Exception e) {
              message.append("\n\t\tImport failed: ").append(e);
              log.error("Could not import data for synchronizer " + namespace, e);
            } finally {
              ((DBRecordKeeper) sync.getKeeper()).setAbsoluteIntegrity(preAI);
            }
          } finally {
            theJsonReader.endObject(syncState);
          }
        }
        theJsonReader.endArray(rootState);
        int passwords = 0;
        if (theJsonReader.goToProperty("passwords")) {
          rootState = theJsonReader.startArray();
          prisms.util.LongList pwdData = new prisms.util.LongList();
          for (item = theJsonReader.getNextItem(true, false);
              item instanceof JsonSerialReader.ObjectItem;
              item = theJsonReader.getNextItem(true, false)) {
            JsonSerialReader.StructState pwdState = theJsonReader.save();
            try {
              theJsonReader.goToProperty("userName");
              String userName = theJsonReader.parseString();
              User user = apps[0].getEnvironment().getUserSource().getUser(userName);
              if (user == null) continue;
              theJsonReader.goToProperty("passwordData");
              theJsonReader.startArray();
              Number num;
              do {
                num = theJsonReader.parseNumber();
                if (num != null) pwdData.add(num.longValue());
              } while (num != null);
              theJsonReader.endArray(null);
              try {
                apps[0].getEnvironment().getUserSource().setPassword(user, pwdData.toArray(), true);
              } catch (prisms.arch.PrismsException e) {
                log.error("Could not set password for user " + userName, e);
              }
              passwords++;
              pwdData.clear();
            } finally {
              theJsonReader.endObject(pwdState);
            }
          }
          theJsonReader.endArray(rootState);
        }
        message.append("\n\tImported ").append(passwords).append(" passwords");
        log.info(message.toString());
        return true;
      } catch (Exception e) {
        log.error("Could not read or parse exported data", e);
        return false;
      } finally {
        close();
      }
    }
    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;
    }