Repository: GoogleCloudPlatform/cloud-bigtable-client
Pull request #1879 (2018-07-13 13:07:28)

Fixing deprecation and other warnings.


Patch:
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/config/BigtableOptions.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/config/BigtableOptions.java
index 6b0c1ab53..53b2ecee0 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/config/BigtableOptions.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/config/BigtableOptions.java
@@ -65,7 +65,7 @@ public class BigtableOptions implements Serializable, Cloneable {
     // 20 Channels seemed to work well on a 4 CPU machine, and this ratio seems to scale well for
     // higher CPU machines. Use no more than 250 Channels by default.
     int availableProcessors = Runtime.getRuntime().availableProcessors();
-    return (int) Math.min(250, Math.max(1, availableProcessors * 4));
+    return Math.min(250, Math.max(1, availableProcessors * 4));
   }
 
   public static BigtableOptions getDefaultOptions() {
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/config/CredentialFactory.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/config/CredentialFactory.java
index 447c55321..decc5a965 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/config/CredentialFactory.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/config/CredentialFactory.java
@@ -135,21 +135,6 @@ public class CredentialFactory {
     }
   }
 
-  /**
-   * Initializes OAuth2 credential using preconfigured ServiceAccount settings on the local
-   * Google Compute Engine VM. See:
-   * <a href="https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances"
-   * >Creating and Enabling Service Accounts for Instances</a>.
-   *
-   * @return a {@link com.google.auth.Credentials} object.
-   * @throws java.io.IOException if any.
-   * @throws java.security.GeneralSecurityException if any.
-   */
-  public static Credentials getCredentialFromMetadataServiceAccount()
-      throws IOException, GeneralSecurityException {
-    return new ComputeEngineCredentials(getHttpTransportFactory());
-  }
-
   /**
    * Initializes OAuth2 credential from a private keyfile, as described in
    * <a href="https://developers.google.com/api-client-library/java/google-api-java-client/oauth2#service_accounts"
@@ -188,8 +173,15 @@ public class CredentialFactory {
     PrivateKey privateKey =
         SecurityUtils.loadPrivateKeyFromKeyStore(SecurityUtils.getPkcs12KeyStore(),
           new FileInputStream(privateKeyFile), "notasecret", "privatekey", "notasecret");
-    return new ServiceAccountCredentials(clientId, serviceAccountEmail, privateKey, privateKeyId,
-        scopes, getHttpTransportFactory(), null /* tokenServerUri */);
+    return ServiceAccountCredentials
+            .newBuilder()
+            .setClientId(clientId)
+            .setClientEmail(serviceAccountEmail)
+            .setPrivateKey(privateKey)
+            .setPrivateKeyId(privateKeyId)
+            .setScopes(scopes)
+            .setHttpTransportFactory(getHttpTransportFactory())
+            .build();
   }
 
   /**
@@ -202,10 +194,8 @@ public class CredentialFactory {
    *
    * @return a {@link com.google.auth.Credentials} object.
    * @throws java.io.IOException if any.
-   * @throws java.security.GeneralSecurityException if any.
    */
-  public static Credentials getApplicationDefaultCredential() throws IOException,
-      GeneralSecurityException {
+  public static Credentials getApplicationDefaultCredential() throws IOException {
     return GoogleCredentials.getApplicationDefault(getHttpTransportFactory())
         .createScoped(CLOUD_BIGTABLE_ALL_SCOPES);
   }
@@ -216,10 +206,8 @@ public class CredentialFactory {
    * @param inputStream a {@link java.io.InputStream} object.
    * @return a {@link com.google.auth.Credentials} object.
    * @throws java.io.IOException if any.
-   * @throws java.security.GeneralSecurityException if any.
    */
-  public static Credentials getInputStreamCredential(InputStream inputStream) throws IOException,
-      GeneralSecurityException {
+  public static Credentials getInputStreamCredential(InputStream inputStream) throws IOException {
     return GoogleCredentials.fromStream(inputStream, getHttpTransportFactory())
         .createScoped(CLOUD_BIGTABLE_ALL_SCOPES);
   }
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/config/RetryOptions.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/config/RetryOptions.java
index af5dd1fec..ed311c5d8 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/config/RetryOptions.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/config/RetryOptions.java
@@ -343,28 +343,6 @@ public class RetryOptions implements Serializable {
   public boolean isRetryable(Status.Code code) {
     return statusToRetryOn.contains(code);
   }
-
-  /**
-   * <p>createBackoff.</p>
-   *
-   * @return a {@link com.google.api.client.util.BackOff} object.
-   */
-  public BackOff createBackoff() {
-    return createBackoffBuilder().build();
-  }
-
-  /**
-   * <p>createBackoffBuilder.</p>
-   *
-   * @return a {@link com.google.api.client.util.ExponentialBackOff.Builder} object.
-   */
-  @VisibleForTesting
-  protected ExponentialBackOff.Builder createBackoffBuilder() {
-    return new ExponentialBackOff.Builder()
-        .setInitialIntervalMillis(getInitialBackoffMillis())
-        .setMaxElapsedTimeMillis(getMaxElapsedBackoffMillis())
-        .setMultiplier(getBackoffMultiplier());
-  }
   
   /** {@inheritDoc} */
   @Override
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSession.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSession.java
index 345d67e9b..40070abec 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSession.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSession.java
@@ -39,6 +39,7 @@ import com.google.cloud.bigtable.grpc.io.Watchdog;
 import com.google.cloud.bigtable.grpc.io.WatchdogInterceptor;
 import com.google.cloud.bigtable.metrics.BigtableClientMetrics;
 import com.google.cloud.bigtable.metrics.BigtableClientMetrics.MetricLevel;
+import com.google.cloud.bigtable.util.ThreadUtil;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
@@ -48,7 +49,6 @@ import io.grpc.ClientInterceptors;
 import io.grpc.ManagedChannel;
 import io.grpc.ManagedChannelBuilder;
 import io.grpc.MethodDescriptor;
-import io.grpc.internal.GrpcUtil;
 import java.io.Closeable;
 import java.io.IOException;
 import java.net.InetAddress;
@@ -103,7 +103,7 @@ public class BigtableSession implements Closeable {
   private static void performWarmup() {
     // Initialize some core dependencies in parallel.  This can speed up startup by 150+ ms.
     ExecutorService connectionStartupExecutor = Executors
-        .newCachedThreadPool(GrpcUtil.getThreadFactory("BigtableSession-startup-%d", true));
+        .newCachedThreadPool(ThreadUtil.getThreadFactory("BigtableSession-startup-%d", true));
 
     connectionStartupExecutor.execute(new Runnable() {
       @Override
@@ -504,7 +504,10 @@ public class BigtableSession implements Closeable {
         .forAddress(host, options.getPort());
 
     if (options.usePlaintextNegotiation()) {
-      builder.usePlaintext(true);
+      // NOTE: usePlaintext(true) is deprecated in newer versions of grpc (1.11.0).
+      //       usePlantxext() is the preferred approach, but won't work with older versions.
+      //       This means that plaintext negotiation can't be used with Beam.
+      builder.usePlaintext();
     }
 
     return builder
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSessionSharedThreadPools.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSessionSharedThreadPools.java
index f1afeada1..b7dd57a8a 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSessionSharedThreadPools.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSessionSharedThreadPools.java
@@ -15,12 +15,12 @@
  */
 package com.google.cloud.bigtable.grpc;
 
+import com.google.cloud.bigtable.util.ThreadUtil;
+
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 
-import io.grpc.internal.GrpcUtil;
-
 /**
  * This class contains executors and other thread pool related resources that can be reused across a
  * few {@link com.google.cloud.bigtable.grpc.BigtableSession}s.  All executors are automatically expand if there is higher use.
@@ -66,7 +66,7 @@ public class BigtableSessionSharedThreadPools {
   public synchronized ExecutorService getBatchThreadPool() {
     if (batchThreadPool == null) {
       batchThreadPool = Executors
-          .newCachedThreadPool(GrpcUtil.getThreadFactory(BATCH_POOL_THREAD_NAME_PATTERN, true));
+          .newCachedThreadPool(ThreadUtil.getThreadFactory(BATCH_POOL_THREAD_NAME_PATTERN, true));
     }
     return batchThreadPool;
   }
@@ -79,7 +79,7 @@ public class BigtableSessionSharedThreadPools {
   public synchronized ScheduledExecutorService getRetryExecutor() {
     if (retryExecutor == null) {
       retryExecutor = Executors.newScheduledThreadPool(RETRY_THREAD_COUNT,
-        GrpcUtil.getThreadFactory(RETRY_THREADPOOL_NAME_PATTERN, true));
+          ThreadUtil.getThreadFactory(RETRY_THREADPOOL_NAME_PATTERN, true));
     }
     return retryExecutor;
   }
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/CallOptionsFactory.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/CallOptionsFactory.java
index c1afa34ed..2409a28f6 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/CallOptionsFactory.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/CallOptionsFactory.java
@@ -24,6 +24,7 @@ import com.google.bigtable.v2.RowSet;
 import com.google.cloud.bigtable.config.CallOptionsConfig;
 
 import io.grpc.CallOptions;
+import io.grpc.Context;
 import io.grpc.Deadline;
 import io.grpc.MethodDescriptor;
 
@@ -52,13 +53,19 @@ public interface CallOptionsFactory {
   <RequestT> CallOptions create(MethodDescriptor<RequestT, ?> descriptor, RequestT request);
 
   /**
-   * Always returns {@link CallOptions#DEFAULT}.
+   * Returns {@link CallOptions#DEFAULT} with any {@link Context#current()}'s {@link Context#getDeadline()}
+   *  applied to it.
    */
   public static class Default implements CallOptionsFactory {
     @Override
     public <RequestT> CallOptions create(MethodDescriptor<RequestT, ?> descriptor,
         RequestT request) {
-      return CallOptions.DEFAULT;
+      Deadline contextDeadline = Context.current().getDeadline();
+      if (contextDeadline != null) {
+        return CallOptions.DEFAULT.withDeadline(contextDeadline);
+      } else {
+        return CallOptions.DEFAULT;
+      }
     }
   }
 
@@ -71,17 +78,25 @@ public interface CallOptionsFactory {
     }
 
     @Override
-    public <RequestT> CallOptions create(
-        MethodDescriptor<RequestT, ?> descriptor, RequestT request) {
-      if (!config.isUseTimeout() || request == null) {
+    /**
+     * Creates a {@link CallOptions} with a focus on {@link Deadlines}.  Deadlines are decided in the following order:
+     * <ol>
+     *     <li> If a user set a  {@link Context} deadline (see {@link Context#getDeadline()}), use that</li>
+     *     <li> If a user configured deadlines via {@link CallOptionsConfig}, use it.</li>
+     *     <li> Otherwise, use {@link CallOptions#DEFAULT}.</li>
+     * </ol>
+     *
+     */
+    public <RequestT> CallOptions create(MethodDescriptor<RequestT, ?> descriptor, RequestT request) {
+      Deadline contextDeadline = Context.current().getDeadline();
+      if (contextDeadline != null) {
+        return CallOptions.DEFAULT.withDeadline(contextDeadline);
+      } else if (config.isUseTimeout() && request != null) {
+        int timeout = isLongRequest(request) ? config.getLongRpcTimeoutMs() : config.getShortRpcTimeoutMs();
+        return CallOptions.DEFAULT.withDeadline(Deadline.after(timeout, TimeUnit.MILLISECONDS));
+      } else {
         return CallOptions.DEFAULT;
       }
-
-      int timeout = config.getShortRpcTimeoutMs();
-      if (isLongRequest(request)) {
-        timeout = config.getLongRpcTimeoutMs();
-      }
-      return CallOptions.DEFAULT.withDeadline(Deadline.after(timeout, TimeUnit.MILLISECONDS));
     }
 
     /**
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/AbstractRetryingOperation.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/AbstractRetryingOperation.java
index c315071b7..9315f1171 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/AbstractRetryingOperation.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/AbstractRetryingOperation.java
@@ -25,8 +25,8 @@ import java.util.concurrent.TimeUnit;
 import javax.annotation.Nullable;
 
 import com.google.api.client.util.BackOff;
+import com.google.api.client.util.ExponentialBackOff;
 import com.google.api.client.util.Sleeper;
-import com.google.bigtable.v2.BigtableGrpc;
 import com.google.bigtable.v2.ReadRowsRequest;
 import com.google.cloud.bigtable.config.Logger;
 import com.google.cloud.bigtable.config.RetryOptions;
@@ -43,8 +43,6 @@ import com.google.common.util.concurrent.ListenableFuture;
 import io.grpc.CallOptions;
 import io.grpc.ClientCall;
 import io.grpc.Metadata;
-import io.grpc.MethodDescriptor;
-import io.grpc.MethodDescriptor.MethodType;
 import io.grpc.Status;
 import io.grpc.Status.Code;
 import io.opencensus.contrib.grpc.util.StatusConverter;
@@ -122,8 +120,6 @@ public abstract class AbstractRetryingOperation<RequestT, ResponseT, ResultT>
   }
 
   protected BackOff currentBackoff;
-  @VisibleForTesting
-  Sleeper sleeper = Sleeper.DEFAULT;
 
   protected final BigtableAsyncRpc<RequestT, ResponseT> rpc;
   protected final RetryOptions retryOptions;
@@ -282,7 +278,7 @@ public abstract class AbstractRetryingOperation<RequestT, ResponseT, ResultT>
 
   protected long getNextBackoff() {
     if (currentBackoff == null) {
-      currentBackoff = retryOptions.createBackoff();
+      currentBackoff = createBackoff();
     }
     try {
       return currentBackoff.nextBackOffMillis();
@@ -291,6 +287,20 @@ public abstract class AbstractRetryingOperation<RequestT, ResponseT, ResultT>
     }
   }
 
+  /**
+   * <p>createBackoff.</p>
+   *
+   * @return a {@link com.google.api.client.util.ExponentialBackOff} object.
+   */
+  @VisibleForTesting
+  protected ExponentialBackOff createBackoff() {
+    return new ExponentialBackOff.Builder()
+            .setInitialIntervalMillis(retryOptions.getInitialBackoffMillis())
+            .setMaxElapsedTimeMillis(retryOptions.getMaxElapsedBackoffMillis())
+            .setMultiplier(retryOptions.getBackoffMultiplier())
+            .build();
+  }
+
   /**
    * Calls {@link BigtableAsyncRpc#newCall(CallOptions)} and
    * {@link BigtableAsyncRpc#start(Object, io.grpc.ClientCall.Listener, Metadata, ClientCall)} }
@@ -307,7 +317,7 @@ public abstract class AbstractRetryingOperation<RequestT, ResponseT, ResultT>
         // There's a subtle race condition in RetryingStreamOperation which requires a separate
         // newCall/start split. The call variable needs to be set before onMessage() happens; that
         // usually will occur, but some unit tests broke with a merged newCall and start.
-        call = rpc.newCall(getCallOptions());
+        call = rpc.newCall(getRpcCallOptions());
         rpc.start(getRetryRequest(), this, metadata, call);
       }
     } catch (Exception e) {
@@ -319,16 +329,47 @@ public abstract class AbstractRetryingOperation<RequestT, ResponseT, ResultT>
     return call;
   }
 
-  protected CallOptions getCallOptions() {
-    if (callOptions.getDeadline() != null) {
-      return callOptions;
-    }
-    if (request instanceof ReadRowsRequest
-        && !CallOptionsFactory.ConfiguredCallOptionsFactory.isGet((ReadRowsRequest) request)) {
-      // This is a streaming read.
-      return callOptions;
+  /**
+   * Returns the {@link CallOptions} that a user set for the entire Operation, which can span multiple RPCs/retries.
+   * @return The {@link CallOptions}
+   */
+  protected CallOptions getOperationCallOptions() {
+    return callOptions;
+  }
+
+  /**
+   * Create an {@link CallOption} that has a fail safe RPC deadline to make sure that unary operations don't hang. This
+   * will have to be overridden for streaming RPCs like read rows.
+   * <p>
+   * The logic is as follows:
+   * <ol>
+   *   <li> If the user provides a deadline, use the deadline</li>
+   *   <li> Else If this is a streaming read, don't set an explicit deadline.  The
+   *   {@link com.google.cloud.bigtable.grpc.io.Watchdog} will handle hangling</li>
+   *   <li> Else Set a deadline of {@link #UNARY_DEADLINE_MINUTES} minutes deadline.</li>
+   * </ol>
+   *
+   * @ee the
+   * {@link com.google.cloud.bigtable.grpc.io.Watchdog} handles hanging for streaming reads
+   *
+   * @return a {@link CallOptions}
+   */
+  protected CallOptions getRpcCallOptions() {
+    if (callOptions.getDeadline() != null || isStreamingRead()) {
+      // If the user set a deadline, honor it.
+      // If this is a streaming read, then the Watchdog will take affect and ensure that hanging does not occur.
+      return getOperationCallOptions();
+    } else {
+      // Unary calls should fail after 6 minutes, if there isn't any response from the server.
+      return callOptions.withDeadlineAfter(UNARY_DEADLINE_MINUTES, TimeUnit.MINUTES);
     }
-    return callOptions.withDeadlineAfter(UNARY_DEADLINE_MINUTES, TimeUnit.MINUTES);
+  }
+
+  // TODO(sduskis): This is only required because BigtableDataGrpcClient doesn't always use
+  //      RetryingReadRowsOperation like it should.
+  protected boolean isStreamingRead() {
+    return request instanceof ReadRowsRequest &&
+            !CallOptionsFactory.ConfiguredCallOptionsFactory.isGet((ReadRowsRequest) request);
   }
 
   protected RequestT getRetryRequest() {
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/BigtableAsyncUtilities.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/BigtableAsyncUtilities.java
index 5fdc7a91c..6c81450a1 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/BigtableAsyncUtilities.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/BigtableAsyncUtilities.java
@@ -91,14 +91,16 @@ public interface BigtableAsyncUtilities {
           } catch (Throwable t) {
             LOG.error("Could not sendMessage()", t);
             call.cancel("Exception in sendMessage.", t);
-            throw Throwables.propagate(t);
+            Throwables.throwIfUnchecked(t);
+            throw new RuntimeException(t);
           }
           try {
             call.halfClose();
           } catch (Throwable t) {
             LOG.error("Could not halfClose()", t);
             call.cancel("Exception in halfClose.", t);
-            throw Throwables.propagate(t);
+            Throwables.throwIfUnchecked(t);
+            throw new RuntimeException(t);
           }
         }
       };
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/RetryingUnaryOperation.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/RetryingUnaryOperation.java
index cfb3813b5..4464d0602 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/RetryingUnaryOperation.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/RetryingUnaryOperation.java
@@ -70,13 +70,4 @@ public class RetryingUnaryOperation<RequestT, ResponseT>
     }
     return true;
   }
-
-  @Override
-  protected CallOptions getCallOptions() {
-    CallOptions originalCallOptions = super.getCallOptions();
-    if (originalCallOptions.getDeadline() != null) {
-      return originalCallOptions;
-    }
-    return originalCallOptions.withDeadlineAfter(UNARY_DEADLINE_MINUTES, TimeUnit.MINUTES);
-  }
 }
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/io/CredentialInterceptorCache.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/io/CredentialInterceptorCache.java
index f2c33aeb5..4935cb0f3 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/io/CredentialInterceptorCache.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/io/CredentialInterceptorCache.java
@@ -21,10 +21,10 @@ import com.google.cloud.bigtable.config.CredentialFactory;
 import com.google.cloud.bigtable.config.CredentialOptions;
 import com.google.cloud.bigtable.config.CredentialOptions.CredentialType;
 import com.google.cloud.bigtable.config.RetryOptions;
+import com.google.cloud.bigtable.util.ThreadUtil;
 import com.google.common.base.Preconditions;
 
 import io.grpc.ClientInterceptor;
-import io.grpc.internal.GrpcUtil;
 
 import java.io.IOException;
 import java.security.GeneralSecurityException;
@@ -51,7 +51,7 @@ public class CredentialInterceptorCache {
   }
 
   private final ExecutorService executor =
-      Executors.newCachedThreadPool(GrpcUtil.getThreadFactory("Credentials-Refresh-%d", true));
+      Executors.newCachedThreadPool(ThreadUtil.getThreadFactory("Credentials-Refresh-%d", true));
 
   private ClientInterceptor defaultCredentialInterceptor;
 
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/scanner/RowMerger.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/scanner/RowMerger.java
index 28a11d4e7..8e1ed239c 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/scanner/RowMerger.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/scanner/RowMerger.java
@@ -15,7 +15,6 @@
  */
 package com.google.cloud.bigtable.grpc.scanner;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -29,7 +28,6 @@ import com.google.bigtable.v2.ReadRowsResponse;
 import com.google.bigtable.v2.ReadRowsResponse.CellChunk;
 import com.google.bigtable.v2.ReadRowsResponse.CellChunk.RowStatusCase;
 import com.google.bigtable.v2.RowFilter.Interleave;
-import com.google.cloud.bigtable.util.ByteStringer;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.protobuf.ByteString;
@@ -242,7 +240,7 @@ public class RowMerger implements StreamObserver<ReadRowsResponse> {
 
     // cell in progress info
     private CellIdentifier currentId;
-    private ByteArrayOutputStream outputStream;
+    private ByteString.Output outputStream;
 
 
     private final Map<String, List<FlatRow.Cell>> cells = new TreeMap<>();
@@ -257,7 +255,7 @@ public class RowMerger implements StreamObserver<ReadRowsResponse> {
 
     private final void completeMultiChunkCell() {
       Preconditions.checkArgument(hasChunkInProgess());
-      addCell(ByteStringer.wrap(outputStream.toByteArray()));
+      addCell(outputStream.toByteString());
       outputStream = null;
     }
 
@@ -328,7 +326,7 @@ public class RowMerger implements StreamObserver<ReadRowsResponse> {
 
     private void addPartialCellChunk(ReadRowsResponse.CellChunk chunk) throws IOException {
       if (outputStream == null) {
-        outputStream = new ByteArrayOutputStream(chunk.getValueSize());
+        outputStream = ByteString.newOutput();
       }
       chunk.getValue().writeTo(outputStream);
     }
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ThreadUtil.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ThreadUtil.java
new file mode 100644
index 000000000..3ab7d7845
--- /dev/null
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ThreadUtil.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 Google LLC. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.cloud.bigtable.util;
+
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * Creates a {@link ThreadFactory} that's safe to use in AppEngine.
+ * <p>
+ * This class copies code that originates in {@link io.grpc.internal.GrpcUtil#getThreadFactory(String, boolean)}.
+ */
+public class ThreadUtil {
+
+  // AppEngine runtimes have constraints on threading and socket handling
+  // that need to be accommodated.
+  public static final boolean IS_RESTRICTED_APPENGINE =
+          System.getProperty("com.google.appengine.runtime.environment") != null
+                  && "1.7".equals(System.getProperty("java.specification.version"));
+
+  /**
+   * Get a {@link ThreadFactory} suitable for use in the current environment.
+   * @param nameFormat to apply to threads created by the factory.
+   * @param daemon {@code true} if the threads the factory creates are daemon threads, {@code false}
+   *     otherwise.
+   * @return a {@link ThreadFactory}.
+   */
+  public static ThreadFactory getThreadFactory(String nameFormat, boolean daemon) {
+    if (IS_RESTRICTED_APPENGINE) {
+      return MoreExecutors.platformThreadFactory();
+    } else {
+      return new ThreadFactoryBuilder()
+          .setDaemon(daemon)
+          .setNameFormat(nameFormat)
+          .build();
+    }
+  }
+
+}
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/AbstractBigtableTable.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/AbstractBigtableTable.java
index 1297bf255..f1fe67f0e 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/AbstractBigtableTable.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/AbstractBigtableTable.java
@@ -90,6 +90,7 @@ import io.opencensus.trace.Tracing;
  * @author sduskis
  * @version $Id: $Id
  */
+@SuppressWarnings("deprecation")
 public abstract class AbstractBigtableTable implements Table {
   /** Constant <code>LOG</code> */
   protected static final Logger LOG = new Logger(AbstractBigtableTable.class);
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/BigtableExtendedScan.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/BigtableExtendedScan.java
index b53f7b5d1..286bfb8f8 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/BigtableExtendedScan.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/BigtableExtendedScan.java
@@ -17,7 +17,7 @@ package com.google.cloud.bigtable.hbase;
 
 import com.google.bigtable.v2.RowRange;
 import com.google.bigtable.v2.RowSet;
-import com.google.cloud.bigtable.util.ByteStringer;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import com.google.cloud.bigtable.util.RowKeyUtil;
 import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.client.Table;
@@ -27,6 +27,7 @@ import org.apache.hadoop.hbase.client.Table;
  * allows for an arbitrary set of ranges and row keys as part of a scan. Instance of
  * BigtableExtendedScan can be used in {@link Table#getScanner(Scan)}.
  */
+@SuppressWarnings("deprecation")
 public class BigtableExtendedScan extends Scan {
   private RowSet.Builder rowSet = RowSet.newBuilder();
 
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/CheckConfig.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/CheckConfig.java
index 8fd8db421..41d62bee2 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/CheckConfig.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/CheckConfig.java
@@ -28,7 +28,6 @@ import org.apache.hadoop.hbase.client.AbstractBigtableConnection;
 import org.apache.hadoop.hbase.client.Admin;
 import org.apache.hadoop.hbase.client.Connection;
 import org.apache.hadoop.hbase.client.ConnectionFactory;
-import org.apache.hadoop.hbase.client.HConnection;
 import org.apache.hadoop.util.GenericOptionsParser;
 
 import java.io.IOException;
@@ -81,7 +80,7 @@ public class CheckConfig {
     }
 
     String configuredConnectionClass =
-        fullConfiguration.get(HConnection.HBASE_CLIENT_CONNECTION_IMPL);
+        fullConfiguration.get(BigtableConfiguration.HBASE_CLIENT_CONNECTION_IMPL);
 
     boolean isCorrectClassSpecified = false;
 
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/MutationAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/MutationAdapter.java
index fefe1e86b..3e709a934 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/MutationAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/MutationAdapter.java
@@ -22,7 +22,7 @@ import org.apache.hadoop.hbase.client.Row;
 
 import com.google.bigtable.v2.MutateRowRequest;
 import com.google.bigtable.v2.MutateRowsRequest;
-import com.google.cloud.bigtable.util.ByteStringer;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import com.google.protobuf.ByteString;
 
 /**
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/BigtableWhileMatchResultScannerAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/BigtableWhileMatchResultScannerAdapter.java
index 97a09ca45..f49e85406 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/BigtableWhileMatchResultScannerAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/BigtableWhileMatchResultScannerAdapter.java
@@ -99,7 +99,7 @@ public class BigtableWhileMatchResultScannerAdapter {
         try {
           bigtableResultScanner.close();
         } catch (IOException ioe) {
-          throw Throwables.propagate(ioe);
+          throw new RuntimeException(ioe);
         } finally {
           closeSpan();
         }
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/FamilyFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/FamilyFilterAdapter.java
index 9279cdc44..1deac86d8 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/FamilyFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/FamilyFilterAdapter.java
@@ -20,6 +20,7 @@ import static com.google.cloud.bigtable.data.v2.wrappers.Filters.FILTERS;
 import com.google.bigtable.v2.RowFilter;
 import com.google.cloud.bigtable.hbase.adapters.read.ReaderExpressionHelper;
 
+import com.google.protobuf.ByteString;
 import org.apache.hadoop.hbase.filter.BinaryComparator;
 import org.apache.hadoop.hbase.filter.ByteArrayComparable;
 import org.apache.hadoop.hbase.filter.CompareFilter;
@@ -53,9 +54,9 @@ public class FamilyFilterAdapter extends TypedFilterAdapterBase<FamilyFilter> {
     } else if (comparator instanceof RegexStringComparator) {
       family = Bytes.toString(comparator.getValue());
     } else if (comparator instanceof BinaryComparator) {
-      byte[] quotedRegularExpression =
+      ByteString quotedRegularExpression =
           ReaderExpressionHelper.quoteRegularExpression(comparator.getValue());
-      family = Bytes.toString(quotedRegularExpression);
+      family = quotedRegularExpression.toStringUtf8();
     } else {
       throw new IllegalStateException(
           "Cannot adapt comparator " + comparator.getClass().getCanonicalName());
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/MultipleColumnPrefixFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/MultipleColumnPrefixFilterAdapter.java
index f5ca768db..7bb35e4b0 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/MultipleColumnPrefixFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/MultipleColumnPrefixFilterAdapter.java
@@ -20,11 +20,10 @@ import static com.google.cloud.bigtable.data.v2.wrappers.Filters.FILTERS;
 import com.google.bigtable.v2.RowFilter;
 import com.google.cloud.bigtable.data.v2.wrappers.Filters.InterleaveFilter;
 import com.google.cloud.bigtable.hbase.adapters.read.ReaderExpressionHelper;
-import com.google.cloud.bigtable.util.ByteStringer;
 
+import com.google.protobuf.ByteString;
 import org.apache.hadoop.hbase.filter.MultipleColumnPrefixFilter;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
 /**
@@ -43,10 +42,10 @@ public class MultipleColumnPrefixFilterAdapter
       FilterAdapterContext context,
       MultipleColumnPrefixFilter filter) throws IOException {
     InterleaveFilter interleave = FILTERS.interleave();
-    ByteArrayOutputStream outputStream = null;
+    ByteString.Output outputStream = null;
     for (byte[] prefix : filter.getPrefix()) {
       if (outputStream == null) {
-        outputStream = new ByteArrayOutputStream(prefix.length * 2);
+        outputStream = ByteString.newOutput(prefix.length * 2);
       } else {
         outputStream.reset();
       }
@@ -54,7 +53,7 @@ public class MultipleColumnPrefixFilterAdapter
       ReaderExpressionHelper.writeQuotedExpression(outputStream, prefix);
       outputStream.write(ReaderExpressionHelper.ALL_QUALIFIERS_BYTES);
 
-      interleave.filter(FILTERS.qualifier().regex(ByteStringer.wrap(outputStream.toByteArray())));
+      interleave.filter(FILTERS.qualifier().regex(outputStream.toByteString()));
     }
     return interleave.toProto();
   }
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/PrefixFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/PrefixFilterAdapter.java
index 45aa00f2d..4e4dae228 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/PrefixFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/PrefixFilterAdapter.java
@@ -19,14 +19,12 @@ import static com.google.cloud.bigtable.data.v2.wrappers.Filters.FILTERS;
 
 import com.google.bigtable.v2.RowFilter;
 import com.google.cloud.bigtable.hbase.adapters.read.ReaderExpressionHelper;
-import com.google.cloud.bigtable.util.ByteStringer;
 import com.google.cloud.bigtable.util.RowKeyUtil;
 import com.google.cloud.bigtable.util.RowKeyWrapper;
 import com.google.common.collect.ImmutableRangeSet;
 import com.google.common.collect.Range;
 import com.google.common.collect.RangeSet;
 import com.google.protobuf.ByteString;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import org.apache.hadoop.hbase.filter.PrefixFilter;
 
@@ -44,11 +42,11 @@ public class PrefixFilterAdapter extends TypedFilterAdapterBase<PrefixFilter> {
   @Override
   public RowFilter adapt(FilterAdapterContext context, PrefixFilter filter)
       throws IOException {
-    ByteArrayOutputStream baos = new ByteArrayOutputStream(filter.getPrefix().length * 2);
-    ReaderExpressionHelper.writeQuotedRegularExpression(baos, filter.getPrefix());
+    ByteString.Output output = ByteString.newOutput(filter.getPrefix().length * 2);
+    ReaderExpressionHelper.writeQuotedRegularExpression(output, filter.getPrefix());
     // Unquoted all bytes:
-    baos.write(ReaderExpressionHelper.ALL_QUALIFIERS_BYTES);
-    return FILTERS.key().regex(ByteStringer.wrap(baos.toByteArray())).toProto();
+    output.write(ReaderExpressionHelper.ALL_QUALIFIERS_BYTES);
+    return FILTERS.key().regex(output.toByteString()).toProto();
   }
 
   /**
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/QualifierFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/QualifierFilterAdapter.java
index 57806f0b6..91483ca8c 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/QualifierFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/QualifierFilterAdapter.java
@@ -21,7 +21,6 @@ import com.google.bigtable.v2.RowFilter;
 import com.google.cloud.bigtable.data.v2.wrappers.Filters.Filter;
 import com.google.cloud.bigtable.data.v2.wrappers.Filters.QualifierRangeFilter;
 import com.google.cloud.bigtable.hbase.adapters.read.ReaderExpressionHelper;
-import com.google.cloud.bigtable.util.ByteStringer;
 import com.google.protobuf.ByteString;
 
 import org.apache.hadoop.hbase.filter.BinaryComparator;
@@ -71,8 +70,7 @@ public class QualifierFilterAdapter extends TypedFilterAdapterBase<QualifierFilt
   private Filter adaptBinaryComparator(
       FilterAdapterContext context, CompareOp compareOp, BinaryComparator comparator)
       throws IOException {
-    byte[] quoted = ReaderExpressionHelper.quoteRegularExpression(comparator.getValue());
-    ByteString quotedValue = ByteStringer.wrap(quoted);
+    ByteString quotedValue = ReaderExpressionHelper.quoteRegularExpression(comparator.getValue());
     switch (compareOp) {
       case LESS:
         return range(context).endOpen(quotedValue);
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/RowFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/RowFilterAdapter.java
index 5671893fb..75f7cd490 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/RowFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/RowFilterAdapter.java
@@ -27,7 +27,6 @@ import org.apache.hadoop.hbase.filter.RegexStringComparator;
 
 import com.google.bigtable.v2.RowFilter;
 import com.google.cloud.bigtable.hbase.adapters.read.ReaderExpressionHelper;
-import com.google.cloud.bigtable.util.ByteStringer;
 import com.google.protobuf.ByteString;
 
 /**
@@ -61,9 +60,8 @@ public class RowFilterAdapter
     } else if (comparator instanceof RegexStringComparator) {
       regexValue = ByteString.copyFrom(comparator.getValue());
     } else if (comparator instanceof BinaryComparator) {
-      byte[] quotedRegularExpression =
+      regexValue =
           ReaderExpressionHelper.quoteRegularExpression(comparator.getValue());
-      regexValue = ByteStringer.wrap(quotedRegularExpression);
     } else {
       throw new IllegalStateException(String.format("Cannot adapt comparator %s", comparator
           .getClass().getCanonicalName()));
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/SingleColumnValueFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/SingleColumnValueFilterAdapter.java
index 77d64a3fe..54b69e293 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/SingleColumnValueFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/SingleColumnValueFilterAdapter.java
@@ -23,12 +23,10 @@ import java.io.IOException;
 
 import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
 import org.apache.hadoop.hbase.filter.ValueFilter;
-import org.apache.hadoop.hbase.util.Bytes;
 
 import com.google.bigtable.v2.RowFilter;
 import com.google.cloud.bigtable.data.v2.wrappers.Filters.Filter;
 import com.google.cloud.bigtable.data.v2.wrappers.Filters.ChainFilter;
-import com.google.cloud.bigtable.util.ByteStringer;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.protobuf.ByteString;
 
@@ -180,8 +178,8 @@ public class SingleColumnValueFilterAdapter
   @VisibleForTesting
   static ChainFilter getColumnSpecFilter(byte[] family, byte[] qualifier, boolean latestVersionOnly)
       throws IOException {
-    ByteString wrappedQual = ByteStringer.wrap(quoteRegularExpression(qualifier));
-    String wrappedFamily = Bytes.toString(quoteRegularExpression(family));
+    ByteString wrappedQual = quoteRegularExpression(qualifier);
+    String wrappedFamily = quoteRegularExpression(family).toStringUtf8();
     ChainFilter builder = FILTERS.chain()
         .filter(FILTERS.family().regex(wrappedFamily))
         .filter(FILTERS.qualifier().regex(wrappedQual));
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/BigtableResultScannerAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/BigtableResultScannerAdapter.java
index 332a3fcdc..2e1514fff 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/BigtableResultScannerAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/BigtableResultScannerAdapter.java
@@ -80,7 +80,7 @@ public class BigtableResultScannerAdapter<T> {
         try {
           bigtableResultScanner.close();
         } catch (IOException ioe) {
-          throw Throwables.propagate(ioe);
+          throw new RuntimeException(ioe);
         } finally {
           closeSpan();
         }
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/FlatRowAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/FlatRowAdapter.java
index 8d48cd014..a06aa036b 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/FlatRowAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/FlatRowAdapter.java
@@ -19,7 +19,7 @@ import com.google.cloud.bigtable.grpc.scanner.FlatRow;
 import com.google.cloud.bigtable.hbase.BigtableConstants;
 import com.google.cloud.bigtable.hbase.adapters.ResponseAdapter;
 import com.google.cloud.bigtable.hbase.util.TimestampConverter;
-import com.google.cloud.bigtable.util.ByteStringer;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import com.google.common.base.Objects;
 
 import java.util.ArrayList;
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ReaderExpressionHelper.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ReaderExpressionHelper.java
index b14ce6458..94344c1bd 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ReaderExpressionHelper.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ReaderExpressionHelper.java
@@ -15,6 +15,7 @@
  */
 package com.google.cloud.bigtable.hbase.adapters.read;
 
+import com.google.protobuf.ByteString;
 import org.apache.hadoop.hbase.util.Bytes;
 
 import java.io.ByteArrayOutputStream;
@@ -37,42 +38,10 @@ public class ReaderExpressionHelper {
   public static final byte[] ANY_BYTE_BYTES = Bytes.toBytes(ANY_BYTE);
   /** Constant <code>ANY_BYTE_BYTES=Bytes.toBytes(ANY_BYTE)</code> */
   public static final byte[] ALL_BYTE_BYTES = Bytes.toBytes(ALL_BYTES);
-  /** Constant <code>ALL_QUALIFIERS="\\C*"</code> */
-  public static final String ALL_QUALIFIERS = ALL_BYTES;
   /** Constant <code>ALL_QUALIFIERS_BYTES=Bytes.toBytes(ALL_QUALIFIERS)</code> */
   public static final byte[] ALL_QUALIFIERS_BYTES = ALL_BYTE_BYTES;
-  /** Constant <code>ALL_FAMILIES=".*"</code> */
-  public static final String ALL_FAMILIES = ".*";
-  /** Constant <code>ALL_FAMILIES_BYTES=Bytes.toBytes(ALL_FAMILIES)</code> */
-  public static final byte[] ALL_FAMILIES_BYTES = Bytes.toBytes(ALL_FAMILIES);
-  /** Constant <code>ALL_VERSIONS="all"</code> */
-  public static final String ALL_VERSIONS = "all";
-  /** Constant <code>ALL_VERSIONS_BYTES=Bytes.toBytes(ALL_VERSIONS)</code> */
-  public static final byte[] ALL_VERSIONS_BYTES = Bytes.toBytes(ALL_VERSIONS);
-  /** Constant <code>LATEST_VERSION="latest"</code> */
-  public static final String LATEST_VERSION = "latest";
-  /** Constant <code>INTERLEAVE_CHARACTERS=Bytes.toBytes(" + ")</code> */
-  public static final byte[] INTERLEAVE_CHARACTERS = Bytes.toBytes(" + ");
-  /** Constant <code>PIPE_CHARACTER_BYTES=Bytes.toBytes(" | ")</code> */
-  public static final byte[] PIPE_CHARACTER_BYTES = Bytes.toBytes(" | ");
   private final static byte[] NULL_CHARACTER_BYTES = Bytes.toBytes("\\x00");
 
-  /**
-   * Write unquoted to the OutputStream applying just Bigtable reader
-   * expression quoting. Used when the supplied value is NOT a regular expression.
-   *
-   * @param unquoted A byte-array, possibly containing bytes outside of the ASCII
-   * @param outputStream A stream to write quoted output to
-   * @throws java.io.IOException if any.
-   */
-  public static void writeFilterQuotedExpression(byte[] unquoted, OutputStream outputStream)
-      throws IOException{
-    QuoteFilterExpressionStream quoteFilterExpressionStream =
-        new QuoteFilterExpressionStream(outputStream);
-    quoteFilterExpressionStream.write(unquoted);
-    quoteFilterExpressionStream.close();
-  }
-
   /**
    * Write unquoted to the OutputStream applying both RE2:QuoteMeta and Bigtable reader
    * expression quoting.
@@ -93,10 +62,10 @@ public class ReaderExpressionHelper {
    * @return an array of byte.
    * @throws java.io.IOException if any.
    */
-  public static byte[] quoteRegularExpression(byte[] unquoted) throws IOException {
-    ByteArrayOutputStream baos = new ByteArrayOutputStream(unquoted.length * 2);
-    writeQuotedRegularExpression(baos, unquoted);
-    return baos.toByteArray();
+  public static ByteString quoteRegularExpression(byte[] unquoted) throws IOException {
+    ByteString.Output output = ByteString.newOutput(unquoted.length * 2);
+    writeQuotedRegularExpression(output, unquoted);
+    return output.toByteString();
   }
 
   /**
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/RowAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/RowAdapter.java
index 99a640347..c611dadec 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/RowAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/RowAdapter.java
@@ -21,7 +21,7 @@ import com.google.bigtable.v2.Family;
 import com.google.bigtable.v2.Row;
 import com.google.cloud.bigtable.hbase.adapters.ResponseAdapter;
 import com.google.cloud.bigtable.hbase.util.TimestampConverter;
-import com.google.cloud.bigtable.util.ByteStringer;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import java.util.SortedSet;
 import java.util.TreeSet;
 import org.apache.hadoop.hbase.KeyValue;
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ScanAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ScanAdapter.java
index 72f11824f..94d483643 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ScanAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ScanAdapter.java
@@ -17,7 +17,6 @@ package com.google.cloud.bigtable.hbase.adapters.read;
 
 import static com.google.cloud.bigtable.data.v2.wrappers.Filters.FILTERS;
 
-import com.google.bigtable.v2.BigtableGrpc;
 import com.google.common.collect.Range;
 import com.google.bigtable.v2.ReadRowsRequest;
 import com.google.bigtable.v2.RowFilter;
@@ -31,7 +30,6 @@ import com.google.cloud.bigtable.hbase.BigtableConstants;
 import com.google.cloud.bigtable.hbase.BigtableExtendedScan;
 import com.google.cloud.bigtable.hbase.adapters.filters.FilterAdapter;
 import com.google.cloud.bigtable.hbase.adapters.filters.FilterAdapterContext;
-import com.google.cloud.bigtable.util.ByteStringer;
 import com.google.cloud.bigtable.util.RowKeyWrapper;
 import com.google.common.base.Optional;
 import com.google.common.collect.RangeSet;
@@ -175,7 +173,7 @@ public class ScanAdapter implements ReadOperationAdapter<Scan> {
     }
   }
 
-  private static byte[] quoteRegex(byte[] unquoted)  {
+  private static ByteString quoteRegex(byte[] unquoted)  {
     try {
       return ReaderExpressionHelper.quoteRegularExpression(unquoted);
     } catch (IOException e) {
@@ -211,7 +209,7 @@ public class ScanAdapter implements ReadOperationAdapter<Scan> {
   }
 
   private Filters.Filter createColumnQualifierFilter(byte[] unquotedQualifier) {
-    return FILTERS.qualifier().regex(ByteStringer.wrap(quoteRegex(unquotedQualifier)));
+    return FILTERS.qualifier().regex(quoteRegex(unquotedQualifier));
   }
 
   private Filters.Filter createFamilyFilter(byte[] familyName) {
diff --git a/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableIO.java b/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableIO.java
index 6cdf75fc3..651c0c54f 100755
--- a/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableIO.java
+++ b/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableIO.java
@@ -73,12 +73,12 @@ import com.google.bigtable.repackaged.com.google.cloud.bigtable.grpc.BigtableSes
 import com.google.bigtable.repackaged.com.google.cloud.bigtable.grpc.async.ResourceLimiterStats;
 import com.google.bigtable.repackaged.com.google.cloud.bigtable.grpc.scanner.FlatRow;
 import com.google.bigtable.repackaged.com.google.cloud.bigtable.grpc.scanner.ResultScanner;
-import com.google.bigtable.repackaged.com.google.cloud.bigtable.util.ZeroCopyByteStringUtil;
 import com.google.bigtable.repackaged.com.google.common.annotations.VisibleForTesting;
 import com.google.bigtable.repackaged.com.google.common.base.Preconditions;
 import com.google.cloud.bigtable.batch.common.CloudBigtableServiceImpl;
 import com.google.cloud.bigtable.hbase.BigtableOptionsFactory;
 import com.google.cloud.bigtable.hbase.adapters.read.FlatRowAdapter;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 
 /**
  * <p>
@@ -630,7 +630,7 @@ public class CloudBigtableIO {
     public boolean advance() throws IOException {
       FlatRow row = scanner.next();
       if (row != null && rangeTracker.tryReturnRecordAt(true,
-        ByteKey.copyFrom(ZeroCopyByteStringUtil.get(row.getRowKey())))) {
+        ByteKey.copyFrom(ByteStringer.extract(row.getRowKey())))) {
         current = FLAT_ROW_ADAPTER.adaptResponse(row);
         rowsRead.addAndGet(1l);
         return true;
diff --git a/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableScanConfiguration.java b/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableScanConfiguration.java
index 35b557e7d..1b6295e56 100644
--- a/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableScanConfiguration.java
+++ b/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableScanConfiguration.java
@@ -19,6 +19,7 @@ import java.io.Serializable;
 import java.util.Map;
 import java.util.Objects;
 
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import org.apache.beam.sdk.io.range.ByteKey;
 import org.apache.beam.sdk.io.range.ByteKeyRange;
 import org.apache.beam.sdk.options.ValueProvider;
@@ -29,7 +30,6 @@ import com.google.bigtable.repackaged.com.google.bigtable.v2.ReadRowsRequest;
 import com.google.bigtable.repackaged.com.google.bigtable.v2.RowRange;
 import com.google.bigtable.repackaged.com.google.bigtable.v2.RowSet;
 import com.google.bigtable.repackaged.com.google.cloud.bigtable.grpc.BigtableInstanceName;
-import com.google.bigtable.repackaged.com.google.cloud.bigtable.util.ByteStringer;
 import com.google.bigtable.repackaged.com.google.protobuf.ByteString;
 import com.google.cloud.bigtable.hbase.adapters.Adapters;
 import com.google.cloud.bigtable.hbase.adapters.read.DefaultReadHooks;
diff --git a/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/dataflow/CloudBigtableScanConfiguration.java b/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/dataflow/CloudBigtableScanConfiguration.java
index 3bf3d71c2..5f12019d8 100644
--- a/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/dataflow/CloudBigtableScanConfiguration.java
+++ b/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/dataflow/CloudBigtableScanConfiguration.java
@@ -18,13 +18,13 @@ package com.google.cloud.bigtable.dataflow;
 import java.util.Map;
 import java.util.Objects;
 
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import org.apache.hadoop.hbase.client.Scan;
 
 import com.google.bigtable.repackaged.com.google.bigtable.v2.ReadRowsRequest;
 import com.google.bigtable.repackaged.com.google.bigtable.v2.RowRange;
 import com.google.bigtable.repackaged.com.google.bigtable.v2.RowSet;
 import com.google.bigtable.repackaged.com.google.cloud.bigtable.grpc.BigtableInstanceName;
-import com.google.bigtable.repackaged.com.google.cloud.bigtable.util.ByteStringer;
 import com.google.bigtable.repackaged.com.google.protobuf.ByteString;
 import com.google.cloud.bigtable.batch.common.ByteStringUtil;
 import com.google.cloud.bigtable.hbase.adapters.Adapters;


Test patch:
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/config/RetryOptionsUtil.java b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/config/RetryOptionsUtil.java
deleted file mode 100644
index 398c84fee..000000000
--- a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/config/RetryOptionsUtil.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2015 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * 
- *     http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.cloud.bigtable.config;
-
-import static com.google.cloud.bigtable.config.RetryOptions.DEFAULT_BACKOFF_MULTIPLIER;
-import static com.google.cloud.bigtable.config.RetryOptions.DEFAULT_INITIAL_BACKOFF_MILLIS;
-import static com.google.cloud.bigtable.config.RetryOptions.DEFAULT_MAX_ELAPSED_BACKOFF_MILLIS;
-import static com.google.cloud.bigtable.config.RetryOptions.DEFAULT_MAX_SCAN_TIMEOUT_RETRIES;
-import static com.google.cloud.bigtable.config.RetryOptions.DEFAULT_READ_PARTIAL_ROW_TIMEOUT_MS;
-import static com.google.cloud.bigtable.config.RetryOptions.DEFAULT_STREAMING_BUFFER_SIZE;
-import static com.google.cloud.bigtable.config.RetryOptions.DEFAULT_ENABLE_GRPC_RETRIES_SET;
-
-import com.google.api.client.util.ExponentialBackOff;
-import com.google.api.client.util.NanoClock;
-
-/**
- * Testing utility for creating RetryOptions
- */
-public class RetryOptionsUtil {
-  public static RetryOptions createTestRetryOptions(final NanoClock nanoClock) {
-    return createTestRetryOptions(nanoClock, false);
-  }
-
-  public static RetryOptions createTestRetryOptions(final NanoClock nanoClock,
-      boolean allowRetriesWithoutTimestamp) {
-    return new RetryOptions(true, allowRetriesWithoutTimestamp, DEFAULT_INITIAL_BACKOFF_MILLIS,
-        DEFAULT_BACKOFF_MULTIPLIER, DEFAULT_MAX_ELAPSED_BACKOFF_MILLIS,
-        DEFAULT_STREAMING_BUFFER_SIZE, DEFAULT_READ_PARTIAL_ROW_TIMEOUT_MS,
-        DEFAULT_MAX_SCAN_TIMEOUT_RETRIES, DEFAULT_ENABLE_GRPC_RETRIES_SET) {
-      private static final long serialVersionUID = 1L;
-
-      @Override
-      protected ExponentialBackOff.Builder createBackoffBuilder() {
-        return super.createBackoffBuilder().setNanoClock(nanoClock);
-      }
-    };
-  }
-}
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/TestBigtableDataGrpcClient.java b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/TestBigtableDataGrpcClient.java
index 2eb672b82..9c00c28d2 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/TestBigtableDataGrpcClient.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/TestBigtableDataGrpcClient.java
@@ -51,7 +51,6 @@ import com.google.bigtable.v2.ReadRowsResponse;
 import com.google.bigtable.v2.RowRange;
 import com.google.cloud.bigtable.config.BigtableOptions;
 import com.google.cloud.bigtable.config.RetryOptions;
-import com.google.cloud.bigtable.config.RetryOptionsUtil;
 import com.google.cloud.bigtable.grpc.io.GoogleCloudResourcePrefixInterceptor;
 import com.google.cloud.bigtable.grpc.scanner.FlatRow;
 import com.google.cloud.bigtable.grpc.scanner.ResultScanner;
@@ -79,9 +78,6 @@ public class TestBigtableDataGrpcClient {
   @Mock
   ClientCall mockClientCall;
 
-  @Mock
-  NanoClock nanoClock;
-
   BigtableDataGrpcClient defaultClient;
 
   @Before
@@ -93,8 +89,8 @@ public class TestBigtableDataGrpcClient {
   }
 
   protected BigtableDataGrpcClient createClient(boolean allowRetriesWithoutTimestamp) {
-    RetryOptions retryOptions =
-        RetryOptionsUtil.createTestRetryOptions(nanoClock, allowRetriesWithoutTimestamp);
+    RetryOptions retryOptions = new RetryOptions.Builder()
+            .setAllowRetriesWithoutTimestamp(allowRetriesWithoutTimestamp).build();
     BigtableOptions options = new BigtableOptions.Builder().setRetryOptions(retryOptions).build();
     doAnswer(new Answer<Void>(){
       @Override
@@ -107,7 +103,7 @@ public class TestBigtableDataGrpcClient {
   }
 
   @Test
-  public void testRetyableMutateRow() throws Exception {
+  public void testRetryableMutateRow() {
     MutateRowRequest request = MutateRowRequest.newBuilder().setTableName(TABLE_NAME).build();
     setResponse(MutateRowResponse.getDefaultInstance());
     defaultClient.mutateRow(request);
@@ -115,14 +111,14 @@ public class TestBigtableDataGrpcClient {
   }
 
   @Test
-  public void testRetyableMutateRowAsync() {
+  public void testRetryableMutateRowAsync() {
     MutateRowRequest request = MutateRowRequest.newBuilder().setTableName(TABLE_NAME).build();
     defaultClient.mutateRowAsync(request);
     verifyRequestCalled(request);
   }
 
   @Test
-  public void testRetyableCheckAndMutateRow() throws Exception {
+  public void testRetryableCheckAndMutateRow() {
     CheckAndMutateRowRequest request =
         CheckAndMutateRowRequest.newBuilder().setTableName(TABLE_NAME).build();
     setResponse(CheckAndMutateRowResponse.getDefaultInstance());
@@ -131,7 +127,7 @@ public class TestBigtableDataGrpcClient {
   }
 
   @Test
-  public void testRetyableCheckAndMutateRowAsync() {
+  public void testRetryableCheckAndMutateRowAsync() {
     CheckAndMutateRowRequest request =
         CheckAndMutateRowRequest.newBuilder().setTableName(TABLE_NAME).build();
     defaultClient.checkAndMutateRowAsync(request);
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/TestBigtableTableAdminGrpcClient.java b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/TestBigtableTableAdminGrpcClient.java
index 052de1a5c..0dc1d52ef 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/TestBigtableTableAdminGrpcClient.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/TestBigtableTableAdminGrpcClient.java
@@ -26,7 +26,6 @@ import com.google.bigtable.admin.v2.GenerateConsistencyTokenRequest;
 import com.google.bigtable.admin.v2.GenerateConsistencyTokenResponse;
 import com.google.bigtable.admin.v2.GetSnapshotRequest;
 import com.google.bigtable.admin.v2.GetTableRequest;
-import com.google.bigtable.admin.v2.Instance;
 import com.google.bigtable.admin.v2.ListSnapshotsRequest;
 import com.google.bigtable.admin.v2.ModifyColumnFamiliesRequest;
 import com.google.bigtable.admin.v2.SnapshotTableRequest;
@@ -34,7 +33,6 @@ import com.google.bigtable.admin.v2.Table;
 
 import com.google.cloud.bigtable.config.BigtableOptions;
 import com.google.cloud.bigtable.config.RetryOptions;
-import com.google.cloud.bigtable.config.RetryOptionsUtil;
 import io.grpc.CallOptions;
 import io.grpc.Channel;
 import io.grpc.ClientCall;
@@ -52,7 +50,6 @@ import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -94,9 +91,7 @@ public class TestBigtableTableAdminGrpcClient {
   }
 
   protected BigtableTableAdminGrpcClient createClient(boolean allowRetriesWithoutTimestamp) {
-    RetryOptions retryOptions =
-        RetryOptionsUtil.createTestRetryOptions(nanoClock, allowRetriesWithoutTimestamp);
-    BigtableOptions options = new BigtableOptions.Builder().setRetryOptions(retryOptions).build();
+    BigtableOptions options = new BigtableOptions.Builder().build();
     return new BigtableTableAdminGrpcClient(mockChannel, null, options);
   }
 
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/TestCallOptionsFactory.java b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/TestCallOptionsFactory.java
new file mode 100644
index 000000000..114983be1
--- /dev/null
+++ b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/TestCallOptionsFactory.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2018 Google LLC. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.cloud.bigtable.grpc;
+
+import com.google.bigtable.v2.MutateRowRequest;
+import com.google.bigtable.v2.MutateRowsRequest;
+import com.google.cloud.bigtable.config.CallOptionsConfig;
+import io.grpc.CallOptions;
+import io.grpc.Context;
+import io.grpc.Deadline;
+import io.grpc.DeadlineUtil;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link CallOptionsFactory}.
+ */
+@RunWith(JUnit4.class)
+public class TestCallOptionsFactory {
+
+  private static final long NOW = System.nanoTime();
+
+  @Mock
+  ScheduledExecutorService mockExecutor;
+
+  @Before
+  public void setup() {
+    MockitoAnnotations.initMocks(this);
+  }
+
+  @Test
+  public void testDefault() {
+    CallOptionsFactory factory = new CallOptionsFactory.Default();
+    Assert.assertSame(CallOptions.DEFAULT, factory.create(null, null));
+  }
+
+  @Test
+  public void testDefaultWithContext() {
+    final Deadline deadline = DeadlineUtil.deadlineWithFixedTime(1, TimeUnit.SECONDS, NOW);
+    Context.CancellableContext context = Context.current().withDeadline(deadline, mockExecutor);
+    context.run(new Runnable() {
+      @Override
+      public void run() {
+        CallOptionsFactory factory = new CallOptionsFactory.Default();
+        assertEqualsDeadlines(
+            deadline.timeRemaining(TimeUnit.MILLISECONDS),
+            getDeadlineMs(factory, null)
+        );
+      }
+    });
+  }
+
+  @Test
+  public void testConfiguredDefaultConfig() {
+    CallOptionsConfig config = new CallOptionsConfig.Builder().build();
+    CallOptionsFactory factory = new CallOptionsFactory.ConfiguredCallOptionsFactory(config);
+    Assert.assertSame(CallOptions.DEFAULT, factory.create(null, null));
+  }
+
+  @Test
+  public void testConfiguredConfigEnabled() {
+    CallOptionsConfig config = new CallOptionsConfig.Builder().setUseTimeout(true).build();
+    CallOptionsFactory factory = new CallOptionsFactory.ConfiguredCallOptionsFactory(config);
+    assertEqualsDeadlines(
+        config.getShortRpcTimeoutMs(),
+        getDeadlineMs(factory, MutateRowRequest.getDefaultInstance())
+    );
+    assertEqualsDeadlines(
+        config.getLongRpcTimeoutMs(),
+        getDeadlineMs(factory, MutateRowsRequest.getDefaultInstance())
+    );
+  }
+
+  @Test
+  public void testConfiguredWithContext() {
+    Deadline deadline = DeadlineUtil.deadlineWithFixedTime(1, TimeUnit.SECONDS, NOW);
+    Context.CancellableContext context = Context.current().withDeadline(deadline, mockExecutor);
+    context.run(new Runnable() {
+      @Override
+      public void run() {
+        CallOptionsConfig config = new CallOptionsConfig.Builder()
+            .setUseTimeout(true)
+            .setShortRpcTimeoutMs((int) TimeUnit.SECONDS.toMillis(100))
+            .setLongRpcTimeoutMs((int) TimeUnit.SECONDS.toMillis(1000))
+            .build();
+        CallOptionsFactory factory = new CallOptionsFactory.ConfiguredCallOptionsFactory(config);
+        // The deadline in the context in 1 second, and the deadline in the config is 100+ seconds
+        assertEqualsDeadlines(
+            TimeUnit.SECONDS.toMillis(1),
+            getDeadlineMs(factory, MutateRowRequest.getDefaultInstance())
+        );
+      }
+    });
+  }
+
+  /**
+   * Deadline / Timestamp math could lead to some minor variations from expected values. This method
+   * allows for a minor delta.
+   *
+   * @param expected
+   * @param actual
+   */
+  private static void assertEqualsDeadlines(long expected, long actual) {
+    Assert.assertEquals((double) expected, (double) actual, 10);
+  }
+
+  private int getDeadlineMs(CallOptionsFactory factory, Object request) {
+    return (int) factory.create(null, request).getDeadline().timeRemaining(TimeUnit.MILLISECONDS);
+  }
+}
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/async/TestMutateRowsRequestManager.java b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/async/TestMutateRowsRequestManager.java
index 5988ce175..598220a9c 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/async/TestMutateRowsRequestManager.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/async/TestMutateRowsRequestManager.java
@@ -27,14 +27,12 @@ import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 import org.mockito.MockitoAnnotations;
 
-import com.google.api.client.util.NanoClock;
 import com.google.bigtable.v2.MutateRowsRequest;
 import com.google.bigtable.v2.MutateRowsRequest.Entry;
 import com.google.bigtable.v2.MutateRowsResponse;
 import com.google.bigtable.v2.Mutation;
 import com.google.bigtable.v2.Mutation.SetCell;
 import com.google.cloud.bigtable.config.RetryOptions;
-import com.google.cloud.bigtable.config.RetryOptionsUtil;
 import com.google.cloud.bigtable.grpc.async.MutateRowsRequestManager.ProcessingStatus;
 import com.google.rpc.Status;
 
@@ -91,19 +89,13 @@ public class TestMutateRowsRequestManager {
   }
 
   private AtomicLong time = new AtomicLong();
-  private NanoClock nanoClock = new NanoClock() {
-    @Override
-    public long nanoTime() {
-      return time.get();
-    }
-  };
 
   private RetryOptions retryOptions;
 
   @Before
   public void setup() {
     MockitoAnnotations.initMocks(this);
-    retryOptions = RetryOptionsUtil.createTestRetryOptions(nanoClock);
+    retryOptions = new RetryOptions.Builder().build();
   }
 
   @Test
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/async/TestRetryingMutateRowsOperation.java b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/async/TestRetryingMutateRowsOperation.java
index 16fb8c0c7..45433b75d 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/async/TestRetryingMutateRowsOperation.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/async/TestRetryingMutateRowsOperation.java
@@ -41,7 +41,6 @@ import com.google.bigtable.v2.MutateRowsResponse;
 import com.google.bigtable.v2.Mutation;
 import com.google.bigtable.v2.Mutation.SetCell;
 import com.google.cloud.bigtable.config.RetryOptions;
-import com.google.cloud.bigtable.config.RetryOptionsUtil;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.rpc.Status;
 
@@ -129,7 +128,7 @@ public class TestRetryingMutateRowsOperation {
     MockitoAnnotations.initMocks(this);
     when(mutateRows.getRpcMetrics()).thenReturn(metrics);
     when(mutateRows.getMethodDescriptor()).thenReturn(BigtableGrpc.getMutateRowsMethod());
-    retryOptions = RetryOptionsUtil.createTestRetryOptions(nanoClock);
+    retryOptions = new RetryOptions.Builder().build();
   }
 
   @Test
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/async/TestRetryingUnaryOperation.java b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/async/TestRetryingUnaryOperation.java
index e2980f870..284158c5e 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/async/TestRetryingUnaryOperation.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/async/TestRetryingUnaryOperation.java
@@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 
+import com.google.api.client.util.ExponentialBackOff;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -45,7 +46,6 @@ import com.google.bigtable.v2.BigtableGrpc;
 import com.google.bigtable.v2.ReadRowsRequest;
 import com.google.bigtable.v2.ReadRowsResponse;
 import com.google.cloud.bigtable.config.RetryOptions;
-import com.google.cloud.bigtable.config.RetryOptionsUtil;
 import com.google.cloud.bigtable.grpc.scanner.BigtableRetriesExhaustedException;
 import com.google.common.util.concurrent.ListenableFuture;
 import io.grpc.CallOptions;
@@ -88,14 +88,11 @@ public class TestRetryingUnaryOperation {
   @Before
   public void setup() {
     MockitoAnnotations.initMocks(this);
-    retryOptions = RetryOptionsUtil.createTestRetryOptions(nanoClock);
+    retryOptions = new RetryOptions.Builder().build();
 
     when(readAsync.getRpcMetrics()).thenReturn(metrics);
     when(readAsync.getMethodDescriptor()).thenReturn(BigtableGrpc.getReadRowsMethod());
 
-    underTest = new RetryingUnaryOperation<>(retryOptions, ReadRowsRequest.getDefaultInstance(),
-        readAsync, CallOptions.DEFAULT, executorService, new Metadata());
-
     totalSleep = new AtomicLong();
 
     doAnswer(new Answer<Void>() {
@@ -123,6 +120,21 @@ public class TestRetryingUnaryOperation {
       }
     });
     when(readAsync.isRetryable(any(ReadRowsRequest.class))).thenReturn(true);
+
+    underTest = new RetryingUnaryOperation<ReadRowsRequest, ReadRowsResponse>(retryOptions,
+            ReadRowsRequest.getDefaultInstance(), readAsync, CallOptions.DEFAULT, executorService,
+            new Metadata()) {
+        @Override
+        protected ExponentialBackOff createBackoff() {
+            return new ExponentialBackOff.Builder()
+                    .setNanoClock(nanoClock)
+                    .setInitialIntervalMillis(retryOptions.getInitialBackoffMillis())
+                    .setMaxElapsedTimeMillis(retryOptions.getMaxElapsedBackoffMillis())
+                    .setMultiplier(retryOptions.getBackoffMultiplier())
+                    .build();
+        }
+    };
+
   }
 
   @Test
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/io/RefreshingOAuth2CredentialsInterceptorTest.java b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/io/RefreshingOAuth2CredentialsInterceptorTest.java
index 2feb4167f..2a7bdddc4 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/io/RefreshingOAuth2CredentialsInterceptorTest.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/test/java/com/google/cloud/bigtable/grpc/io/RefreshingOAuth2CredentialsInterceptorTest.java
@@ -69,15 +69,12 @@ public class RefreshingOAuth2CredentialsInterceptorTest {
     }
 
     @AfterClass
-    public static void shtudown() {
+    public static void shutdown() {
         executorService.shutdownNow();
     }
 
     private RefreshingOAuth2CredentialsInterceptor underTest;
 
-    @Mock
-    private OAuth2Credentials mockCredentials;
-
     @Mock
     private Channel mockChannel;
 
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/test/java/io/grpc/DeadlineUtil.java b/bigtable-client-core-parent/bigtable-client-core/src/test/java/io/grpc/DeadlineUtil.java
new file mode 100644
index 000000000..c3f91acc2
--- /dev/null
+++ b/bigtable-client-core-parent/bigtable-client-core/src/test/java/io/grpc/DeadlineUtil.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2018 Google LLC. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.grpc;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Hack to allow the creation of a {@link Deadline} with a constant time.
+ */
+public class DeadlineUtil {
+
+  public static Deadline deadlineWithFixedTime(int duration, TimeUnit unit, final long timeNs) {
+    return Deadline.after(duration, unit, new Deadline.Ticker() {
+      @Override
+      public long read() {
+        return timeNs;
+      }
+    });
+  }
+}
diff --git a/bigtable-client-core-parent/bigtable-hbase-integration-tests-common/src/test/java/com/google/cloud/bigtable/hbase/AbstractTestFilters.java b/bigtable-client-core-parent/bigtable-hbase-integration-tests-common/src/test/java/com/google/cloud/bigtable/hbase/AbstractTestFilters.java
index a8d7165ee..9c1945ca5 100644
--- a/bigtable-client-core-parent/bigtable-hbase-integration-tests-common/src/test/java/com/google/cloud/bigtable/hbase/AbstractTestFilters.java
+++ b/bigtable-client-core-parent/bigtable-hbase-integration-tests-common/src/test/java/com/google/cloud/bigtable/hbase/AbstractTestFilters.java
@@ -225,14 +225,20 @@ public abstract class AbstractTestFilters extends AbstractTest {
     // Test ColumnCountGetFilter on scan.  ColumnCountGetFilter is not made for scans, and once
     // the column limit has been met, Filter#filterAllRemaining() returns true.
     Filter filter = new ColumnCountGetFilter(numColumnsToFilter);
-    Scan scan = new Scan(Bytes.toBytes(rowPrefix), Bytes.toBytes(endRowKey)).setFilter(filter);
+    Scan scan = new Scan()
+            .withStartRow(Bytes.toBytes(rowPrefix))
+            .withStopRow(Bytes.toBytes(endRowKey))
+            .setFilter(filter);
     ResultScanner scanner = table.getScanner(scan);
     Result[] results = scanner.next(1000);
     Assert.assertEquals(1, results.length);
 
     // Test ColumnPaginationFilter on scan
     filter = new ColumnPaginationFilter(numColumnsToFilter, offset);
-    scan = new Scan(Bytes.toBytes(rowPrefix), Bytes.toBytes(endRowKey)).setFilter(filter);
+    scan = new Scan()
+            .withStartRow(Bytes.toBytes(rowPrefix))
+            .withStartRow(Bytes.toBytes(endRowKey))
+            .setFilter(filter);
     scanner = table.getScanner(scan);
     results = scanner.next(1000);
     Assert.assertEquals(numRows, results.length);
@@ -1097,7 +1103,7 @@ public abstract class AbstractTestFilters extends AbstractTest {
     ByteArrayComparable rowValue2Comparable = new BinaryComparator(Bytes.toBytes("12"));
     ValueFilter valueFilter = new ValueFilter(CompareOp.NOT_EQUAL, rowValue2Comparable);
     WhileMatchFilter simpleWhileMatch = new WhileMatchFilter(valueFilter);
-    Scan scan = new Scan(Bytes.toBytes(rowKeyPrefix));
+    Scan scan = new Scan().withStartRow(Bytes.toBytes(rowKeyPrefix));
     scan.setFilter(simpleWhileMatch);
 
     int[] expected = {0, 1, 10, 11};
@@ -1242,7 +1248,7 @@ public abstract class AbstractTestFilters extends AbstractTest {
       Operator.MUST_PASS_ONE,
       simpleWhileMatch1,
       simpleWhileMatch2);
-    Scan scan = new Scan(Bytes.toBytes(rowKeyPrefix));
+    Scan scan = new Scan().withStartRow(Bytes.toBytes(rowKeyPrefix));
     scan.setFilter(filterList);
 
     int[] expected = {0, 1, 10, 11, 12, 13, 14};
@@ -1266,7 +1272,7 @@ public abstract class AbstractTestFilters extends AbstractTest {
       Operator.MUST_PASS_ALL,
       simpleWhileMatch1,
       simpleWhileMatch2);
-    Scan scan = new Scan(Bytes.toBytes(rowKeyPrefix));
+    Scan scan = new Scan().withStartRow(Bytes.toBytes(rowKeyPrefix));
     scan.setFilter(filterList);
 
     int[] expected = {0, 1, 10, 11};
@@ -1284,7 +1290,7 @@ public abstract class AbstractTestFilters extends AbstractTest {
     ValueFilter valueFilter1 = new ValueFilter(CompareOp.NOT_EQUAL, rowValue2Comparable1);
     WhileMatchFilter simpleWhileMatch1 = new WhileMatchFilter(valueFilter1);
     WhileMatchFilter simpleWhileMatch2 = new WhileMatchFilter(simpleWhileMatch1);
-    Scan scan = new Scan(Bytes.toBytes(rowKeyPrefix));
+    Scan scan = new Scan().withStartRow(Bytes.toBytes(rowKeyPrefix));
     scan.setFilter(simpleWhileMatch2);
 
     int[] expected = {0, 1, 10, 11};
@@ -1554,8 +1560,8 @@ public abstract class AbstractTestFilters extends AbstractTest {
     }
     table.put(puts);
     Scan scan = new Scan();
-    scan.setStartRow(Bytes.toBytes("trandA"));
-    scan.setStopRow(Bytes.toBytes("trandB"));
+    scan.withStartRow(Bytes.toBytes("trandA"));
+    scan.withStopRow(Bytes.toBytes("trandB"));
     RandomRowFilter filter = new RandomRowFilter(0.5f);
     scan.setFilter(filter);
     ResultScanner scanner = table.getScanner(scan);
@@ -1579,7 +1585,7 @@ public abstract class AbstractTestFilters extends AbstractTest {
 
     Scan rootScan = new Scan()
         .addColumn(SharedTestEnvRule.COLUMN_FAMILY, qualToCheck)
-        .setStartRow(rowKey).setStopRow(rowKey);
+        .withStartRow(rowKey).withStopRow(rowKey);
 
     Assert.assertNull("< 1000 should fail",
         getFirst(table, rootScan, CompareOp.LESS, 1000l));
@@ -1670,8 +1676,8 @@ public abstract class AbstractTestFilters extends AbstractTest {
 
     // Make sure that it works with start & end rows: exclude first & last row
     Scan boundedScan = new Scan().addFamily(COLUMN_FAMILY).setFilter(filter)
-        .setStartRow(rowKeys[1])
-        .setStopRow(rowKeys[rowKeys.length - 1]);
+        .withStartRow(rowKeys[1])
+        .withStopRow(rowKeys[rowKeys.length - 1]);
 
     ResultScanner boundedScanner = table.getScanner(boundedScan);
     Result[] boundedResults = boundedScanner.next(rowCount + 2);
@@ -1863,7 +1869,7 @@ public abstract class AbstractTestFilters extends AbstractTest {
     Table table = getDefaultTable();
     table.put(puts);
 
-    Scan scan = new Scan(Bytes.toBytes("pageFilter-"));
+    Scan scan = new Scan().withStartRow(Bytes.toBytes("pageFilter-"));
 
     PageFilter pageFilter = new PageFilter(20);
     scan.setFilter(pageFilter);
@@ -1963,7 +1969,7 @@ public abstract class AbstractTestFilters extends AbstractTest {
         Operator.MUST_PASS_ONE,
         prefixFilter1,
         prefixFilter2);
-    Scan scan = new Scan(Bytes.toBytes(rowKeyPrefix));
+    Scan scan = new Scan().withStartRow(Bytes.toBytes(rowKeyPrefix));
     scan.setFilter(filterList);
 
     try (ResultScanner scanner = table.getScanner(scan)) {
diff --git a/bigtable-client-core-parent/bigtable-hbase-integration-tests-common/src/test/java/com/google/cloud/bigtable/hbase/TestScan.java b/bigtable-client-core-parent/bigtable-hbase-integration-tests-common/src/test/java/com/google/cloud/bigtable/hbase/TestScan.java
index 9f857d8de..944fefd2b 100644
--- a/bigtable-client-core-parent/bigtable-hbase-integration-tests-common/src/test/java/com/google/cloud/bigtable/hbase/TestScan.java
+++ b/bigtable-client-core-parent/bigtable-hbase-integration-tests-common/src/test/java/com/google/cloud/bigtable/hbase/TestScan.java
@@ -80,21 +80,31 @@ public class TestScan extends AbstractTest {
     table.put(new Put(rowKey).addColumn(COLUMN_FAMILY, qual, ts1, values[0]));
     table.put(new Put(rowKey).addColumn(COLUMN_FAMILY, qual, ts2, values[1]));
 
-    try (ResultScanner resultScanner =
-        table.getScanner(new Scan(rowKey, rowFollowing(rowKey)).setTimeRange(0, ts1))) {
+    Scan scan1 = new Scan()
+            .withStartRow(rowKey)
+            .withStopRow(rowFollowing(rowKey))
+            .setTimeRange(0, ts1);
+    try (ResultScanner resultScanner = table.getScanner(scan1)) {
       Assert.assertNull(resultScanner.next());
     }
 
-    try (ResultScanner resultScanner =
-        table.getScanner(new Scan(rowKey, rowFollowing(rowKey)).setTimeRange(0, ts2 + 1))) {
+
+    Scan scan2 = new Scan()
+        .withStartRow(rowKey)
+        .withStopRow(rowFollowing(rowKey))
+        .setTimeRange(0, ts2 + 1);
+    try (ResultScanner resultScanner = table.getScanner(scan2)) {
       Result result = resultScanner.next();
       Assert.assertNotNull(result);
       Assert.assertArrayEquals(values[1],
         CellUtil.cloneValue(result.getColumnLatestCell(COLUMN_FAMILY, qual)));
     }
 
-    try (ResultScanner resultScanner = table.getScanner(
-      new Scan(rowKey, rowFollowing(rowKey)).setTimeRange(0, ts1 + 1))) {
+    Scan scan3 = new Scan()
+        .withStartRow(rowKey)
+        .withStartRow(rowFollowing(rowKey))
+        .setTimeRange(0, ts1 + 1);
+    try (ResultScanner resultScanner = table.getScanner(scan3)) {
       Result result = resultScanner.next();
       Assert.assertNotNull(result);
       Assert.assertArrayEquals(values[0],
@@ -119,9 +129,9 @@ public class TestScan extends AbstractTest {
     table.put(put);
 
     // Get without a qualifer, and confirm all results are returned.
-    Scan scan = new Scan();
-    scan.setStartRow(rowKey)
-        .setStopRow(rowFollowing(rowKey))
+    Scan scan = new Scan()
+        .withStartRow(rowKey)
+        .withStopRow(rowFollowing(rowKey))
         .addFamily(COLUMN_FAMILY);
 
     ResultScanner resultScanner = table.getScanner(scan);
@@ -172,8 +182,8 @@ public class TestScan extends AbstractTest {
     table.put(puts);
 
     Scan scan = new Scan();
-    scan.setStartRow(rowKeys[0])
-        .setStopRow(rowFollowing(rowKeys[rowsToWrite - 1]))
+    scan.withStartRow(rowKeys[0])
+        .withStopRow(rowFollowing(rowKeys[rowsToWrite - 1]))
         .addFamily(COLUMN_FAMILY);
 
     try(ResultScanner resultScanner = table.getScanner(scan)) {
@@ -238,8 +248,8 @@ public class TestScan extends AbstractTest {
     table.put(puts);
 
     Scan scan = new Scan();
-    scan.setStartRow(rowKeys[0])
-        .setStopRow(rowFollowing(rowKeys[rowsToWrite - 1]))
+    scan.withStartRow(rowKeys[0])
+        .withStopRow(rowFollowing(rowKeys[rowsToWrite - 1]))
         .addFamily(COLUMN_FAMILY);
     int deleteCount = 0;
     try (ResultScanner resultScanner = table.getScanner(scan)) {
@@ -264,7 +274,7 @@ public class TestScan extends AbstractTest {
 
     table.put(new Put(rowKey).addColumn(COLUMN_FAMILY, qualifier, value));
     Scan scan = new Scan();
-    scan.setStartRow(rowKey).setStopRow(rowKey);
+    scan.withStartRow(rowKey).withStopRow(rowKey);
     try (ResultScanner resultScanner = table.getScanner(scan)) {
       Result result = resultScanner.next();
       Assert.assertNotNull(result);
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ByteStringer.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/util/ByteStringer.java
similarity index 98%
rename from bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ByteStringer.java
rename to bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/util/ByteStringer.java
index 8a8063427..10804f5a6 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ByteStringer.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/util/ByteStringer.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.google.cloud.bigtable.util;
+package com.google.cloud.bigtable.hbase.util;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ZeroCopyByteStringUtil.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/util/ZeroCopyByteStringUtil.java
similarity index 98%
rename from bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ZeroCopyByteStringUtil.java
rename to bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/util/ZeroCopyByteStringUtil.java
index 3ae296fc5..70b95276c 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ZeroCopyByteStringUtil.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/util/ZeroCopyByteStringUtil.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.google.cloud.bigtable.util;
+package com.google.cloud.bigtable.hbase.util;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/TestBatchExecutor.java b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/TestBatchExecutor.java
index f54bb56ae..2de382a36 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/TestBatchExecutor.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/TestBatchExecutor.java
@@ -38,7 +38,7 @@ import com.google.cloud.bigtable.grpc.scanner.FlatRow;
 import com.google.cloud.bigtable.grpc.scanner.FlatRow.Cell;
 import com.google.cloud.bigtable.hbase.adapters.Adapters;
 import com.google.cloud.bigtable.hbase.adapters.HBaseRequestAdapter;
-import com.google.cloud.bigtable.util.ByteStringer;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestFamilyFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestFamilyFilterAdapter.java
index 6a1e73432..601842eb5 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestFamilyFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestFamilyFilterAdapter.java
@@ -69,8 +69,7 @@ public class TestFamilyFilterAdapter {
             CompareFilter.CompareOp.EQUAL, comparator);
     Assert.assertEquals(
         RowFilter.newBuilder()
-            .setRowKeyRegexFilter(
-                ByteString.copyFrom(ReaderExpressionHelper.quoteRegularExpression(bytes)))
+            .setRowKeyRegexFilter(ReaderExpressionHelper.quoteRegularExpression(bytes))
             .build(),
         adapter.adapt(context, filter));
   }
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestRowFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestRowFilterAdapter.java
index 74a3a5ba0..5518719d9 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestRowFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestRowFilterAdapter.java
@@ -69,8 +69,7 @@ public class TestRowFilterAdapter {
             CompareFilter.CompareOp.EQUAL, comparator);
     Assert.assertEquals(
         RowFilter.newBuilder()
-            .setRowKeyRegexFilter(
-                ByteString.copyFrom(ReaderExpressionHelper.quoteRegularExpression(bytes)))
+            .setRowKeyRegexFilter(ReaderExpressionHelper.quoteRegularExpression(bytes))
             .build(),
         adapter.adapt(context, filter));
   }
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestRowAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestRowAdapter.java
index 30c4b3306..e56d31767 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestRowAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestRowAdapter.java
@@ -23,8 +23,7 @@ import com.google.bigtable.v2.Cell;
 import com.google.bigtable.v2.Column;
 import com.google.bigtable.v2.Family;
 import com.google.bigtable.v2.Row;
-import com.google.cloud.bigtable.hbase.adapters.read.RowAdapter;
-import com.google.cloud.bigtable.util.ByteStringer;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import com.google.protobuf.ByteString;
 
 import org.apache.hadoop.hbase.client.Result;
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestScanAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestScanAdapter.java
index 12b7db77d..9c92be8f2 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestScanAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestScanAdapter.java
@@ -27,7 +27,7 @@ import com.google.bigtable.v2.RowSet;
 import com.google.cloud.bigtable.hbase.BigtableExtendedScan;
 import com.google.cloud.bigtable.hbase.adapters.filters.FilterAdapter;
 import com.google.cloud.bigtable.hbase.adapters.filters.FilterAdapterContext;
-import com.google.cloud.bigtable.util.ByteStringer;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import com.google.cloud.bigtable.util.RowKeyWrapper;
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
@@ -247,10 +247,10 @@ public class TestScanAdapter {
     Mockito.when(filterAdapter.adaptFilter(any(FilterAdapterContext.class), eq(fakeFilter)))
         .thenReturn(Optional.of(RowFilter.getDefaultInstance()));
 
-    Scan scan = new Scan();
-    scan.setStartRow("a".getBytes());
-    scan.setStopRow("z".getBytes());
-    scan.setFilter(fakeFilter);
+    Scan scan = new Scan()
+        .withStartRow("a".getBytes())
+        .withStopRow("z".getBytes())
+        .setFilter(fakeFilter);
 
     Builder adapted = scanAdapter.adapt(scan, throwingReadHooks);
 
@@ -269,9 +269,9 @@ public class TestScanAdapter {
   @Test
   /** Make sure that the scan rowSet is unaffected when the filter is not set */
   public void testNarrowedScanWithoutFilter() {
-    Scan scan = new Scan();
-    scan.setStartRow("a".getBytes());
-    scan.setStopRow("z".getBytes());
+    Scan scan = new Scan()
+        .withStartRow("a".getBytes())
+        .withStopRow("z".getBytes());
 
     RowSet result = scanAdapter.adapt(scan, throwingReadHooks).build().getRows();
     RowSet expected = RowSet.newBuilder()
@@ -287,10 +287,10 @@ public class TestScanAdapter {
 
   @Test
   public void testEmptyFilterList(){
-    Scan scan = new Scan();
-    scan.setFilter(new FilterList());
-    scan.setStartRow("a".getBytes());
-    scan.setStopRow("z".getBytes());
+    Scan scan = new Scan()
+        .setFilter(new FilterList())
+        .withStartRow("a".getBytes())
+        .withStopRow("z".getBytes());
 
     RowSet result = scanAdapter.adapt(scan, throwingReadHooks).build().getRows();
     RowSet expected = RowSet.newBuilder()
diff --git a/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/batch/common/ByteStringUtil.java b/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/batch/common/ByteStringUtil.java
index c79619c44..1ddbc2319 100644
--- a/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/batch/common/ByteStringUtil.java
+++ b/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/batch/common/ByteStringUtil.java
@@ -15,8 +15,8 @@
  */
 package com.google.cloud.bigtable.batch.common;
 
-import com.google.bigtable.repackaged.com.google.cloud.bigtable.util.ByteStringer;
 import com.google.bigtable.repackaged.com.google.protobuf.ByteString;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import com.google.cloud.dataflow.sdk.io.range.ByteKey;
 
 /**
diff --git a/bigtable-hbase-1.x-parent/bigtable-hbase-1.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java b/bigtable-hbase-1.x-parent/bigtable-hbase-1.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java
index 34d16b029..5cd650218 100644
--- a/bigtable-hbase-1.x-parent/bigtable-hbase-1.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java
+++ b/bigtable-hbase-1.x-parent/bigtable-hbase-1.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java
@@ -15,7 +15,7 @@
  */
 package com.google.cloud.bigtable.hbase;
 
-import io.grpc.internal.GrpcUtil;
+import com.google.cloud.bigtable.util.ThreadUtil;
 import org.apache.hadoop.hbase.shaded.org.apache.commons.lang.RandomStringUtils;
 import org.apache.hadoop.hbase.HColumnDescriptor;
 import org.apache.hadoop.hbase.HTableDescriptor;
@@ -101,7 +101,7 @@ public class ManyThreadDriver {
     final TableName tableName = TableName.valueOf(tableNameStr);
     final AtomicBoolean finished = new AtomicBoolean(false);
     ExecutorService executor = Executors.newFixedThreadPool(numThreads,
-      GrpcUtil.getThreadFactory("WORK_EXECUTOR-%d", true));
+      ThreadUtil.getThreadFactory("WORK_EXECUTOR-%d", true));
     ScheduledExecutorService finishExecutor = setupShutdown(finished);
     try (Connection connection = BigtableConfiguration.connect(projectId, instanceId)) {
       setupTable(tableName, connection);
@@ -144,7 +144,7 @@ public class ManyThreadDriver {
 
   static ScheduledExecutorService setupShutdown(final AtomicBoolean finished) {
     ScheduledExecutorService finishExecutor =
-        Executors.newScheduledThreadPool(1, GrpcUtil.getThreadFactory("FINISH_SCHEDULER-%d", true));
+        Executors.newScheduledThreadPool(1, ThreadUtil.getThreadFactory("FINISH_SCHEDULER-%d", true));
     finishExecutor.schedule(new Runnable() {
       @Override
       public void run() {
diff --git a/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java b/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java
index 8446ab4fb..3da057ca2 100644
--- a/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java
+++ b/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java
@@ -15,7 +15,7 @@
  */
 package com.google.cloud.bigtable.hbase;
 
-import io.grpc.internal.GrpcUtil;
+import com.google.cloud.bigtable.util.ThreadUtil;
 import org.apache.hadoop.hbase.shaded.org.apache.commons.lang.RandomStringUtils;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.client.Admin;
@@ -101,7 +101,7 @@ public class ManyThreadDriver {
     final TableName tableName = TableName.valueOf(tableNameStr);
     final AtomicBoolean finished = new AtomicBoolean(false);
     ExecutorService executor = Executors.newFixedThreadPool(numThreads,
-      GrpcUtil.getThreadFactory("WORK_EXECUTOR-%d", true));
+      ThreadUtil.getThreadFactory("WORK_EXECUTOR-%d", true));
     ScheduledExecutorService finishExecutor = setupShutdown(finished);
     try (Connection connection = BigtableConfiguration.connect(projectId, instanceId)) {
       setupTable(tableName, connection);
@@ -144,7 +144,7 @@ public class ManyThreadDriver {
 
   static ScheduledExecutorService setupShutdown(final AtomicBoolean finished) {
     ScheduledExecutorService finishExecutor =
-        Executors.newScheduledThreadPool(1, GrpcUtil.getThreadFactory("FINISH_SCHEDULER-%d", true));
+        Executors.newScheduledThreadPool(1, ThreadUtil.getThreadFactory("FINISH_SCHEDULER-%d", true));
     finishExecutor.schedule(new Runnable() {
       @Override
       public void run() {
diff --git a/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/RecreateConnectionDriver.java b/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/RecreateConnectionDriver.java
index 9bf893668..af0356c31 100644
--- a/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/RecreateConnectionDriver.java
+++ b/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/RecreateConnectionDriver.java
@@ -23,6 +23,7 @@ import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import com.google.cloud.bigtable.util.ThreadUtil;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.client.Admin;
@@ -35,8 +36,6 @@ import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
 import org.apache.hadoop.hbase.shaded.org.apache.commons.lang.RandomStringUtils;
 import org.apache.hadoop.hbase.util.Bytes;
 
-import io.grpc.internal.GrpcUtil;
-
 public class RecreateConnectionDriver {
   
   static final byte[] COLUMN_FAMILY = Bytes.toBytes("cf");
@@ -54,7 +53,7 @@ public class RecreateConnectionDriver {
     final TableName tableName = TableName.valueOf(tableNameStr);
     final AtomicBoolean finished = new AtomicBoolean(false);
     ExecutorService executor = Executors.newFixedThreadPool(numThreads,
-      GrpcUtil.getThreadFactory("WORK_EXECUTOR-%d", true));
+      ThreadUtil.getThreadFactory("WORK_EXECUTOR-%d", true));
     ScheduledExecutorService finishExecutor = setupShutdown(finished);
     try (Connection connection = connect(projectId, instanceId)) {
       setupTable(tableName, connection);
@@ -112,7 +111,7 @@ public class RecreateConnectionDriver {
 
   static ScheduledExecutorService setupShutdown(final AtomicBoolean finished) {
     ScheduledExecutorService finishExecutor =
-        Executors.newScheduledThreadPool(1, GrpcUtil.getThreadFactory("FINISH_SCHEDULER-%d", true));
+        Executors.newScheduledThreadPool(1, ThreadUtil.getThreadFactory("FINISH_SCHEDULER-%d", true));
     finishExecutor.schedule(new Runnable() {
       @Override
       public void run() {