[CALCITE-951] Print the server-side stack in the local exception (Josh Elser)

The AvaticaSqlException has a string representation of the stack
trace from the server, but didn't explicitly add it when printing
its own stacktrace.

Close apache/calcite#165


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/b82704f8
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/b82704f8
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/b82704f8

Branch: refs/heads/master
Commit: b82704f8c1a1559b4b2a04315ae934851a4a74eb
Parents: 2fd8c5a
Author: Josh Elser <[email protected]>
Authored: Wed Nov 4 12:04:38 2015 -0500
Committer: Julian Hyde <[email protected]>
Committed: Wed Nov 4 11:07:50 2015 -0800

----------------------------------------------------------------------
 .../calcite/avatica/remote/RemoteMetaTest.java  | 70 +++++++++++++-------
 .../calcite/avatica/AvaticaSqlException.java    | 69 +++++++++++++++++++
 2 files changed, 114 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/b82704f8/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
----------------------------------------------------------------------
diff --git 
a/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
 
b/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
index c68f3cf..d493c01 100644
--- 
a/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
+++ 
b/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
@@ -31,9 +31,11 @@ import org.apache.calcite.avatica.server.Main;
 import org.apache.calcite.avatica.server.Main.HandlerFactory;
 import org.apache.calcite.avatica.util.ArrayImpl;
 
+import com.google.common.base.Throwables;
 import com.google.common.cache.Cache;
 
 import org.eclipse.jetty.server.handler.AbstractHandler;
+
 import org.junit.AfterClass;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -59,6 +61,7 @@ import java.util.Random;
 import java.util.UUID;
 
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -76,33 +79,14 @@ public class RemoteMetaTest {
   // Keep a reference to the servers we start to clean them up after
   private static final List<HttpServer> ACTIVE_SERVERS = new ArrayList<>();
 
-  /** Factory that provides a {@link JdbcMeta}. */
-  public static class FullyRemoteJdbcMetaFactory implements Meta.Factory {
-
-    private static JdbcMeta instance = null;
-
-    private static JdbcMeta getInstance() {
-      if (instance == null) {
-        try {
-          instance = new JdbcMeta(CONNECTION_SPEC.url, 
CONNECTION_SPEC.username,
-              CONNECTION_SPEC.password);
-        } catch (SQLException e) {
-          throw new RuntimeException(e);
-        }
-      }
-      return instance;
-    }
-
-    @Override public Meta create(List<String> args) {
-      return getInstance();
-    }
-  }
+  private final HttpServer server;
+  private final String url;
 
   @Parameters
   public static List<Object[]> parameters() throws Exception {
     List<Object[]> params = new ArrayList<>();
 
-    final String[] mainArgs = new String[] { 
FullyRemoteJdbcMetaFactory.class.getName() };
+    final String[] mainArgs = { FullyRemoteJdbcMetaFactory.class.getName() };
 
     // Bind to '0' to pluck an ephemeral port instead of expecting a certain 
one to be free
 
@@ -126,9 +110,6 @@ public class RemoteMetaTest {
     return params;
   }
 
-  private final HttpServer server;
-  private final String url;
-
   public RemoteMetaTest(HttpServer server, Driver.Serialization serialization) 
{
     this.server = server;
     final int port = server.getPort();
@@ -449,6 +430,45 @@ public class RemoteMetaTest {
       ConnectionSpec.getDatabaseLock().unlock();
     }
   }
+
+  @Test public void testLocalStackTraceHasServerStackTrace() {
+    ConnectionSpec.getDatabaseLock().lock();
+    try {
+      Statement statement = DriverManager.getConnection(url).createStatement();
+      statement.executeQuery("SELECT * FROM BOGUS_TABLE_DEF_DOESNT_EXIST");
+    } catch (SQLException e) {
+      // Verify that we got the expected exception
+      assertThat(e, instanceOf(AvaticaSqlException.class));
+
+      // Attempt to verify that we got a "server-side" class in the stack.
+      assertThat(Throwables.getStackTraceAsString(e),
+          containsString(JdbcMeta.class.getName()));
+    } finally {
+      ConnectionSpec.getDatabaseLock().unlock();
+    }
+  }
+
+  /** Factory that provides a {@link JdbcMeta}. */
+  public static class FullyRemoteJdbcMetaFactory implements Meta.Factory {
+
+    private static JdbcMeta instance = null;
+
+    private static JdbcMeta getInstance() {
+      if (instance == null) {
+        try {
+          instance = new JdbcMeta(CONNECTION_SPEC.url, 
CONNECTION_SPEC.username,
+              CONNECTION_SPEC.password);
+        } catch (SQLException e) {
+          throw new RuntimeException(e);
+        }
+      }
+      return instance;
+    }
+
+    @Override public Meta create(List<String> args) {
+      return getInstance();
+    }
+  }
 }
 
 // End RemoteMetaTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/b82704f8/avatica/src/main/java/org/apache/calcite/avatica/AvaticaSqlException.java
----------------------------------------------------------------------
diff --git 
a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaSqlException.java 
b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaSqlException.java
index 9782581..cfcf305 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaSqlException.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaSqlException.java
@@ -16,6 +16,8 @@
  */
 package org.apache.calcite.avatica;
 
+import java.io.PrintStream;
+import java.io.PrintWriter;
 import java.sql.SQLException;
 import java.util.List;
 import java.util.Objects;
@@ -54,6 +56,73 @@ public class AvaticaSqlException extends SQLException {
   public List<String> getStackTraces() {
     return stackTraces;
   }
+
+  // printStackTrace() will get redirected to printStackTrace(PrintStream), 
don't need to override.
+
+  @Override public void printStackTrace(PrintStream stream) {
+    super.printStackTrace(stream);
+    stream.flush();
+    printServerStackTrace(new PrintStreamOrWriter(stream));
+  }
+
+  @Override public void printStackTrace(PrintWriter writer) {
+    super.printStackTrace(writer);
+    writer.flush();
+    printServerStackTrace(new PrintStreamOrWriter(writer));
+  }
+
+  void printServerStackTrace(PrintStreamOrWriter streamOrWriter) {
+    for (String serverStackTrace : this.stackTraces) {
+      streamOrWriter.println(serverStackTrace);
+    }
+  }
+
+  /**
+   * A class that encapsulates either a PrintStream or a PrintWriter.
+   */
+  private static class PrintStreamOrWriter {
+    /**
+     * Enumeration to differentiate between a PrintStream and a PrintWriter.
+     */
+    private enum Type {
+      STREAM,
+      WRITER
+    }
+
+    private PrintStream stream;
+    private PrintWriter writer;
+    private final Type type;
+
+    public PrintStreamOrWriter(PrintStream stream) {
+      this.stream = stream;
+      type = Type.STREAM;
+    }
+
+    public PrintStreamOrWriter(PrintWriter writer) {
+      this.writer = writer;
+      type = Type.WRITER;
+    }
+
+    /**
+     * Prints the given string to the the provided stream or writer.
+     *
+     * @param string The string to print
+     */
+    public void println(String string) {
+      switch (type) {
+      case STREAM:
+        stream.println(string);
+        stream.flush();
+        return;
+      case WRITER:
+        writer.println(string);
+        writer.flush();
+        return;
+      default:
+        throw new IllegalStateException();
+      }
+    }
+  }
 }
 
 // End AvaticaSqlException.java

Reply via email to