You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by el...@apache.org on 2016/03/07 19:28:08 UTC

[30/59] [partial] calcite git commit: [CALCITE-1078] Detach avatica from the core calcite Maven project

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufSerializationTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufSerializationTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufSerializationTest.java
new file mode 100644
index 0000000..cd8a329
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufSerializationTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.remote;
+
+import org.apache.calcite.avatica.ColumnMetaData.Rep;
+import org.apache.calcite.avatica.Meta.Signature;
+import org.apache.calcite.avatica.Meta.StatementHandle;
+import org.apache.calcite.avatica.proto.Common.WireMessage;
+import org.apache.calcite.avatica.proto.Requests;
+import org.apache.calcite.avatica.remote.Service.Request;
+
+import com.google.protobuf.HBaseZeroCopyByteString;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Protobuf serialization tests.
+ */
+public class ProtobufSerializationTest {
+
+  private Signature getSignature() {
+    return null;
+  }
+
+  private List<TypedValue> getTypedValues() {
+    List<TypedValue> paramValues =
+        Arrays.asList(TypedValue.create(Rep.BOOLEAN.name(), Boolean.TRUE),
+            TypedValue.create(Rep.STRING.name(), "string"));
+    return paramValues;
+  }
+
+  @Test public void testExecuteSerialization() throws Exception {
+    Service.ExecuteRequest executeRequest = new Service.ExecuteRequest(
+        new StatementHandle("connection", 12345, getSignature()), getTypedValues(), 0);
+
+    Requests.ExecuteRequest pbExecuteRequest = executeRequest.serialize();
+    ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
+    pbExecuteRequest.writeTo(baos);
+
+    byte[] serialized = baos.toByteArray();
+    baos.reset();
+    WireMessage wireMsg = WireMessage.newBuilder().setName(Requests.ExecuteRequest.class.getName())
+        .setWrappedMessage(HBaseZeroCopyByteString.wrap(serialized)).build();
+    wireMsg.writeTo(baos);
+    serialized = baos.toByteArray();
+
+    ProtobufTranslation translator = new ProtobufTranslationImpl();
+
+    Request newRequest = translator.parseRequest(serialized);
+
+    Assert.assertEquals(executeRequest, newRequest);
+  }
+
+}
+
+// End ProtobufSerializationTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufServiceTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufServiceTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufServiceTest.java
new file mode 100644
index 0000000..a29ee34
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufServiceTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.remote;
+
+import org.apache.calcite.avatica.proto.Requests;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Test class for ProtobufService.
+ */
+public class ProtobufServiceTest {
+
+  @Test public void testCastProtobufMessage() {
+    final Requests.CommitRequest commitReq =
+        Requests.CommitRequest.newBuilder().setConnectionId("cnxn1").build();
+    final Requests.RollbackRequest rollbackReq =
+        Requests.RollbackRequest.newBuilder().setConnectionId("cnxn1").build();
+
+    assertEquals(commitReq,
+        ProtobufService.castProtobufMessage(commitReq, Requests.CommitRequest.class));
+    assertEquals(rollbackReq,
+        ProtobufService.castProtobufMessage(rollbackReq, Requests.RollbackRequest.class));
+
+    try {
+      ProtobufService.castProtobufMessage(commitReq, Requests.RollbackRequest.class);
+      fail("Should have seen IllegalArgumentException casting CommitRequest into RollbackRequest");
+    } catch (IllegalArgumentException e) {
+      // Expected
+    }
+
+    try {
+      ProtobufService.castProtobufMessage(rollbackReq, Requests.CommitRequest.class);
+      fail("Should have seen IllegalArgumentException casting RollbackRequest into CommitRequest");
+    } catch (IllegalArgumentException e) {
+      // Expected
+    }
+  }
+}
+
+// End ProtobufServiceTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
new file mode 100644
index 0000000..c75bdb0
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
@@ -0,0 +1,376 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.remote;
+
+import org.apache.calcite.avatica.AvaticaParameter;
+import org.apache.calcite.avatica.AvaticaSeverity;
+import org.apache.calcite.avatica.ColumnMetaData;
+import org.apache.calcite.avatica.ColumnMetaData.ArrayType;
+import org.apache.calcite.avatica.ColumnMetaData.Rep;
+import org.apache.calcite.avatica.ColumnMetaData.ScalarType;
+import org.apache.calcite.avatica.ConnectionPropertiesImpl;
+import org.apache.calcite.avatica.Meta;
+import org.apache.calcite.avatica.Meta.Frame;
+import org.apache.calcite.avatica.Meta.Signature;
+import org.apache.calcite.avatica.Meta.Style;
+import org.apache.calcite.avatica.MetaImpl;
+import org.apache.calcite.avatica.QueryState;
+import org.apache.calcite.avatica.remote.Service.CatalogsRequest;
+import org.apache.calcite.avatica.remote.Service.CloseConnectionRequest;
+import org.apache.calcite.avatica.remote.Service.CloseConnectionResponse;
+import org.apache.calcite.avatica.remote.Service.CloseStatementRequest;
+import org.apache.calcite.avatica.remote.Service.CloseStatementResponse;
+import org.apache.calcite.avatica.remote.Service.ColumnsRequest;
+import org.apache.calcite.avatica.remote.Service.CommitRequest;
+import org.apache.calcite.avatica.remote.Service.CommitResponse;
+import org.apache.calcite.avatica.remote.Service.ConnectionSyncRequest;
+import org.apache.calcite.avatica.remote.Service.ConnectionSyncResponse;
+import org.apache.calcite.avatica.remote.Service.CreateStatementRequest;
+import org.apache.calcite.avatica.remote.Service.CreateStatementResponse;
+import org.apache.calcite.avatica.remote.Service.DatabasePropertyRequest;
+import org.apache.calcite.avatica.remote.Service.DatabasePropertyResponse;
+import org.apache.calcite.avatica.remote.Service.ErrorResponse;
+import org.apache.calcite.avatica.remote.Service.ExecuteResponse;
+import org.apache.calcite.avatica.remote.Service.FetchRequest;
+import org.apache.calcite.avatica.remote.Service.FetchResponse;
+import org.apache.calcite.avatica.remote.Service.OpenConnectionRequest;
+import org.apache.calcite.avatica.remote.Service.OpenConnectionResponse;
+import org.apache.calcite.avatica.remote.Service.PrepareAndExecuteRequest;
+import org.apache.calcite.avatica.remote.Service.PrepareRequest;
+import org.apache.calcite.avatica.remote.Service.PrepareResponse;
+import org.apache.calcite.avatica.remote.Service.Request;
+import org.apache.calcite.avatica.remote.Service.Response;
+import org.apache.calcite.avatica.remote.Service.ResultSetResponse;
+import org.apache.calcite.avatica.remote.Service.RollbackRequest;
+import org.apache.calcite.avatica.remote.Service.RollbackResponse;
+import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse;
+import org.apache.calcite.avatica.remote.Service.SchemasRequest;
+import org.apache.calcite.avatica.remote.Service.SyncResultsRequest;
+import org.apache.calcite.avatica.remote.Service.SyncResultsResponse;
+import org.apache.calcite.avatica.remote.Service.TableTypesRequest;
+import org.apache.calcite.avatica.remote.Service.TablesRequest;
+import org.apache.calcite.avatica.remote.Service.TypeInfoRequest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.sql.DatabaseMetaData;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests serialization of requests and response objects.
+ *
+ * @param <T> The object class being tested
+ */
+@RunWith(Parameterized.class)
+public class ProtobufTranslationImplTest<T> {
+
+  /**
+   * Simple function definition that acts as an identity.
+   *
+   * @param <A> Argument type
+   */
+  private interface IdentityFunction<A> {
+    A apply(A obj) throws IOException;
+  }
+
+  /**
+   * Identity function that accepts a request, serializes it to protobuf, and converts it back.
+   */
+  private static class RequestFunc implements IdentityFunction<Request> {
+    private final ProtobufTranslation translation;
+
+    public RequestFunc(ProtobufTranslation translation) {
+      this.translation = translation;
+    }
+
+    public Request apply(Request request) throws IOException {
+      // Serialize and then re-parse the request
+      return translation.parseRequest(translation.serializeRequest(request));
+    }
+  }
+
+  /**
+   * Identity function that accepts a response, serializes it to protobuf, and converts it back.
+   */
+  private static class ResponseFunc implements IdentityFunction<Response> {
+    private final ProtobufTranslation translation;
+
+    public ResponseFunc(ProtobufTranslation translation) {
+      this.translation = translation;
+    }
+
+    public Response apply(Response response) throws IOException {
+      // Serialize and then re-pare the response
+      return translation.parseResponse(translation.serializeResponse(response));
+    }
+  }
+
+  @Parameters
+  public static List<Object[]> parameters() {
+    List<Object[]> params = new ArrayList<>();
+
+    // The impl we're testing
+    ProtobufTranslationImpl translation = new ProtobufTranslationImpl();
+
+    // Identity transformation for Requests
+    RequestFunc requestFunc = new RequestFunc(translation);
+    // Identity transformation for Responses
+    ResponseFunc responseFunc = new ResponseFunc(translation);
+
+    List<Request> requests = getRequests();
+    List<Request> requestsWithNulls = getRequestsWithNulls();
+    List<Response> responses = getResponses();
+
+    // Requests
+    for (Request request : requests) {
+      params.add(new Object[] {request, requestFunc});
+    }
+
+    // Requests with nulls in parameters
+    for (Request request : requestsWithNulls) {
+      params.add(new Object[] {request, requestFunc});
+    }
+
+    // Responses
+    for (Response response : responses) {
+      params.add(new Object[] {response, responseFunc});
+    }
+
+    return params;
+  }
+
+  /**
+   * Generates a collection of Requests whose serialization will be tested.
+   */
+  private static List<Request> getRequests() {
+    LinkedList<Request> requests = new LinkedList<>();
+
+    requests.add(new CatalogsRequest());
+    requests.add(new DatabasePropertyRequest());
+    requests.add(new SchemasRequest("connectionId", "catalog", "schemaPattern"));
+    requests.add(
+        new TablesRequest("connectionId", "catalog", "schemaPattern", "tableNamePattern",
+            Arrays.asList("STRING", "BOOLEAN", "INT")));
+    requests.add(new TableTypesRequest());
+    requests.add(
+        new ColumnsRequest("connectionId", "catalog", "schemaPattern", "tableNamePattern",
+            "columnNamePattern"));
+    requests.add(new TypeInfoRequest());
+    requests.add(
+        new PrepareAndExecuteRequest("connectionId", Integer.MAX_VALUE, "sql",
+            Long.MAX_VALUE));
+    requests.add(new PrepareRequest("connectionId", "sql", Long.MAX_VALUE));
+
+    List<TypedValue> paramValues =
+        Arrays.asList(TypedValue.create(Rep.BOOLEAN.name(), Boolean.TRUE),
+            TypedValue.create(Rep.STRING.name(), "string"));
+    FetchRequest fetchRequest = new FetchRequest("connectionId", Integer.MAX_VALUE,
+        Long.MAX_VALUE, Integer.MAX_VALUE);
+    requests.add(fetchRequest);
+
+    requests.add(new CreateStatementRequest("connectionId"));
+    requests.add(new CloseStatementRequest("connectionId", Integer.MAX_VALUE));
+    Map<String, String> info = new HashMap<>();
+    info.put("param1", "value1");
+    info.put("param2", "value2");
+    requests.add(new OpenConnectionRequest("connectionId", info));
+    requests.add(new CloseConnectionRequest("connectionId"));
+    requests.add(
+        new ConnectionSyncRequest("connectionId",
+            new ConnectionPropertiesImpl(Boolean.FALSE, Boolean.FALSE,
+                Integer.MAX_VALUE, "catalog", "schema")));
+
+    requests.add(new SyncResultsRequest("connectionId", 12345, getSqlQueryState(), 150));
+    requests.add(new SyncResultsRequest("connectionId2", 54321, getMetadataQueryState1(), 0));
+    requests.add(new SyncResultsRequest("connectionId3", 5, getMetadataQueryState2(), 10));
+
+    requests.add(new CommitRequest("connectionId"));
+    requests.add(new RollbackRequest("connectionId"));
+
+    return requests;
+  }
+
+  private static QueryState getSqlQueryState() {
+    return new QueryState("SELECT * from TABLE");
+  }
+
+  private static QueryState getMetadataQueryState1() {
+    return new QueryState(MetaDataOperation.GET_COLUMNS, new Object[] {
+      "",
+      null,
+      "%",
+      "%"
+    });
+  }
+
+  private static QueryState getMetadataQueryState2() {
+    return new QueryState(MetaDataOperation.GET_CATALOGS, new Object[0]);
+  }
+
+  private static List<Request> getRequestsWithNulls() {
+    LinkedList<Request> requests = new LinkedList<>();
+
+    // We're pretty fast and loose on what can be null.
+    requests.add(new SchemasRequest(null, null, null));
+    // Repeated fields default to an empty list
+    requests.add(new TablesRequest(null, null, null, null, Collections.<String>emptyList()));
+    requests.add(new ColumnsRequest(null, null, null, null, null));
+    requests.add(new PrepareAndExecuteRequest(null, 0, null, 0));
+    requests.add(new PrepareRequest(null, null, 0));
+    requests.add(new CreateStatementRequest(null));
+    requests.add(new CloseStatementRequest(null, 0));
+    requests.add(new OpenConnectionRequest(null, null));
+    requests.add(new CloseConnectionRequest(null));
+    requests.add(new ConnectionSyncRequest(null, null));
+
+    return requests;
+  }
+
+  private static ColumnMetaData getArrayColumnMetaData(ScalarType componentType, int index,
+      String name) {
+    ArrayType arrayType = ColumnMetaData.array(componentType, "Array", Rep.ARRAY);
+    return new ColumnMetaData(
+        index, false, true, false, false, DatabaseMetaData.columnNullable,
+        true, -1, name, name, null,
+        0, 0, null, null, arrayType, true, false, false,
+        "ARRAY");
+  }
+
+  /**
+   * Generates a collection of Responses whose serialization will be tested.
+   */
+  private static List<Response> getResponses() {
+    final RpcMetadataResponse rpcMetadata = new RpcMetadataResponse("localhost:8765");
+    LinkedList<Response> responses = new LinkedList<>();
+
+    // Nested classes (Signature, ColumnMetaData, CursorFactory, etc) are implicitly getting tested)
+
+    // Stub out the metadata for a row
+    ScalarType arrayComponentType = ColumnMetaData.scalar(Types.INTEGER, "integer", Rep.INTEGER);
+    ColumnMetaData arrayColumnMetaData = getArrayColumnMetaData(arrayComponentType, 2, "counts");
+    List<ColumnMetaData> columns =
+        Arrays.asList(MetaImpl.columnMetaData("str", 0, String.class),
+            MetaImpl.columnMetaData("count", 1, Integer.class),
+            arrayColumnMetaData);
+    List<AvaticaParameter> params =
+        Arrays.asList(
+            new AvaticaParameter(false, 10, 0, Types.VARCHAR, "VARCHAR",
+                String.class.getName(), "str"));
+    Meta.CursorFactory cursorFactory = Meta.CursorFactory.create(Style.LIST, Object.class,
+        Arrays.asList("str", "count", "counts"));
+    // The row values
+    List<Object> rows = new ArrayList<>();
+    rows.add(new Object[] {"str_value1", 50, Arrays.asList(1, 2, 3)});
+    rows.add(new Object[] {"str_value2", 100, Arrays.asList(1)});
+
+    // Create the signature and frame using the metadata and values
+    Signature signature = Signature.create(columns, "sql", params, cursorFactory,
+        Meta.StatementType.SELECT);
+    Frame frame = Frame.create(Integer.MAX_VALUE, true, rows);
+
+    // And then create a ResultSetResponse
+    ResultSetResponse results1 = new ResultSetResponse("connectionId", Integer.MAX_VALUE, true,
+        signature, frame, Long.MAX_VALUE, rpcMetadata);
+    responses.add(results1);
+
+    responses.add(new CloseStatementResponse(rpcMetadata));
+
+    ConnectionPropertiesImpl connProps = new ConnectionPropertiesImpl(false, true,
+        Integer.MAX_VALUE, "catalog", "schema");
+    responses.add(new ConnectionSyncResponse(connProps, rpcMetadata));
+
+    responses.add(new OpenConnectionResponse(rpcMetadata));
+    responses.add(new CloseConnectionResponse(rpcMetadata));
+
+    responses.add(new CreateStatementResponse("connectionId", Integer.MAX_VALUE, rpcMetadata));
+
+    Map<Meta.DatabaseProperty, Object> propertyMap = new HashMap<>();
+    for (Meta.DatabaseProperty prop : Meta.DatabaseProperty.values()) {
+      propertyMap.put(prop, prop.defaultValue);
+    }
+    responses.add(new DatabasePropertyResponse(propertyMap, rpcMetadata));
+
+    responses.add(
+        new ExecuteResponse(Arrays.asList(results1, results1, results1), false, rpcMetadata));
+    responses.add(new FetchResponse(frame, false, false, rpcMetadata));
+    responses.add(new FetchResponse(frame, true, true, rpcMetadata));
+    responses.add(new FetchResponse(frame, false, true, rpcMetadata));
+    responses.add(
+        new PrepareResponse(
+            new Meta.StatementHandle("connectionId", Integer.MAX_VALUE, signature),
+            rpcMetadata));
+
+    StringWriter sw = new StringWriter();
+    new Exception().printStackTrace(new PrintWriter(sw));
+    responses.add(
+        new ErrorResponse(Collections.singletonList(sw.toString()), "Test Error Message",
+            ErrorResponse.UNKNOWN_ERROR_CODE, ErrorResponse.UNKNOWN_SQL_STATE,
+            AvaticaSeverity.WARNING, rpcMetadata));
+
+    // No more results, statement not missing
+    responses.add(new SyncResultsResponse(false, false, rpcMetadata));
+    // Missing statement, no results
+    responses.add(new SyncResultsResponse(false, true, rpcMetadata));
+    // More results, no missing statement
+    responses.add(new SyncResultsResponse(true, false, rpcMetadata));
+
+    // Some tests to make sure ErrorResponse doesn't fail.
+    responses.add(new ErrorResponse((List<String>) null, null, 0, null, null, null));
+    responses.add(
+        new ErrorResponse(Arrays.asList("stacktrace1", "stacktrace2"), null, 0, null, null, null));
+
+    responses.add(new CommitResponse());
+    responses.add(new RollbackResponse());
+
+    return responses;
+  }
+
+  private final T object;
+  private final IdentityFunction<T> function;
+
+  public ProtobufTranslationImplTest(T object, IdentityFunction<T> func) {
+    this.object = object;
+    this.function = func;
+  }
+
+  @Test
+  public void testSerialization() throws Exception {
+    // Function acts as opposite sides of the transport.
+    // An object (a request or response) starts on one side
+    // of the transport, serialized, "sent" over the transport
+    // and then reconstituted. The object on either side should
+    // be equivalent.
+    assertEquals(object, this.function.apply(object));
+  }
+}
+
+// End ProtobufTranslationImplTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/TypedValueTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/TypedValueTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/TypedValueTest.java
new file mode 100644
index 0000000..28fe6f6
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/TypedValueTest.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.remote;
+
+import org.apache.calcite.avatica.ColumnMetaData.Rep;
+import org.apache.calcite.avatica.util.ByteString;
+
+import org.junit.Test;
+
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test serialization of TypedValue.
+ */
+public class TypedValueTest {
+
+  private void serializeAndEqualityCheck(TypedValue value) {
+    TypedValue copy = TypedValue.fromProto(value.toProto());
+
+    assertEquals(value.type, copy.type);
+    assertEquals(value.value, copy.value);
+  }
+
+  @Test
+  public void testBoolean() {
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.PRIMITIVE_BOOLEAN, true));
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.BOOLEAN, Boolean.TRUE));
+  }
+
+  @Test
+  public void testByte() {
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.PRIMITIVE_BYTE, (byte) 4));
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.BYTE, Byte.valueOf((byte) 4)));
+  }
+
+  @Test
+  public void testShort() {
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.PRIMITIVE_SHORT, (short) 42));
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.SHORT, Short.valueOf((short) 42)));
+  }
+
+  @Test
+  public void testInteger() {
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.PRIMITIVE_INT, (int) 42000));
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.INTEGER, Integer.valueOf((int) 42000)));
+  }
+
+  @Test
+  public void testLong() {
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.PRIMITIVE_LONG, Long.MAX_VALUE));
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.LONG, Long.valueOf(Long.MAX_VALUE)));
+  }
+
+  @Test
+  public void testFloat() {
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.PRIMITIVE_FLOAT, 3.14159f));
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.FLOAT, Float.valueOf(3.14159f)));
+  }
+
+  @Test
+  public void testDouble() {
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.PRIMITIVE_DOUBLE, Double.MAX_VALUE));
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.DOUBLE, Double.valueOf(Double.MAX_VALUE)));
+  }
+
+  @Test
+  public void testChar() {
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.PRIMITIVE_CHAR, 'c'));
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.CHARACTER, Character.valueOf('c')));
+  }
+
+  @Test
+  public void testString() {
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.STRING, "qwertyasdf"));
+  }
+
+  @Test
+  public void testByteString() {
+    serializeAndEqualityCheck(
+        TypedValue.ofLocal(Rep.BYTE_STRING,
+            new ByteString("qwertyasdf".getBytes(StandardCharsets.UTF_8))));
+  }
+
+  @Test
+  public void testSqlDate() {
+    // days since epoch
+    serializeAndEqualityCheck(TypedValue.ofLocal(Rep.JAVA_SQL_DATE, 25));
+  }
+
+  @Test
+  public void testUtilDate() {
+    serializeAndEqualityCheck(
+        TypedValue.ofLocal(Rep.JAVA_UTIL_DATE, System.currentTimeMillis()));
+  }
+
+  @Test
+  public void testSqlTime() {
+    // millis since epoch
+    serializeAndEqualityCheck(
+        TypedValue.ofLocal(Rep.JAVA_SQL_TIME, 42 * 1024 * 1024));
+  }
+
+  @Test
+  public void testSqlTimestamp() {
+    serializeAndEqualityCheck(
+        TypedValue.ofLocal(Rep.JAVA_SQL_TIMESTAMP, 42L * 1024 * 1024 * 1024));
+  }
+}
+
+// End TypedValueTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaClientRuntimeExceptionTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaClientRuntimeExceptionTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaClientRuntimeExceptionTest.java
new file mode 100644
index 0000000..411f29c
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaClientRuntimeExceptionTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.test;
+
+import org.apache.calcite.avatica.AvaticaClientRuntimeException;
+import org.apache.calcite.avatica.AvaticaSeverity;
+import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test class for {@link AvaticaClientRuntimeException}.
+ */
+public class AvaticaClientRuntimeExceptionTest {
+
+  @Test public void testGetters() {
+    final String errorMsg = "My error message";
+    final int errorCode = 10;
+    final String sqlState = "abc12";
+    final AvaticaSeverity severity = AvaticaSeverity.ERROR;
+    final List<String> stacktraces = Arrays.asList("my stack trace");
+    final RpcMetadataResponse metadata = new RpcMetadataResponse("localhost:8765");
+    AvaticaClientRuntimeException e = new AvaticaClientRuntimeException(errorMsg, errorCode,
+        sqlState, severity, stacktraces, metadata);
+    assertEquals(errorMsg, e.getMessage());
+    assertEquals(errorCode, e.getErrorCode());
+    assertEquals(severity, e.getSeverity());
+    assertEquals(stacktraces, e.getServerExceptions());
+    assertEquals(metadata, e.getRpcMetadata());
+  }
+
+}
+
+// End AvaticaClientRuntimeExceptionTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaSeverityTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaSeverityTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaSeverityTest.java
new file mode 100644
index 0000000..945f959
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaSeverityTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.test;
+
+import org.apache.calcite.avatica.AvaticaSeverity;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for {@link AvaticaSeverity}.
+ */
+public class AvaticaSeverityTest {
+
+  @Test
+  public void testProtobufSerialization() {
+    for (AvaticaSeverity severity : AvaticaSeverity.values()) {
+      assertEquals(severity, AvaticaSeverity.fromProto(severity.toProto()));
+    }
+  }
+
+}
+
+// End AvaticaSeverityTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaSqlExceptionTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaSqlExceptionTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaSqlExceptionTest.java
new file mode 100644
index 0000000..3d7b5b0
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaSqlExceptionTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.test;
+
+import org.apache.calcite.avatica.AvaticaSqlException;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for {@link AvaticaSqlException}.
+ */
+public class AvaticaSqlExceptionTest {
+
+  @Test public void testGetters() {
+    final String msg = "My query failed!";
+    final int code = 42;
+    final String sql = "SELECT foo FROM bar;";
+    final String stacktrace = "My Stack Trace";
+    final String server = "localhost:8765";
+
+    AvaticaSqlException e = new AvaticaSqlException(msg, sql, code, Arrays.asList(stacktrace),
+        server);
+    assertTrue(e.getMessage().contains(msg));
+    assertEquals(code, e.getErrorCode());
+    assertEquals(sql, e.getSQLState());
+    assertEquals(1, e.getStackTraces().size());
+    assertEquals(stacktrace, e.getStackTraces().get(0));
+    assertEquals(server, e.getRemoteServer());
+  }
+
+}
+
+// End AvaticaSqlExceptionTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaUtilsTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaUtilsTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaUtilsTest.java
new file mode 100644
index 0000000..850cbfd
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaUtilsTest.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.test;
+
+import org.apache.calcite.avatica.AvaticaUtils;
+
+import org.junit.Test;
+
+import java.math.BigInteger;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * Unit test for Avatica utilities.
+ */
+public class AvaticaUtilsTest {
+  @Test public void testInstantiatePlugin() {
+    final String s =
+        AvaticaUtils.instantiatePlugin(String.class, "java.lang.String");
+    assertThat(s, is(""));
+
+    // No default constructor or INSTANCE member
+    try {
+      final Integer i =
+          AvaticaUtils.instantiatePlugin(Integer.class, "java.lang.Integer");
+      fail("expected error, got " + i);
+    } catch (Throwable e) {
+      assertThat(e.getMessage(),
+          is("Property 'java.lang.Integer' not valid for plugin type java.lang.Integer"));
+    }
+
+    final BigInteger b =
+        AvaticaUtils.instantiatePlugin(BigInteger.class, "java.math.BigInteger#ONE");
+    assertThat(b, is(BigInteger.ONE));
+
+    try {
+      final BigInteger b2 =
+          AvaticaUtils.instantiatePlugin(BigInteger.class,
+              "java.math.BigInteger.ONE");
+      fail("expected error, got " + b2);
+    } catch (Throwable e) {
+      assertThat(e.getMessage(),
+          is("Property 'java.math.BigInteger.ONE' not valid for plugin type java.math.BigInteger"));
+    }
+  }
+
+  /** Unit test for
+   * {@link org.apache.calcite.avatica.AvaticaUtils#unique(java.lang.String)}. */
+  @Test public void testUnique() {
+    // Below, the "probably" comments indicate the strings that will be
+    // generated the first time you run the test.
+    final Set<String> list = new LinkedHashSet<>();
+    list.add(AvaticaUtils.unique("a")); // probably "a"
+    assertThat(list.size(), is(1));
+    list.add(AvaticaUtils.unique("a")); // probably "a_1"
+    assertThat(list.size(), is(2));
+    list.add(AvaticaUtils.unique("b")); // probably "b"
+    assertThat(list.size(), is(3));
+    list.add(AvaticaUtils.unique("a_1")); // probably "a_1_3"
+    assertThat(list.size(), is(4));
+    list.add(AvaticaUtils.unique("A")); // probably "A"
+    assertThat(list.size(), is(5));
+    list.add(AvaticaUtils.unique("a")); // probably "a_5"
+    assertThat(list.size(), is(6));
+  }
+}
+
+// End AvaticaUtilsTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/test/ConnectStringParserTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/test/ConnectStringParserTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/test/ConnectStringParserTest.java
new file mode 100644
index 0000000..cdc8d8a
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/test/ConnectStringParserTest.java
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.test;
+
+import org.apache.calcite.avatica.ConnectStringParser;
+
+import org.junit.Test;
+
+import java.sql.SQLException;
+import java.util.Properties;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+/**
+ * Unit test for JDBC connect string parser, {@link ConnectStringParser}. The
+ * ConnectStringParser is adapted from code in Mondrian, but most of the tests
+ * below were unfortunately "reinvented" prior to having the Mondrian unit tests
+ * in hand.
+ */
+public class ConnectStringParserTest {
+  /**
+   * Tests simple connect string. Adapted from Mondrian tests.
+   */
+  @Test public void testSimpleStrings() throws Throwable {
+    Properties props = ConnectStringParser.parse("foo=x;bar=y;foo=z");
+    assertEquals(
+        "bar",
+        "y",
+        props.get("bar"));
+    assertNull(
+        "BAR",
+        props.get("BAR")); // case-sensitive, unlike Mondrian
+    assertEquals(
+        "last foo",
+        "z",
+        props.get("foo"));
+    assertNull(
+        "key=\" bar\"",
+        props.get(" bar"));
+    assertNull(
+        "bogus key",
+        props.get("kipper"));
+    assertEquals(
+        "param count",
+        2,
+        props.size());
+
+    String synth = ConnectStringParser.getParamString(props);
+    Properties synthProps = ConnectStringParser.parse(synth);
+    assertEquals("reversible", props, synthProps);
+  }
+
+  /**
+   * Tests complex connect strings. Adapted directly from Mondrian tests.
+   */
+  @Test public void testComplexStrings() throws Throwable {
+    Properties props =
+        ConnectStringParser.parse("normalProp=value;"
+            + "emptyValue=;"
+            + " spaceBeforeProp=abc;"
+            + " spaceBeforeAndAfterProp =def;"
+            + " space in prop = foo bar ;"
+            + "equalsInValue=foo=bar;"
+            + "semiInProp;Name=value;"
+            + " singleQuotedValue = 'single quoted value ending in space ' ;"
+            + " doubleQuotedValue = "
+            + "\"=double quoted value preceded by equals\" ;"
+            + " singleQuotedValueWithSemi = 'one; two';"
+            + " singleQuotedValueWithSpecials = 'one; two \"three''four=five'");
+
+    assertEquals(
+        "param count",
+        11,
+        props.size());
+
+    String value;
+    value = (String) props.get("normalProp");
+    assertEquals("value", value);
+    value = (String) props.get("emptyValue");
+    assertEquals("", value); // empty string, not null!
+    value = (String) props.get("spaceBeforeProp");
+    assertEquals("abc", value);
+    value = (String) props.get("spaceBeforeAndAfterProp");
+    assertEquals("def", value);
+    value = (String) props.get("space in prop");
+    assertEquals(value, "foo bar");
+    value = (String) props.get("equalsInValue");
+    assertEquals("foo=bar", value);
+    value = (String) props.get("semiInProp;Name");
+    assertEquals("value", value);
+    value = (String) props.get("singleQuotedValue");
+    assertEquals("single quoted value ending in space ", value);
+    value = (String) props.get("doubleQuotedValue");
+    assertEquals("=double quoted value preceded by equals", value);
+    value = (String) props.get("singleQuotedValueWithSemi");
+    assertEquals(value, "one; two");
+    value = (String) props.get("singleQuotedValueWithSpecials");
+    assertEquals(value, "one; two \"three'four=five");
+  }
+
+  /**
+   * Tests for specific errors thrown by the parser.
+   */
+  @Test public void testConnectStringErrors() throws Throwable {
+    // force some parsing errors
+    try {
+      ConnectStringParser.parse("key='can't parse'");
+      fail("quoted value ended too soon");
+    } catch (SQLException e) {
+      assertExceptionMatches(e, ".*quoted value ended.*position 9.*");
+    }
+
+    try {
+      ConnectStringParser.parse("key='\"can''t parse\"");
+      fail("unterminated quoted value");
+    } catch (SQLException e) {
+      assertExceptionMatches(e, ".*unterminated quoted value.*");
+    }
+  }
+
+  /**
+   * Tests most of the examples from the <a
+   * href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/oledbconnectionstringsyntax.asp">
+   * OLE DB spec</a>. Omitted are cases for Window handles, returning multiple
+   * values, and special handling of "Provider" keyword.
+   */
+  @Test public void testOleDbExamples() throws Throwable {
+    // test the parser with examples from OLE DB documentation
+    Quad[] quads = {
+      // {reason for test, key, val, string to parse},
+      new Quad(
+          "printable chars",
+          "Jet OLE DB:System Database", "c:\\system.mda",
+          "Jet OLE DB:System Database=c:\\system.mda"),
+      new Quad(
+          "key embedded semi",
+          "Authentication;Info", "Column 5",
+          "Authentication;Info=Column 5"),
+      new Quad(
+          "key embedded equal",
+          "Verification=Security", "True",
+          "Verification==Security=True"),
+      new Quad(
+          "key many equals",
+          "Many==One", "Valid",
+          "Many====One=Valid"),
+      new Quad(
+          "key too many equal",
+          "TooMany=", "False",
+          "TooMany===False"),
+      new Quad(
+          "value embedded quote and semi",
+          "ExtProps", "Data Source='localhost';Key Two='value 2'",
+          "ExtProps=\"Data Source='localhost';Key Two='value 2'\""),
+      new Quad(
+          "value embedded double quote and semi",
+          "ExtProps", "Integrated Security=\"SSPI\";Key Two=\"value 2\"",
+          "ExtProps='Integrated Security=\"SSPI\";Key Two=\"value 2\"'"),
+      new Quad(
+          "value double quoted",
+          "DataSchema", "\"MyCustTable\"",
+          "DataSchema='\"MyCustTable\"'"),
+      new Quad(
+          "value single quoted",
+          "DataSchema", "'MyCustTable'",
+          "DataSchema=\"'MyCustTable'\""),
+      new Quad(
+          "value double quoted double trouble",
+          "Caption", "\"Company's \"new\" customer\"",
+          "Caption=\"\"\"Company's \"\"new\"\" customer\"\"\""),
+      new Quad(
+          "value single quoted double trouble",
+          "Caption", "\"Company's \"new\" customer\"",
+          "Caption='\"Company''s \"new\" customer\"'"),
+      new Quad(
+          "embedded blanks and trim",
+          "My Keyword", "My Value",
+          " My Keyword = My Value ;MyNextValue=Value"),
+      new Quad(
+          "value single quotes preserve blanks",
+          "My Keyword", " My Value ",
+          " My Keyword =' My Value ';MyNextValue=Value"),
+      new Quad(
+          "value double quotes preserve blanks",
+          "My Keyword", " My Value ",
+          " My Keyword =\" My Value \";MyNextValue=Value"),
+      new Quad(
+          "last redundant key wins",
+          "SomeKey", "NextValue",
+          "SomeKey=FirstValue;SomeKey=NextValue"),
+    };
+    for (Quad quad : quads) {
+      Properties props = ConnectStringParser.parse(quad.str);
+
+      assertEquals(quad.why, quad.val, props.get(quad.key));
+      String synth = ConnectStringParser.getParamString(props);
+
+      try {
+        assertEquals("reversible " + quad.why, quad.str, synth);
+      } catch (Throwable e) {
+        // it's OK that the strings don't match as long as the
+        // two strings parse out the same way and are thus
+        // "semantically reversible"
+        Properties synthProps = ConnectStringParser.parse(synth);
+        assertEquals("equivalent " + quad.why, props, synthProps);
+      }
+    }
+  }
+
+  static void assertExceptionMatches(
+      Throwable e,
+      String expectedPattern) {
+    if (e == null) {
+      fail("Expected an error which matches pattern '" + expectedPattern + "'");
+    }
+    String msg = e.toString();
+    if (!msg.matches(expectedPattern)) {
+      fail("Got a different error '" + msg + "' than expected '"
+          + expectedPattern + "'");
+    }
+  }
+
+  /** Collection of values comprising a test. */
+  static class Quad {
+    private final String why;
+    private final String key;
+    private final String val;
+    private final String str;
+
+    Quad(String why, String key, String val, String str) {
+      this.why = why;
+      this.key = key;
+      this.val = val;
+      this.str = str;
+    }
+  }
+}
+
+// End ConnectStringParserTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/test/JsonHandlerTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/test/JsonHandlerTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/test/JsonHandlerTest.java
new file mode 100644
index 0000000..57cf60a
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/test/JsonHandlerTest.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.test;
+
+import org.apache.calcite.avatica.AvaticaParameter;
+import org.apache.calcite.avatica.ColumnMetaData;
+import org.apache.calcite.avatica.Meta;
+import org.apache.calcite.avatica.Meta.CursorFactory;
+import org.apache.calcite.avatica.metrics.noop.NoopMetricsSystem;
+import org.apache.calcite.avatica.remote.JsonHandler;
+import org.apache.calcite.avatica.remote.JsonService;
+import org.apache.calcite.avatica.remote.LocalJsonService;
+import org.apache.calcite.avatica.remote.Service;
+import org.apache.calcite.avatica.remote.TypedValue;
+
+import org.junit.Test;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.UUID;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Tests JSON encoding/decoding in the remote service.
+ */
+public class JsonHandlerTest {
+
+  private static final Random RANDOM = new Random();
+
+  /**
+   * Implementation of {@link org.apache.calcite.avatica.remote.Service}
+   * that does nothing.
+   */
+  public static class NoopService implements Service {
+    @Override public ResultSetResponse apply(CatalogsRequest request) {
+      return null;
+    }
+
+    @Override public ResultSetResponse apply(SchemasRequest request) {
+      return null;
+    }
+
+    @Override public ResultSetResponse apply(TablesRequest request) {
+      return null;
+    }
+
+    @Override public ResultSetResponse apply(TableTypesRequest request) {
+      return null;
+    }
+
+    @Override public ResultSetResponse apply(TypeInfoRequest request) {
+      return null;
+    }
+
+    @Override public ResultSetResponse apply(ColumnsRequest request) {
+      return null;
+    }
+
+    @Override public PrepareResponse apply(PrepareRequest request) {
+      return null;
+    }
+
+    @Override public ExecuteResponse apply(PrepareAndExecuteRequest request) {
+      return null;
+    }
+
+    @Override public FetchResponse apply(FetchRequest request) {
+      return null;
+    }
+
+    @Override public CreateStatementResponse apply(CreateStatementRequest request) {
+      return null;
+    }
+
+    @Override public CloseStatementResponse apply(CloseStatementRequest request) {
+      return null;
+    }
+
+    @Override public OpenConnectionResponse apply(OpenConnectionRequest request) {
+      return null;
+    }
+
+    @Override public CloseConnectionResponse apply(CloseConnectionRequest request) {
+      return null;
+    }
+
+    @Override public ConnectionSyncResponse apply(ConnectionSyncRequest request) {
+      return null;
+    }
+
+    @Override public DatabasePropertyResponse apply(DatabasePropertyRequest request) {
+      return null;
+    }
+
+    @Override public SyncResultsResponse apply(SyncResultsRequest request) {
+      return null;
+    }
+
+    @Override public ExecuteResponse apply(ExecuteRequest request) {
+      return null;
+    }
+
+    @Override public void setRpcMetadata(RpcMetadataResponse metadata) {}
+
+    @Override public CommitResponse apply(CommitRequest request) {
+      return null;
+    }
+
+    @Override public RollbackResponse apply(RollbackRequest request) {
+      return null;
+    }
+  }
+
+  /**
+   * Instrumented subclass of {@link org.apache.calcite.avatica.test.JsonHandlerTest.NoopService}
+   * that checks the parameter values passed to the "execute" request.
+   *
+   * <p>Note: parameter values for "fetch" request deprecated.
+   */
+  public static class ParameterValuesCheckingService extends NoopService {
+
+    final List<TypedValue> expectedParameterValues;
+
+    public ParameterValuesCheckingService(List<TypedValue> epv) {
+      expectedParameterValues = epv;
+    }
+
+    @Override public ExecuteResponse apply(ExecuteRequest request) {
+      expectedParameterValues.addAll(request.parameterValues);
+
+      final Meta.Signature signature =
+          new Meta.Signature(Collections.<ColumnMetaData>emptyList(),
+              "SELECT 1 FROM VALUE()",
+              Collections.<AvaticaParameter>emptyList(),
+              Collections.<String, Object>emptyMap(),
+              CursorFactory.LIST, Meta.StatementType.SELECT);
+
+      final Service.ResultSetResponse resultSetResponse =
+          new Service.ResultSetResponse(UUID.randomUUID().toString(),
+              RANDOM.nextInt(), false, signature, Meta.Frame.EMPTY, -1L, null);
+
+      return new Service.ExecuteResponse(
+          Collections.singletonList(resultSetResponse), false, null);
+    }
+  }
+
+  @Test public void testExecuteRequestWithNumberParameter() {
+    final List<TypedValue> expectedParameterValues = new ArrayList<>();
+    final Service service = new ParameterValuesCheckingService(expectedParameterValues);
+    final JsonService jsonService = new LocalJsonService(service);
+    final JsonHandler jsonHandler = new JsonHandler(jsonService, NoopMetricsSystem.getInstance());
+
+    final List<TypedValue> parameterValues = Arrays.asList(
+        TypedValue.create("NUMBER", new BigDecimal("123")),
+        TypedValue.create("STRING", "calcite"));
+
+    jsonHandler.apply(
+        "{'request':'execute',"
+        + "'parameterValues':[{'type':'NUMBER','value':123},"
+        + "{'type':'STRING','value':'calcite'}]}");
+    assertThat(expectedParameterValues.size(), is(2));
+    assertThat(expectedParameterValues.get(0), is(parameterValues.get(0)));
+    assertThat(expectedParameterValues.get(1), is(parameterValues.get(1)));
+  }
+}
+
+// End JsonHandlerTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/test/package-info.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/test/package-info.java b/avatica/core/src/test/java/org/apache/calcite/avatica/test/package-info.java
new file mode 100644
index 0000000..501bb9f
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/test/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+
+/**
+ * Avatica tests.
+ */
+@PackageMarker
+package org.apache.calcite.avatica.test;
+
+import org.apache.calcite.avatica.util.PackageMarker;
+
+// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/util/NumberAccessorTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/util/NumberAccessorTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/util/NumberAccessorTest.java
new file mode 100644
index 0000000..4f8e51f
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/util/NumberAccessorTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.util;
+
+import org.apache.calcite.avatica.util.AbstractCursor.Getter;
+import org.apache.calcite.avatica.util.AbstractCursor.NumberAccessor;
+
+import org.junit.Test;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test logic for the NumberAccessor.
+ */
+public class NumberAccessorTest {
+
+  @Test
+  public void testBigDecimalZeroScale() {
+    final BigDecimal orig = new BigDecimal(BigInteger.valueOf(137L), 1);
+    NumberAccessor accessor = new AbstractCursor.NumberAccessor(
+        new Getter() {
+          @Override public Object getObject() {
+            return orig;
+          }
+
+          @Override public boolean wasNull() {
+            return false;
+          }
+        },
+        0);
+
+    assertEquals(orig, accessor.getBigDecimal(0));
+  }
+
+}
+
+// End NumberAccessorTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/util/UnsynchronizedBufferTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/util/UnsynchronizedBufferTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/util/UnsynchronizedBufferTest.java
new file mode 100644
index 0000000..a448d3e
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/util/UnsynchronizedBufferTest.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for the UnsynchronizedBuffer.
+ */
+public class UnsynchronizedBufferTest {
+
+  @Test public void testArrayResizing() {
+    int size = 64;
+    int expected = 128;
+    for (int i = 0; i < 10; i++) {
+      // We keep being one byte short to contain this message
+      int next = UnsynchronizedBuffer.nextArraySize(size + 1);
+      assertEquals(expected, next);
+      size = next;
+      expected *= 2;
+    }
+  }
+}
+
+// End UnsynchronizedBufferTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/metrics-dropwizardmetrics3/pom.xml
----------------------------------------------------------------------
diff --git a/avatica/metrics-dropwizardmetrics3/pom.xml b/avatica/metrics-dropwizardmetrics3/pom.xml
new file mode 100644
index 0000000..4966c5b
--- /dev/null
+++ b/avatica/metrics-dropwizardmetrics3/pom.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to you 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.calcite.avatica</groupId>
+    <artifactId>calcite-avatica-parent</artifactId>
+    <version>1.7.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>calcite-avatica-metrics-dropwizardmetrics3</artifactId>
+  <packaging>jar</packaging>
+  <name>Calcite Avatica Dropwizard Metrics 3</name>
+  <description>An implementation of Avatica Metrics using Dropwizard Metrics.</description>
+
+  <properties>
+    <top.dir>${project.basedir}/..</top.dir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-avatica-metrics</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.dropwizard.metrics</groupId>
+      <artifactId>metrics-core</artifactId>
+      <!-- Avoid specifying version in dependencyManagement in support of other avatica-metrics impls -->
+      <version>${dropwizard-metrics3.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.eclipse.m2e</groupId>
+          <artifactId>lifecycle-mapping</artifactId>
+          <version>1.0.0</version>
+          <configuration>
+            <lifecycleMappingMetadata>
+              <pluginExecutions>
+                <pluginExecution>
+                  <pluginExecutionFilter>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-checkstyle-plugin</artifactId>
+                    <versionRange>[2.12.1,)</versionRange>
+                    <goals>
+                      <goal>check</goal>
+                    </goals>
+                  </pluginExecutionFilter>
+                  <action>
+                    <ignore />
+                  </action>
+                </pluginExecution>
+              </pluginExecutions>
+            </lifecycleMappingMetadata>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+    <plugins>
+      <!-- Parent module has the same plugin and does the work of
+           generating -sources.jar for each project. But without the
+           plugin declared here, IDEs don't know the sources are
+           available. -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar-no-fork</goal>
+              <goal>test-jar-no-fork</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-remote-resources-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>non-root-resources</id>
+            <goals>
+              <goal>process</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardCounter.java
----------------------------------------------------------------------
diff --git a/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardCounter.java b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardCounter.java
new file mode 100644
index 0000000..4204ebf
--- /dev/null
+++ b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardCounter.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.metrics.dropwizard3;
+
+import com.codahale.metrics.Counter;
+
+import java.util.Objects;
+
+/**
+ * Dropwizard Metrics implementation of {@link org.apache.calcite.avatica.metrics.Counter}.
+ */
+public class DropwizardCounter implements org.apache.calcite.avatica.metrics.Counter {
+
+  private final Counter counter;
+
+  public DropwizardCounter(Counter counter) {
+    this.counter = Objects.requireNonNull(counter);
+  }
+
+  @Override public void increment() {
+    this.counter.inc();
+  }
+
+  @Override public void increment(long n) {
+    this.counter.inc(n);
+  }
+
+  @Override public void decrement() {
+    this.counter.dec();
+  }
+
+  @Override public void decrement(long n) {
+    this.counter.dec(n);
+  }
+}
+
+// End DropwizardCounter.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardGauge.java
----------------------------------------------------------------------
diff --git a/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardGauge.java b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardGauge.java
new file mode 100644
index 0000000..5f4b776
--- /dev/null
+++ b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardGauge.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.metrics.dropwizard3;
+
+import com.codahale.metrics.Gauge;
+
+/**
+ * Dropwizard Metrics implementation of {@link org.apache.calcite.avatica.metrics.Gauge}.
+ *
+ * @param <T> The value the gauge returns.
+ */
+public class DropwizardGauge<T> implements Gauge<T> {
+
+  private final org.apache.calcite.avatica.metrics.Gauge<T> gauge;
+
+  public DropwizardGauge(org.apache.calcite.avatica.metrics.Gauge<T> gauge) {
+    this.gauge = gauge;
+  }
+
+  @Override public T getValue() {
+    return gauge.getValue();
+  }
+}
+
+// End DropwizardGauge.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardHistogram.java
----------------------------------------------------------------------
diff --git a/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardHistogram.java b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardHistogram.java
new file mode 100644
index 0000000..266f130
--- /dev/null
+++ b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardHistogram.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.metrics.dropwizard3;
+
+import com.codahale.metrics.Histogram;
+
+import java.util.Objects;
+
+/**
+ * Dropwizard metrics implementation of {@link org.apache.calcite.avatica.metrics.Histogram}.
+ */
+public class DropwizardHistogram implements org.apache.calcite.avatica.metrics.Histogram {
+
+  private final Histogram histogram;
+
+  public DropwizardHistogram(Histogram histogram) {
+    this.histogram = Objects.requireNonNull(histogram);
+  }
+
+  @Override public void update(int value) {
+    histogram.update(value);
+  }
+
+  @Override public void update(long value) {
+    histogram.update(value);
+  }
+}
+
+// End DropwizardHistogram.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMeter.java
----------------------------------------------------------------------
diff --git a/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMeter.java b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMeter.java
new file mode 100644
index 0000000..ab8fafc
--- /dev/null
+++ b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMeter.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.metrics.dropwizard3;
+
+import com.codahale.metrics.Meter;
+
+import java.util.Objects;
+
+/**
+ * Dropwizard metrics implementation of {@link org.apache.calcite.avatica.metrics.Meter}.
+ */
+public class DropwizardMeter implements org.apache.calcite.avatica.metrics.Meter {
+
+  private final Meter meter;
+
+  public DropwizardMeter(Meter meter) {
+    this.meter = Objects.requireNonNull(meter);
+  }
+
+  @Override public void mark() {
+    this.meter.mark();
+  }
+
+  @Override public void mark(long count) {
+    this.meter.mark(count);
+  }
+}
+
+// End DropwizardMeter.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMetricsSystem.java
----------------------------------------------------------------------
diff --git a/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMetricsSystem.java b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMetricsSystem.java
new file mode 100644
index 0000000..6aa71b9
--- /dev/null
+++ b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMetricsSystem.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.metrics.dropwizard3;
+
+import org.apache.calcite.avatica.metrics.Counter;
+import org.apache.calcite.avatica.metrics.Gauge;
+import org.apache.calcite.avatica.metrics.Histogram;
+import org.apache.calcite.avatica.metrics.Meter;
+import org.apache.calcite.avatica.metrics.MetricsSystem;
+import org.apache.calcite.avatica.metrics.Timer;
+
+import com.codahale.metrics.MetricRegistry;
+
+import java.util.Objects;
+
+/**
+ * Dropwizard Metrics implementation of {@link MetricsSystem}.
+ */
+public class DropwizardMetricsSystem implements MetricsSystem {
+
+  private final MetricRegistry registry;
+
+  public DropwizardMetricsSystem(MetricRegistry registry) {
+    this.registry = Objects.requireNonNull(registry);
+  }
+
+  @Override public Timer getTimer(String name) {
+    return new DropwizardTimer(registry.timer(name));
+  }
+
+  @Override public Histogram getHistogram(String name) {
+    return new DropwizardHistogram(registry.histogram(name));
+  }
+
+  @Override public Meter getMeter(String name) {
+    return new DropwizardMeter(registry.meter(name));
+  }
+
+  @Override public Counter getCounter(String name) {
+    return new DropwizardCounter(registry.counter(name));
+  }
+
+  @Override public <T> void register(String name, Gauge<T> gauge) {
+    registry.register(name, new DropwizardGauge<T>(gauge));
+  }
+}
+
+// End DropwizardMetricsSystem.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMetricsSystemConfiguration.java
----------------------------------------------------------------------
diff --git a/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMetricsSystemConfiguration.java b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMetricsSystemConfiguration.java
new file mode 100644
index 0000000..f4c9234
--- /dev/null
+++ b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMetricsSystemConfiguration.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.metrics.dropwizard3;
+
+import org.apache.calcite.avatica.metrics.MetricsSystemConfiguration;
+
+import com.codahale.metrics.MetricRegistry;
+
+import java.util.Objects;
+
+/**
+ * A container which provides a {@link MetricRegistry} to a {@link DropwizardMetricsSystem}.
+ */
+public class DropwizardMetricsSystemConfiguration implements
+    MetricsSystemConfiguration<MetricRegistry> {
+
+  private final MetricRegistry registry;
+
+  public DropwizardMetricsSystemConfiguration(MetricRegistry registry) {
+    this.registry = Objects.requireNonNull(registry);
+  }
+
+  @Override public MetricRegistry get() {
+    return registry;
+  }
+}
+
+// End DropwizardMetricsSystemConfiguration.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMetricsSystemFactory.java
----------------------------------------------------------------------
diff --git a/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMetricsSystemFactory.java b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMetricsSystemFactory.java
new file mode 100644
index 0000000..1480db6
--- /dev/null
+++ b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardMetricsSystemFactory.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.metrics.dropwizard3;
+
+import org.apache.calcite.avatica.metrics.MetricsSystemConfiguration;
+import org.apache.calcite.avatica.metrics.MetricsSystemFactory;
+
+/**
+ * A {@link MetricsSystemFactory} for {@link DropwizardMetricsSystem}.
+ */
+public class DropwizardMetricsSystemFactory implements MetricsSystemFactory {
+
+  @Override public DropwizardMetricsSystem create(MetricsSystemConfiguration<?> config) {
+    // Verify we got configuration this factory can use
+    if (config instanceof DropwizardMetricsSystemConfiguration) {
+      DropwizardMetricsSystemConfiguration typedConfig =
+          (DropwizardMetricsSystemConfiguration) config;
+
+      return new DropwizardMetricsSystem(typedConfig.get());
+    }
+
+    throw new IllegalStateException("Expected instance of "
+        + DropwizardMetricsSystemConfiguration.class.getName() + " but got "
+        + (null == config ? "null" : config.getClass().getName()));
+  }
+}
+
+// End DropwizardMetricsSystemFactory.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardTimer.java
----------------------------------------------------------------------
diff --git a/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardTimer.java b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardTimer.java
new file mode 100644
index 0000000..850f9a6
--- /dev/null
+++ b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/DropwizardTimer.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.calcite.avatica.metrics.dropwizard3;
+
+import com.codahale.metrics.Timer;
+
+import java.util.Objects;
+
+/**
+ * Dropwizard Metrics implementation of {@link org.apache.calcite.avatica.metrics.Timer}.
+ */
+public class DropwizardTimer implements org.apache.calcite.avatica.metrics.Timer {
+
+  private final Timer timer;
+
+  public DropwizardTimer(Timer timer) {
+    this.timer = Objects.requireNonNull(timer);
+  }
+
+  @Override public DropwizardContext start() {
+    return new DropwizardContext(timer.time());
+  }
+
+  /**
+   * Dropwizard Metrics implementation of {@link org.apache.calcite.avatica.metrics.Timer.Context}
+   */
+  public class DropwizardContext implements org.apache.calcite.avatica.metrics.Timer.Context {
+    private final com.codahale.metrics.Timer.Context context;
+
+    public DropwizardContext(com.codahale.metrics.Timer.Context context) {
+      this.context = Objects.requireNonNull(context);
+    }
+
+    @Override public void close() {
+      this.context.stop();
+    }
+  }
+}
+
+// End DropwizardTimer.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/package-info.java
----------------------------------------------------------------------
diff --git a/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/package-info.java b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/package-info.java
new file mode 100644
index 0000000..f88df93
--- /dev/null
+++ b/avatica/metrics-dropwizardmetrics3/src/main/java/org/apache/calcite/avatica/metrics/dropwizard3/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+
+/**
+ * Dropwizard-Metrics (v3) implementation of the Avatica Metrics framework.
+ */
+@PackageMarker
+package org.apache.calcite.avatica.metrics.dropwizard3;
+
+import org.apache.calcite.avatica.metrics.PackageMarker;
+
+// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/metrics-dropwizardmetrics3/src/main/resources/META-INF/services/org.apache.calcite.avatica.metrics.MetricsSystemFactory
----------------------------------------------------------------------
diff --git a/avatica/metrics-dropwizardmetrics3/src/main/resources/META-INF/services/org.apache.calcite.avatica.metrics.MetricsSystemFactory b/avatica/metrics-dropwizardmetrics3/src/main/resources/META-INF/services/org.apache.calcite.avatica.metrics.MetricsSystemFactory
new file mode 100644
index 0000000..25b64a8
--- /dev/null
+++ b/avatica/metrics-dropwizardmetrics3/src/main/resources/META-INF/services/org.apache.calcite.avatica.metrics.MetricsSystemFactory
@@ -0,0 +1,2 @@
+org.apache.calcite.avatica.metrics.dropwizard3.DropwizardMetricsSystemFactory
+