public ClientService(final Properties properties) {

    // show the copyright banner during statup.
    Banner.banner();

    this.properties = (Properties) properties.clone();
  }
  /**
   * Prepare a journal file for use by an {@link IBufferStrategy}. The file will be created if
   * necessary as permitted and instructed by the specified {@link Options}. The root blocks will be
   * created or read from the file and verified. A variety of interesting properties are set on the
   * returned {@link FileMetadata} object.
   */
  public static FileMetadata createInstance(
      final Properties properties, final boolean isScaleout, final long quorumToken) {

    Banner.banner();

    final BufferMode bufferMode =
        BufferMode.valueOf(
            getProperty(properties, Options.BUFFER_MODE, Options.DEFAULT_BUFFER_MODE));

    boolean create = Boolean.parseBoolean(Options.DEFAULT_CREATE);

    boolean isEmptyFile = false;

    final boolean createTempFile =
        Boolean.parseBoolean(
            getProperty(properties, Options.CREATE_TEMP_FILE, Options.DEFAULT_CREATE_TEMP_FILE));

    final File tmpDir =
        new File(getProperty(properties, Options.TMP_DIR, System.getProperty("java.io.tmpdir")));

    if (createTempFile) {

      create = false;

      isEmptyFile = true;

      if (!tmpDir.exists()) {

        if (!tmpDir.mkdirs()) {

          throw new RuntimeException("Could not create directory: " + tmpDir.getAbsolutePath());
        }
      }

      if (!tmpDir.isDirectory()) {

        throw new RuntimeException("Not a directory: " + tmpDir.getAbsolutePath());
      }
    }

    // The backing file.
    final File file;
    {

      // The name of the backing file iff explicitly specified.
      String fname = getProperty(properties, Options.FILE, null);

      if (createTempFile) {

        if (fname != null) {

          throw new RuntimeException(
              "Can not use option '"
                  + Options.CREATE_TEMP_FILE
                  + "' with option '"
                  + Options.FILE
                  + "'");
        }

        try {

          fname =
              File.createTempFile("bigdata-" + bufferMode + "-", Options.JNL, tmpDir).toString();

        } catch (IOException ex) {

          throw new RuntimeException(ex);
        }

      } else if (fname == null) {

        throw new RuntimeException("Required property: '" + Options.FILE + "'");
      }

      file = new File(fname);
    }

    if (file.exists() && file.length() == 0) {

      /*
       * The file exists but is empty. This is what happens if you use
       * File.createTempFile(...) to generate the name of the file to be
       * opened. As a special exception, the journal will be initialized
       * on the empty file.
       *
       * Note: this will also be the case if a new file is opened but
       * never written to.
       */

      isEmptyFile = true;

    } else {

      /*
       * Make sure that the parent directory (if any) exists.
       */

      final File parent = file.getParentFile();

      if (parent != null && !parent.exists()) {

        // create the parent directory.

        if (!parent.mkdirs()) {

          throw new RuntimeException("Could not create parent directory: " + parent);
        }
      }
    }

    final boolean useDirectBuffers =
        Boolean.parseBoolean(
            getProperty(
                properties, Options.USE_DIRECT_BUFFERS, Options.DEFAULT_USE_DIRECT_BUFFERS));

    final boolean readOnly =
        Boolean.parseBoolean(getProperty(properties, Options.READ_ONLY, Options.DEFAULT_READ_ONLY));

    final ForceEnum forceWrites =
        ForceEnum.parse(
            getProperty(properties, Options.FORCE_WRITES, Options.DEFAULT_FORCE_WRITES));

    final boolean deleteOnExit =
        Boolean.parseBoolean(
            getProperty(properties, Options.DELETE_ON_EXIT, Options.DEFAULT_DELETE_ON_EXIT));

    final boolean writeCacheEnabled =
        Boolean.parseBoolean(
            getProperty(
                properties, Options.WRITE_CACHE_ENABLED, Options.DEFAULT_WRITE_CACHE_ENABLED));

    final int writeCacheBufferCount =
        Integer.parseInt(
            getProperty(
                properties,
                Options.WRITE_CACHE_BUFFER_COUNT,
                Options.DEFAULT_WRITE_CACHE_BUFFER_COUNT));

    final boolean validateChecksum =
        Boolean.parseBoolean(
            getProperty(properties, Options.VALIDATE_CHECKSUM, Options.DEFAULT_VALIDATE_CHECKSUM));

    final boolean alternateRootBlock =
        Boolean.parseBoolean(getProperty(properties, Options.ALTERNATE_ROOT_BLOCK, "false"));

    final boolean ignoreBadRootBlock =
        Boolean.parseBoolean(getProperty(properties, Options.IGNORE_BAD_ROOT_BLOCK, "false"));

    if (alternateRootBlock && !readOnly) {

      log.warn(
          "*** Using the alternate root block: " + "Data will be lost on the next commit! ***");
    }

    if (file.exists() && !isEmptyFile) {

      /*
       * Code path when the file exists and is non-empty.
       */

      return new FileMetadata(
          file,
          useDirectBuffers,
          readOnly,
          forceWrites,
          writeCacheEnabled,
          writeCacheBufferCount,
          validateChecksum,
          alternateRootBlock,
          ignoreBadRootBlock,
          properties);

    } else {

      /*
       * Code path with the file does not exist or exists but is empty.
       *
       * Note: Among other things, this code path covers the create of an
       * AbstractJournal when the caller has supplied a filename obtained
       * from the temporary file creation mechanisms.
       */

      final long initialExtent =
          getProperty(
              properties,
              Options.INITIAL_EXTENT,
              Options.DEFAULT_INITIAL_EXTENT,
              new LongRangeValidator(Options.minimumInitialExtent, Long.MAX_VALUE));

      final long maximumExtent =
          getProperty(
              properties,
              Options.MAXIMUM_EXTENT,
              Options.DEFAULT_MAXIMUM_EXTENT,
              new LongRangeValidator(initialExtent, Long.MAX_VALUE));

      /*
       * Note: The caller SHOULD specify an explicit [createTime] when
       * its value is critical. The default assigned here does NOT
       * attempt to use a clock that is consistent with the commit
       * protocol or even a clock that assigns unique timestamps.
       */
      final long createTime =
          Long.parseLong(
              getProperty(properties, Options.CREATE_TIME, "" + System.currentTimeMillis()));

      final int offsetBits =
          getProperty(
              properties,
              Options.OFFSET_BITS,
              Integer.toString(
                  (!isScaleout
                      ? WormAddressManager.SCALE_UP_OFFSET_BITS
                      : WormAddressManager.SCALE_OUT_OFFSET_BITS)),
              new IntegerRangeValidator(
                  WormAddressManager.MIN_OFFSET_BITS, WormAddressManager.MAX_OFFSET_BITS));

      return new FileMetadata(
          file,
          bufferMode,
          useDirectBuffers,
          initialExtent,
          maximumExtent,
          create,
          isEmptyFile,
          deleteOnExit,
          readOnly,
          forceWrites,
          offsetBits,
          writeCacheEnabled,
          writeCacheBufferCount,
          validateChecksum,
          createTime,
          quorumToken,
          alternateRootBlock,
          properties);
    }
  }