/** see https://blogs.oracle.com/swchan/entry/servlet_3_0_web_fragment */
  private void parseOrdering(final String orderingTag) throws XMLStreamException, IOException {
    final XMLInputFactory xif = XMLInputFactory.newInstance();
    final XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(orderingTag));
    xsr.nextTag(); // ordering tag skipped

    Boolean before = null;
    while (xsr.hasNext() && xsr.nextTag() == XMLStreamConstants.START_ELEMENT) {
      final String tagName = xsr.getLocalName();
      if ("before".equalsIgnoreCase(tagName)) {
        before = true;
      } else if ("after".equalsIgnoreCase(tagName)) {
        before = false;
      } else if ("others".equalsIgnoreCase(tagName)) {
        if (before == null) {
          throw new IllegalStateException("others tag is not nested inside before/after tag!");
        }
        // webFragmentOrderType
        if (BooleanUtils.isTrue(before)) {
          webFragmentOrderType = WebFragmentOrderType.Before;
        } else {
          webFragmentOrderType = WebFragmentOrderType.After;
        }
      } else if ("name".equalsIgnoreCase(tagName)) {
        if (before == null) {
          throw new IllegalStateException("name tag is not nested inside before/after tag!");
        }
        final String element = xsr.getElementText();
        if (BooleanUtils.isTrue(before)) {
          // webFragmentBefores
          webFragmentBefores.add(element);
        } else {
          // webFragmentAfters
          webFragmentAfters.add(element);
        }
      }
    }
  }
  public static void main(String[] args) throws SQLException, IOException {
    String host = "localhost";
    String database = null;
    String user = null;
    String password = null;
    int minTuples = 1000;
    Boolean dropIndices = null;
    Boolean dropConstraints = null;
    Boolean createIndices = null;
    boolean showHelp = false;

    for (String arg : args) {
      if (arg.startsWith("--host=")) {
        host = arg.substring(7);
      } else if (arg.startsWith("--database=")) {
        database = arg.substring(11);
      } else if (arg.startsWith("--user="******"--password="******"--min-tuples=")) {
        minTuples = Integer.valueOf(arg.substring(13));
      } else if (arg.startsWith("--drop-indices=")) {
        dropIndices = BooleanUtils.toBooleanObject(arg.substring(15));
      } else if (arg.startsWith("--drop-constraints=")) {
        dropConstraints = BooleanUtils.toBooleanObject(arg.substring(19));
      } else if (arg.startsWith("--create-indices=")) {
        createIndices = BooleanUtils.toBooleanObject(arg.substring(17));
      } else if (arg.startsWith("--help")) {
        showHelp = true;
      }
    }

    Properties props = new Properties();
    props.load(ClassLoader.getSystemResourceAsStream("default.index.properties"));
    try {
      props.load(new FileInputStream(getJarPath() + File.separator + "index.properties"));
    } catch (Exception e) {
      System.err.println("Couldn't locate index.properties -- ignoring.");
    }
    for (Entry<Object, Object> prop : props.entrySet()) {
      String indexName = prop.getKey().toString();
      String[] tableAndColumn = prop.getValue().toString().split("\\.");
      if (tableAndColumn.length == 2) {
        indexMap.put(
            indexName, new ImmutablePair<String, String>(tableAndColumn[0], tableAndColumn[1]));
      }
    }

    if (showHelp
        || database == null
        || user == null
        || password == null
        || (dropIndices == null && dropConstraints == null && createIndices == null)) {
      System.out.println(
          "Usage: java -jar AutoFKIndexDB.jar\nOptions:\n"
              + "\t--host=["
              + host
              + "]\n"
              + "\t--database=["
              + database
              + "]\n"
              + "\t--user=["
              + user
              + "]\n"
              + "\t--password=["
              + password
              + "]\n"
              + "\t--min-tuples=["
              + minTuples
              + "]\n"
              + "\t--drop-indices="
              + (dropIndices != null ? "[" + dropIndices + "]" : "true|false")
              + "\n"
              + "\t--drop-constraints="
              + (dropConstraints != null ? "[" + dropConstraints + "]" : "true|false")
              + "\n"
              + "\t--create-indices="
              + (createIndices != null ? "[" + createIndices + "]" : "true|false")
              + "\n"
              + "\t--help");
      System.exit(1);
    }

    Connection conn = null;
    try {
      String url = "jdbc:postgresql://" + host + ":5432/" + database;
      conn = DriverManager.getConnection(url, user, password);

      AutoFKIndexDB autoFK = new AutoFKIndexDB();

      if (BooleanUtils.isTrue(dropIndices)) {
        autoFK.dropForeignKeyIndices(conn);
      }

      if (BooleanUtils.isTrue(dropConstraints)) {
        autoFK.dropForeignKeyConstraints(conn);
      }

      if (BooleanUtils.isTrue(createIndices)) {
        autoFK.createForeignKeyIndices(conn, minTuples);
      }
    } catch (Exception e) {
      System.err.println("Got an exception: " + e.getMessage());
      e.printStackTrace();
    } finally {
      if (conn != null) {
        conn.close();
      }
    }
  }
  private void createWebAddressAndPut(
      String tenantId, AppManifest appManifest, @Nullable File dataDir) throws Exception {
    final Map<String, Object> scope = new HashMap<>();
    scope.put("appId", app.getId());
    scope.put("tenantId", tenantId);
    scope.put("tenantEnv", tenantConfig.getTenantEnv());
    scope.put("appDomain", tenantConfig.getAppDomain());
    scope.put("domain", appManifest.getDomain());
    scope.put("fqdn", MultiTenantConfig.getFqdn());
    scope.put("webHost", appManifest.getWebHost());
    final GeneralSysConfig sysConfig =
        Preconditions.checkNotNull(
            sysConfigs.sysConfigMap().get(tenantId),
            "Cannot get GeneralSysConfig for tenant '%s'. %s available tenants: %s",
            tenantId,
            sysConfigs.sysConfigMap().size(),
            sysConfigs.sysConfigMap().keySet());
    scope.put("sslSupported", BooleanUtils.isTrue(sysConfig.getSslSupported()));
    final OnDemandXmiLoader<WebAddress> loader;

    // TODO: due to impracticality of classpath resource packaging, hotel-specific files/templates
    // should be put in either
    // app config dir or workspace (hotel's config) dir
    final String hotelWebAddressRes = "/META-INF/template.WebAddress.xmi";
    // Workaround for Pivotal Web Services / Cloud Foundry
    final File hotelWebAddressNoLabel = new File("config/hotel.WebAddress.xmi");
    final File hotelWebAddressWithLabel =
        new File("config_" + tenantConfig.getTenantEnv() + "/hotel.WebAddress.xmi");
    final File hotelWebAddressFile =
        (hotelWebAddressNoLabel.exists() ? hotelWebAddressNoLabel : hotelWebAddressWithLabel)
            .getAbsoluteFile();
    if (dataDir != null) {
      final File webAddressFile =
          new File(
              dataDir, "model/" + tenantId + "_" + tenantConfig.getTenantEnv() + ".WebAddress.xmi");
      if (webAddressFile.exists()) {
        log.info(
            "Tenant '{}' WebAddress file '{}' exists, loading WebAddress model from file",
            tenantId,
            webAddressFile);
        loader = new OnDemandXmiLoader<>(CommonsPackage.eINSTANCE, webAddressFile, scope);
      } else {
        if (tenantConfig.getTenantSource() == TenantSource.CONFIG) {
          log.info(
              "Tenant '{}' WebAddress file '{}' does not exist, loading hotel WebAddress model from config dir: {}",
              tenantId,
              webAddressFile,
              hotelWebAddressFile);
          loader = new OnDemandXmiLoader<>(CommonsPackage.eINSTANCE, hotelWebAddressFile, scope);
        } else {
          log.info(
              "Tenant '{}' WebAddress file '{}' does not exist, loading hotel WebAddress model from classpath: {}",
              tenantId,
              webAddressFile,
              hotelWebAddressRes);
          loader =
              new OnDemandXmiLoader<>(
                  CommonsPackage.eINSTANCE,
                  MultiTenantWebAddressConfig.class,
                  hotelWebAddressRes,
                  scope);
        }
      }
    } else {
      if (tenantConfig.getTenantSource() == TenantSource.CONFIG) {
        log.info(
            "Tenant '{}' has no data dir mapping, loading hotel WebAddress model from config dir: {}",
            tenantId,
            dataDir,
            hotelWebAddressFile);
        loader = new OnDemandXmiLoader<>(CommonsPackage.eINSTANCE, hotelWebAddressFile, scope);
      } else {
        log.info(
            "Tenant '{}' has no data dir mapping, loading hotel WebAddress model from classpath: {}",
            tenantId,
            dataDir,
            hotelWebAddressRes);
        loader =
            new OnDemandXmiLoader<>(
                CommonsPackage.eINSTANCE,
                MultiTenantWebAddressConfig.class,
                hotelWebAddressRes,
                scope);
      }
    }

    //		final String tenantKey = tenant.getKey() + "_" + tenantEnv;
    //				return new StaticXmiLoader<WebAddress>(CommonsPackage.eINSTANCE,
    //						new File(dataFolder(), "model/custom.WebAddress.xmi").toString()).get();
    final WebAddress webAddress = loader.get();
    webAddressMap.put(tenantId, webAddress);
  }