You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by nr...@apache.org on 2018/03/13 20:07:50 UTC

[geode] branch develop updated: GEODE-4266: Make JdbcConnectionException safely serializable to clients (#1601)

This is an automated email from the ASF dual-hosted git repository.

nreich pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new a07b67a  GEODE-4266: Make JdbcConnectionException safely serializable to clients (#1601)
a07b67a is described below

commit a07b67a7dc7828528864520352daf7401249dac7
Author: Nick Reich <nr...@pivotal.io>
AuthorDate: Tue Mar 13 13:07:46 2018 -0700

    GEODE-4266: Make JdbcConnectionException safely serializable to clients (#1601)
    
    
      * SQLExceptions that come from underlying sql drivers may not be availale
        on clients or locators and will not be deserializable. When these
        exceptions are contained in a JdbcConnectorException, the stack trace
        is instead converted to a string to ensure safe serialization
---
 .../connectors/jdbc/JdbcConnectorException.java    | 53 +++++++++++++++++++++-
 .../apache/geode/connectors/jdbc/JdbcLoader.java   |  2 +-
 .../apache/geode/connectors/jdbc/JdbcWriter.java   |  2 +-
 .../jdbc/internal/TableKeyColumnManager.java       |  2 +-
 .../jdbc/JdbcConnectorExceptionTest.java           | 47 +++++++++++++++++++
 5 files changed, 101 insertions(+), 5 deletions(-)

diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/JdbcConnectorException.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/JdbcConnectorException.java
index 24674ea..d152873 100644
--- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/JdbcConnectorException.java
+++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/JdbcConnectorException.java
@@ -14,18 +14,67 @@
  */
 package org.apache.geode.connectors.jdbc;
 
+import java.sql.SQLException;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+
 import org.apache.geode.cache.CacheRuntimeException;
 
 /**
  * An exception thrown when communication with an external JDBC data source fails and can be used
- * to diagnose the cause of database communication failures.
+ * to diagnose the cause of database communication failures. In cases where the cause of this
+ * exception is not safe to serialize to clients, the stack trace is included in the message of the
+ * exception and the cause is left empty.
  *
  * @since Geode 1.5
  */
 public class JdbcConnectorException extends CacheRuntimeException {
   private static final long serialVersionUID = 1L;
 
-  public JdbcConnectorException(Exception e) {
+  /**
+   * Create a new JdbcConnectorException by first checking to see if the causing exception is or
+   * contains an exception that potentially could not be deserialized by remote systems receiving
+   * the serialized exception.
+   *
+   * @param e cause of this Exception
+   * @return a new JdbcConnectorException containing either the causing exception, if it can be
+   *         serialized/deserialized by Geode, or containing the causing exception stack trace in
+   *         its message if not
+   */
+  public static JdbcConnectorException createException(Exception e) {
+    String message;
+    if (containsNonSerializableException(e)) {
+      message = e.getMessage() + System.lineSeparator() + ExceptionUtils.getFullStackTrace(e);
+      return new JdbcConnectorException(message);
+    } else {
+      return new JdbcConnectorException(e);
+    }
+  }
+
+  /*
+   * SQLExceptions likely are instances of or contain exceptions from the underlying SQL driver
+   * and potentially cannot be deserialzed by other systems (e.g. client or locators) that do not
+   * possess the driver on their classpaths.
+   */
+  private static boolean containsNonSerializableException(Exception e) {
+    if (e == null) {
+      return false;
+    }
+
+    if (e instanceof SQLException) {
+      return true;
+    }
+
+    Throwable cause;
+    while ((cause = e.getCause()) != null) {
+      if (cause instanceof SQLException) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private JdbcConnectorException(Exception e) {
     super(e);
   }
 
diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/JdbcLoader.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/JdbcLoader.java
index 5196bce..d76baf9 100644
--- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/JdbcLoader.java
+++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/JdbcLoader.java
@@ -54,7 +54,7 @@ public class JdbcLoader<K, V> extends AbstractJdbcCallback implements CacheLoade
     try {
       return (V) getSqlHandler().read(helper.getRegion(), helper.getKey());
     } catch (SQLException e) {
-      throw new JdbcConnectorException(e);
+      throw JdbcConnectorException.createException(e);
     }
   }
 }
diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/JdbcWriter.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/JdbcWriter.java
index 777a5f3..193066f 100644
--- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/JdbcWriter.java
+++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/JdbcWriter.java
@@ -78,7 +78,7 @@ public class JdbcWriter<K, V> extends AbstractJdbcCallback implements CacheWrite
       getSqlHandler().write(event.getRegion(), event.getOperation(), event.getKey(),
           getPdxNewValue(event));
     } catch (SQLException e) {
-      throw new JdbcConnectorException(e);
+      throw JdbcConnectorException.createException(e);
     }
   }
 
diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/TableKeyColumnManager.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/TableKeyColumnManager.java
index 03c9e69..5fccb66 100644
--- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/TableKeyColumnManager.java
+++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/TableKeyColumnManager.java
@@ -47,7 +47,7 @@ class TableKeyColumnManager {
         key = getPrimaryKeyColumnNameFromMetaData(realTableName, metaData);
       }
     } catch (SQLException e) {
-      throw new JdbcConnectorException(e);
+      throw JdbcConnectorException.createException(e);
     }
     return key;
   }
diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/JdbcConnectorExceptionTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/JdbcConnectorExceptionTest.java
new file mode 100644
index 0000000..fe138db
--- /dev/null
+++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/JdbcConnectorExceptionTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.geode.connectors.jdbc;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.sql.SQLException;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TestName;
+
+import org.apache.geode.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+public class JdbcConnectorExceptionTest {
+  @Rule
+  public TestName testName = new TestName();
+
+  @Test
+  public void returnsExceptionWithCauseForNonSqlException() {
+    Exception e = JdbcConnectorException.createException(new IllegalStateException());
+    assertThat(e.getCause()).isNotNull().isInstanceOf(IllegalStateException.class);
+  }
+
+  @Test
+  public void returnsExceptionWithNoCauseForSqlException() {
+    Exception sqlException = new SQLException();
+    Exception e = JdbcConnectorException.createException(sqlException);
+    assertThat(e.getCause()).isNull();
+    assertThat(e.getMessage())
+        .contains(this.getClass().getCanonicalName() + "." + testName.getMethodName());
+  }
+}

-- 
To stop receiving notification emails like this one, please contact
nreich@apache.org.