@Override
  protected BucketProperties convert(List<RiakPB.RpbGetBucketResp> rawResponse)
      throws ExecutionException {
    // This isn't streaming, there will only be one response.
    RiakPB.RpbBucketProps pbProps = rawResponse.get(0).getProps();
    BucketProperties props =
        new BucketProperties()
            .withNVal(pbProps.getNVal())
            .withAllowMulti(pbProps.getAllowMult())
            .withLastWriteWins(pbProps.getLastWriteWins())
            .withOldVClock(Operations.getUnsignedIntValue(pbProps.getOldVclock()))
            .withYoungVClock(Operations.getUnsignedIntValue(pbProps.getYoungVclock()))
            .withBigVClock(Operations.getUnsignedIntValue(pbProps.getBigVclock()))
            .withSmallVClock(Operations.getUnsignedIntValue(pbProps.getSmallVclock()))
            .withPr(pbProps.getPr())
            .withR(pbProps.getR())
            .withW(pbProps.getW())
            .withPw(pbProps.getPw())
            .withDw(pbProps.getDw())
            .withRw(pbProps.getRw())
            .withBasicQuorum(pbProps.getBasicQuorum())
            .withNotFoundOk(pbProps.getNotfoundOk())
            .withRiakSearchEnabled(pbProps.getSearch())
            .withChashkeyFunction(
                new Function.Builder()
                    .withModule(pbProps.getChashKeyfun().getModule().toStringUtf8())
                    .withFunction(pbProps.getChashKeyfun().getFunction().toStringUtf8())
                    .build());

    if (pbProps.hasLinkfun()) {
      props.withLinkwalkFunction(
          new Function.Builder()
              .withModule(pbProps.getLinkfun().getModule().toStringUtf8())
              .withFunction(pbProps.getLinkfun().getFunction().toStringUtf8())
              .build());
    }

    if (pbProps.hasHasPrecommit()) {
      for (Function f : parseHooks(pbProps.getPrecommitList())) {
        props.withPrecommitHook(f);
      }
    }

    if (pbProps.hasHasPostcommit()) {
      for (Function f : parseHooks(pbProps.getPostcommitList())) {
        props.withPostcommitHook(f);
      }
    }

    if (pbProps.hasYzIndex()) {
      props.withYokozunaIndex(pbProps.getYzIndex().toStringUtf8());
    }

    if (pbProps.hasBackend()) {
      props.withBackend(pbProps.getBackend().toStringUtf8());
    }

    return props;
  }
  abstract static class PropsBuilder<T extends PropsBuilder<T>> {

    protected final RiakPB.RpbBucketProps.Builder propsBuilder = RiakPB.RpbBucketProps.newBuilder();

    protected abstract T self();

    /**
     * Set the allow_multi value.
     *
     * @param allow whether to allow sibling objects to be created.
     * @return a reference to this object.
     */
    public T withAllowMulti(boolean allow) {
      propsBuilder.setAllowMult(allow);
      return self();
    }

    /**
     * Set the backend used by this bucket. Only applies when using {@code riak_kv_multi_backend} in
     * Riak.
     *
     * @param backend the name of the backend to use.
     * @return a reference to this object.
     */
    public T withBackend(String backend) {
      if (null == backend || backend.length() == 0) {
        throw new IllegalArgumentException("Backend can not be null or zero length");
      }
      propsBuilder.setBackend(ByteString.copyFromUtf8(backend));
      return self();
    }

    /**
     * Set the basic_quorum value.
     *
     * <p>The parameter controls whether a read request should return early in some fail cases. E.g.
     * If a quorum of nodes has already returned notfound/error, don't wait around for the rest.
     *
     * @param use the basic_quorum value.
     * @return a reference to this object.
     */
    public T withBasicQuorum(boolean use) {
      propsBuilder.setBasicQuorum(use);
      return self();
    }

    /**
     * Set the big_vclock value.
     *
     * @param bigVClock a long representing a epoch time value.
     * @return a reference to this object.
     * @see <a
     *     href="http://docs.basho.com/riak/latest/theory/concepts/Vector-Clocks/#Vector-Clock-Pruning">Vector
     *     Clock Pruning</a>
     */
    public T withBigVClock(Long bigVClock) {
      propsBuilder.setBigVclock(bigVClock.intValue());
      return self();
    }

    /**
     * Set the chash_keyfun value.
     *
     * @param func a Function representing the Erlang func to use.
     * @return a reference to this object.
     */
    public T withChashkeyFunction(Function func) {
      verifyErlangFunc(func);
      propsBuilder.setChashKeyfun(convertModFun(func));
      return self();
    }

    /**
     * Set the last_write_wins value. Unless you really know what you're doing, you probably do not
     * want to set this to true.
     *
     * @param wins whether to ignore vector clocks when writing.
     * @return a reference to this object.
     */
    public T withLastWriteWins(boolean wins) {
      propsBuilder.setLastWriteWins(wins);
      return self();
    }

    /**
     * Set the linkfun value.
     *
     * @param func a Function representing the Erlang func to use.
     * @return a reference to this object.
     */
    public T withLinkwalkFunction(Function func) {
      verifyErlangFunc(func);
      propsBuilder.setLinkfun(convertModFun(func));
      return self();
    }

    /**
     * Set the rw value. Individual requests (or buckets in a bucket type) can override this.
     *
     * @param rw the rw value as an integer.
     * @return a reference to this object.
     */
    public T withRw(int rw) {
      propsBuilder.setRw(rw);
      return self();
    }

    /**
     * Set the dw value. Individual requests (or buckets in a bucket type) can override this.
     *
     * @param dw the dw value as an integer.
     * @return a reference to this object.
     */
    public T withDw(int dw) {
      propsBuilder.setDw(dw);
      return self();
    }

    /**
     * Set the w value. Individual requests (or buckets in a bucket type) can override this.
     *
     * @param w the w value as an integer.
     * @return a reference to this object.
     */
    public T withW(int w) {
      propsBuilder.setW(w);
      return self();
    }

    /**
     * Set the r value. Individual requests (or buckets in a bucket type) can override this.
     *
     * @param r the r value as an integer.
     * @return a reference to this object.
     */
    public T withR(int r) {
      propsBuilder.setR(r);
      return self();
    }

    /**
     * Set the pr value. Individual requests (or buckets in a bucket type) can override this.
     *
     * @param pr the pr value as an integer.
     * @return a reference to this object.
     */
    public T withPr(int pr) {
      propsBuilder.setPr(pr);
      return self();
    }

    /**
     * Set the pw value. Individual requests (or buckets in a bucket type) can override this.
     *
     * @param pw the pw value as an integer.
     * @return a reference to this object.
     */
    public T withPw(int pw) {
      propsBuilder.setPw(pw);
      return self();
    }

    /**
     * Set the not_found_ok value. If true a vnode returning notfound for a key increments the r
     * tally. False is higher consistency, true is higher availability.
     *
     * @param ok the not_found_ok value.
     * @return a reference to this object.
     */
    public T withNotFoundOk(boolean ok) {
      propsBuilder.setNotfoundOk(ok);
      return self();
    }

    /**
     * Add a pre-commit hook. The supplied Function must be an Erlang or Named JS function.
     *
     * @param hook the Function to add.
     * @return a reference to this object.
     * @see <a href="http://docs.basho.com/riak/latest/dev/using/commit-hooks/">Using Commit
     *     Hooks</a>
     */
    public T withPrecommitHook(Function hook) {
      if (null == hook || !(!hook.isJavascript() || hook.isNamed())) {
        throw new IllegalArgumentException("Must be a named JS or Erlang function.");
      }

      propsBuilder.addPrecommit(convertHook(hook));
      return self();
    }

    /**
     * Add a post-commit hook. The supplied Function must be an Erlang or Named JS function.
     *
     * @param hook the Function to add.
     * @return a reference to this object.
     * @see <a href="http://docs.basho.com/riak/latest/dev/using/commit-hooks/">Using Commit
     *     Hooks</a>
     */
    public T withPostcommitHook(Function hook) {
      verifyErlangFunc(hook);
      propsBuilder.addPostcommit(convertHook(hook));
      return self();
    }

    /**
     * Set the old_vclock value.
     *
     * @param oldVClock an long representing a epoch time value.
     * @return a reference to this object.
     * @see <a
     *     href="http://docs.basho.com/riak/latest/theory/concepts/Vector-Clocks/#Vector-Clock-Pruning">Vector
     *     Clock Pruning</a>
     */
    public T withOldVClock(Long oldVClock) {
      propsBuilder.setOldVclock(oldVClock.intValue());
      return self();
    }

    /**
     * Set the young_vclock value.
     *
     * @param youngVClock a long representing a epoch time value.
     * @return a reference to this object.
     * @see <a
     *     href="http://docs.basho.com/riak/latest/theory/concepts/Vector-Clocks/#Vector-Clock-Pruning">Vector
     *     Clock Pruning</a>
     */
    public T withYoungVClock(Long youngVClock) {
      propsBuilder.setYoungVclock(youngVClock.intValue());
      return self();
    }

    /**
     * Set the small_vclock value.
     *
     * @param smallVClock a long representing a epoch time value.
     * @return a reference to this object.
     * @see <a
     *     href="http://docs.basho.com/riak/latest/theory/concepts/Vector-Clocks/#Vector-Clock-Pruning">Vector
     *     Clock Pruning</a>
     */
    public T withSmallVClock(Long smallVClock) {
      propsBuilder.setSmallVclock(smallVClock.intValue());
      return self();
    }

    /**
     * Set the nVal.
     *
     * @param nVal the number of replicas.
     * @return a reference to this object.
     */
    public T withNVal(int nVal) {
      if (nVal <= 0) {
        throw new IllegalArgumentException("nVal must be >= 1");
      }
      propsBuilder.setNVal(nVal);
      return self();
    }

    /**
     * Enable Legacy Riak Search. Setting this to true causes the search pre-commit hook to be
     * added.
     *
     * <p><b>Note this is only for legacy Riak (&lt; v2.0) Search support.</b>
     *
     * @param enable add/remove (true/false) the pre-commit hook for Legacy Riak Search.
     * @return a reference to this object.
     */
    public T withLegacyRiakSearchEnabled(boolean enable) {
      propsBuilder.setSearch(enable);
      return self();
    }

    /**
     * Associate a Search Index. This only applies if Yokozuna is enabled in Riak v2.0.
     *
     * @param indexName The name of the search index to use.
     * @return a reference to this object.
     */
    public T withSearchIndex(String indexName) {
      if (null == indexName || indexName.length() == 0) {
        throw new IllegalArgumentException("Index name cannot be null or zero length");
      }
      propsBuilder.setSearchIndex(ByteString.copyFromUtf8(indexName));
      return self();
    }

    private void verifyErlangFunc(Function f) {
      if (null == f || f.isJavascript()) {
        throw new IllegalArgumentException("Must be an Erlang Function.");
      }
    }

    private RiakPB.RpbModFun convertModFun(Function f) {
      return RiakPB.RpbModFun.newBuilder()
          .setModule(ByteString.copyFromUtf8(f.getModule()))
          .setFunction(ByteString.copyFromUtf8(f.getFunction()))
          .build();
    }

    private RiakPB.RpbCommitHook convertHook(Function hook) {
      RiakPB.RpbCommitHook.Builder builder = RiakPB.RpbCommitHook.newBuilder();
      RiakPB.RpbModFun.Builder mfBuilder = RiakPB.RpbModFun.newBuilder();

      if (hook.isJavascript()) {
        builder.setName(ByteString.copyFromUtf8(hook.getName()));
      } else {
        mfBuilder.setModule(ByteString.copyFromUtf8(hook.getModule()));
        mfBuilder.setFunction(ByteString.copyFromUtf8(hook.getFunction()));
        builder.setModfun(mfBuilder);
      }

      return builder.build();
    }
  }