You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by kr...@apache.org on 2019/11/20 00:24:43 UTC

[knox] branch master updated: KNOX-2131 - Fixed sonarcloud bugs (#201)

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

krisden pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git


The following commit(s) were added to refs/heads/master by this push:
     new 9c69c8c  KNOX-2131 - Fixed sonarcloud bugs (#201)
9c69c8c is described below

commit 9c69c8c08f19bce87e71c09b86c77603bbb94f6f
Author: Sandor Molnar <sm...@apache.org>
AuthorDate: Wed Nov 20 01:24:36 2019 +0100

    KNOX-2131 - Fixed sonarcloud bugs (#201)
---
 .../rewrite/impl/html/HtmlFilterReaderBase.java    |  64 +++++------
 .../topology/impl/DefaultTopologyService.java      |  43 +++----
 gateway-shell/pom.xml                              |   7 +-
 .../apache/knox/gateway/shell/jdbc/Database.java   |  73 ++++++++++++
 .../gateway/shell/jdbc/derby/DerbyDatabase.java    | 126 +++++++++++++++++++++
 .../shell/jdbc/derby/DerbyDatabaseException.java   |  29 +++++
 .../shell/table/JDBCKnoxShellTableBuilder.java     |  16 +--
 .../gateway/shell/table/KnoxShellTableTest.java    |  52 +++++++++
 .../src/test/resources/createBooksTable.sql        |   5 +
 gateway-shell/src/test/resources/insertBooks.sql   |   4 +
 .../knox/gateway/audit/api/CorrelationContext.java |   4 +-
 .../knox/gateway/util/X500PrincipalParser.java     |   6 +-
 pom.xml                                            |   8 ++
 13 files changed, 369 insertions(+), 68 deletions(-)

diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReaderBase.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReaderBase.java
index 877c753..d782d5b 100644
--- a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReaderBase.java
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReaderBase.java
@@ -37,11 +37,12 @@ import javax.xml.parsers.ParserConfigurationException;
 import java.io.IOException;
 import java.io.Reader;
 import java.io.StringWriter;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Stack;
+import java.util.concurrent.ConcurrentLinkedDeque;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -53,7 +54,7 @@ public abstract class HtmlFilterReaderBase extends Reader implements
 
   private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class );
 
-  private Stack<Level> stack;
+  private Deque<Level> stack;
   private Reader reader;
   private StreamedSource parser;
   private Iterator<Segment> iterator;
@@ -63,9 +64,9 @@ public abstract class HtmlFilterReaderBase extends Reader implements
   private StringBuffer buffer;
   private UrlRewriteFilterContentDescriptor config;
 
-  protected HtmlFilterReaderBase( Reader reader ) throws IOException, ParserConfigurationException {
+  protected HtmlFilterReaderBase( Reader reader ) throws IOException {
     this.reader = reader;
-    stack = new Stack<>();
+    stack = new ConcurrentLinkedDeque<>();
     parser = new StreamedSource( reader );
     iterator = parser.iterator();
     writer = new StringWriter();
@@ -192,7 +193,7 @@ public abstract class HtmlFilterReaderBase extends Reader implements
 
   private String getRuleName(String inputValue) {
     if( config != null && !config.getSelectors().isEmpty() ) {
-      for( UrlRewriteFilterPathDescriptor selector : config.getSelectors() ) {
+      for( UrlRewriteFilterPathDescriptor<?> selector : config.getSelectors() ) {
         if ( selector instanceof UrlRewriteFilterApplyDescriptor) {
           UrlRewriteFilterApplyDescriptor apply = (UrlRewriteFilterApplyDescriptor)selector;
           Matcher matcher = apply.compiledPath( REGEX_COMPILER ).matcher( inputValue );
@@ -209,10 +210,7 @@ public abstract class HtmlFilterReaderBase extends Reader implements
     String inputValue = segment.toString();
     String outputValue = inputValue;
     try {
-      if( stack.isEmpty() ) {
-        // This can happen for whitespace outside of the root element.
-        //outputValue = filterText( null, inputValue );
-      } else {
+      if (!stack.isEmpty()) {
         String tagName = stack.peek().getTag().getName();
         if (SCRIPTTAG.equals(tagName) && config != null && !config.getSelectors().isEmpty() ) {
           // embedded javascript content
@@ -264,19 +262,24 @@ public abstract class HtmlFilterReaderBase extends Reader implements
       return getNamespaces().get( prefix );
     }
 
-    private QName getQName( String name ) {
-      String prefix;
-      String local;
-      int colon = ( name == null ? -1 : name.indexOf( ':' ) );
-      if( colon < 0 ) {
-        prefix = "";
-        local = name;
+    private QName getQName(String name) {
+      final int colon = name == null ? -1 : name.indexOf(':');
+      final String prefix = getPrefix(name, colon);
+      final String local = getLocal(name, colon);
+      final String namespace = getNamespace(prefix);
+      return new QName(namespace, local, prefix);
+    }
+
+    private String getPrefix(String name, final int colon) {
+      return name != null && colon > 0 ? name.substring(0, colon) : "";
+    }
+
+    private String getLocal(String name, final int colon) {
+      if (name != null && colon > 0) {
+        return colon + 1 < name.length() ? name.substring(colon + 1) : "";
       } else {
-        prefix = name.substring( 0, colon );
-        local = ( colon + 1 < name.length() ? name.substring( colon + 1 ) : "" );
+        return name;
       }
-      String namespace = getNamespace( prefix );
-      return new QName( namespace, local, prefix );
     }
 
     private Map<String,String> getNamespaces() {
@@ -288,19 +291,14 @@ public abstract class HtmlFilterReaderBase extends Reader implements
     }
 
     private void parseNamespaces() {
-      Attributes attributes = tag.getAttributes();
-      if( attributes != null ) {
-        for( Attribute attribute : tag.getAttributes() ) {
-          String name = attribute.getName();
-          if( name.toLowerCase(Locale.ROOT).startsWith( "xmlns" ) ) {
-            int colon = name.indexOf( ':', 5 );
-            String prefix;
-            if( colon <= 0 ) {
-              prefix = "";
-            } else {
-              prefix = name.substring( colon );
-            }
-            namespaces.put( prefix, attribute.getValue() );
+      if (tag.getAttributes() != null) {
+        String prefix, attributeName;
+        for (Attribute attribute : tag.getAttributes()) {
+          attributeName = attribute.getName() == null ? "" : attribute.getName();
+          if (attributeName.toLowerCase(Locale.ROOT).startsWith("xmlns")) {
+            int colon = attributeName.indexOf(':', 5);
+            prefix = colon <= 0 ? "" : attributeName.substring(colon);
+            namespaces.put(prefix, attribute.getValue());
           }
         }
       }
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/topology/impl/DefaultTopologyService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/topology/impl/DefaultTopologyService.java
index 4a1101d..e904daa 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/topology/impl/DefaultTopologyService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/topology/impl/DefaultTopologyService.java
@@ -653,27 +653,28 @@ public class DefaultTopologyService extends FileAlterationListenerAdaptor implem
           if (DescriptorsMonitor.isDescriptorFile(descriptorFilename)) {
             String topologyName = FilenameUtils.getBaseName(descriptorFilename);
             File existingDescriptorFile = getExistingFile(descriptorsDirectory, topologyName);
-
-            // If there isn't a corresponding topology file, or if the descriptor has been modified since the
-            // corresponding topology file was generated, then trigger generation of one
-            File matchingTopologyFile = getExistingFile(topologiesDirectory, topologyName);
-            if (matchingTopologyFile == null || matchingTopologyFile.lastModified() < existingDescriptorFile.lastModified()) {
-              descriptorsMonitor.onFileChange(existingDescriptorFile);
-            } else {
-              // If regeneration is NOT required, then we at least need to report the provider configuration
-              // reference relationship (KNOX-1144)
-              String normalizedDescriptorPath = FilenameUtils.normalize(existingDescriptorFile.getAbsolutePath());
-
-              // Parse the descriptor to determine the provider config reference
-              SimpleDescriptor sd = SimpleDescriptorFactory.parse(normalizedDescriptorPath);
-              if (sd != null) {
-                File referencedProviderConfig =
-                           getExistingFile(sharedProvidersDirectory, FilenameUtils.getBaseName(sd.getProviderConfig()));
-                if (referencedProviderConfig != null) {
-                  List<String> references =
-                         descriptorsMonitor.getReferencingDescriptors(referencedProviderConfig.getAbsolutePath());
-                  if (!references.contains(normalizedDescriptorPath)) {
-                    references.add(normalizedDescriptorPath);
+            if (existingDescriptorFile != null) {
+              // If there isn't a corresponding topology file, or if the descriptor has been modified since the
+              // corresponding topology file was generated, then trigger generation of one
+              File matchingTopologyFile = getExistingFile(topologiesDirectory, topologyName);
+              if (matchingTopologyFile == null || matchingTopologyFile.lastModified() < existingDescriptorFile.lastModified()) {
+                descriptorsMonitor.onFileChange(existingDescriptorFile);
+              } else {
+                // If regeneration is NOT required, then we at least need to report the provider configuration
+                // reference relationship (KNOX-1144)
+                String normalizedDescriptorPath = FilenameUtils.normalize(existingDescriptorFile.getAbsolutePath());
+
+                // Parse the descriptor to determine the provider config reference
+                SimpleDescriptor sd = SimpleDescriptorFactory.parse(normalizedDescriptorPath);
+                if (sd != null) {
+                  File referencedProviderConfig =
+                             getExistingFile(sharedProvidersDirectory, FilenameUtils.getBaseName(sd.getProviderConfig()));
+                  if (referencedProviderConfig != null) {
+                    List<String> references =
+                           descriptorsMonitor.getReferencingDescriptors(referencedProviderConfig.getAbsolutePath());
+                    if (!references.contains(normalizedDescriptorPath)) {
+                      references.add(normalizedDescriptorPath);
+                    }
                   }
                 }
               }
diff --git a/gateway-shell/pom.xml b/gateway-shell/pom.xml
index 4f57b71..c0be6e8 100644
--- a/gateway-shell/pom.xml
+++ b/gateway-shell/pom.xml
@@ -126,7 +126,7 @@
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-annotations</artifactId>
         </dependency>
-	    <dependency>
+        <dependency>
             <groupId>org.aspectj</groupId>
             <artifactId>aspectjrt</artifactId>
         </dependency>
@@ -134,5 +134,10 @@
             <groupId>org.aspectj</groupId>
             <artifactId>aspectjweaver</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.derby</groupId>
+            <artifactId>derby</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/jdbc/Database.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/jdbc/Database.java
new file mode 100644
index 0000000..3fbd39b
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/jdbc/Database.java
@@ -0,0 +1,73 @@
+/*
+ * 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.knox.gateway.shell.jdbc;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+public interface Database {
+
+  /**
+   * Creates this database
+   *
+   * @throws SQLException
+   *           if any errors occur
+   */
+  void create() throws SQLException;
+
+  /**
+   * Shuts down this database
+   *
+   * @throws SQLException
+   *           if any errors occur
+   */
+  void shutdown() throws SQLException;
+
+  /**
+   * Retrieves a connection to this database
+   *
+   * @return an SQL connection to this database
+   * @throws SQLException
+   *           if any errors occur
+   */
+  Connection getConnection() throws SQLException;
+
+  /**
+   * Checks whether the specified table is created within this database (does not check schema)
+   *
+   * @param tableName
+   *          the name of the table to check
+   * @return true if the table exists; false otherwise
+   * @throws SQLException
+   *           if any I/O errors occur
+   */
+  boolean hasTable(String tableName) throws SQLException;
+
+  /**
+   * Checks whether the specified table is created within this database
+   *
+   * @param schemaName
+   *          the database schema name
+   * @param tableName
+   *          the name of the table to check
+   * @return true if the table exists; false otherwise
+   * @throws SQLException
+   *           if any I/O errors occur
+   */
+  boolean hasTable(String schemaName, String tableName) throws SQLException;
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/jdbc/derby/DerbyDatabase.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/jdbc/derby/DerbyDatabase.java
new file mode 100644
index 0000000..61edfc5
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/jdbc/derby/DerbyDatabase.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.knox.gateway.shell.jdbc.derby;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.knox.gateway.i18n.messages.MessageLevel;
+import org.apache.knox.gateway.shell.jdbc.Database;
+
+public class DerbyDatabase implements Database {
+
+  public static final String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
+  public static final String PROTOCOL = "jdbc:derby:";
+  private static final String CREATE_ATTRIBUTE = ";create=true";
+  private static final String SHUTDOWN_ATTRIBUTE = ";shutdown=true";
+
+  private final String dbUri;
+
+  /**
+   * Constructor
+   *
+   * @param directory
+   *          the directory where the database should be placed
+   * @throws DerbyDatabaseException
+   *           if the database engine can not be load for some reasons
+   */
+  public DerbyDatabase(String directory) throws DerbyDatabaseException {
+    this.dbUri = PROTOCOL + directory;
+    loadDriver();
+  }
+
+  @Override
+  public void create() throws SQLException {
+    Connection conn = null;
+    try {
+      conn = DriverManager.getConnection(dbUri + CREATE_ATTRIBUTE);
+    } finally {
+      if (conn != null) {
+        conn.close();
+      }
+    }
+  }
+
+  @Override
+  public void shutdown() {
+    try {
+      DriverManager.getConnection(dbUri + SHUTDOWN_ATTRIBUTE);
+    } catch (SQLException e) {
+      // See http://db.apache.org/derby/docs/dev/getstart/rwwdactivity3.html and check
+      // out 'Shut down the database' for an
+      // explanation of this check
+      if (((e.getErrorCode() == 45000) && ("08006".equals(e.getSQLState())))) {
+        // We got the expected exception
+        // Note that for single database shutdown, the expected
+        // SQL state is "08006", and the error code is 45000.
+        log(MessageLevel.INFO, "Derby database is closed");
+      } else {
+        // if the error code or SQLState is different, we have
+        // an unexpected exception (shutdown failed)
+        log(MessageLevel.WARN, "Derby database closed abnormally", e);
+      }
+    }
+  }
+
+  @Override
+  public Connection getConnection() throws SQLException {
+    return DriverManager.getConnection(dbUri);
+  }
+
+  @Override
+  public boolean hasTable(String tableName) throws SQLException {
+    return hasTable(null, tableName);
+  }
+
+  @Override
+  public boolean hasTable(String schemaName, String tableName) throws SQLException {
+    boolean result = false;
+    try (Connection connection = getConnection();
+        ResultSet tables = connection.getMetaData().getTables(connection.getCatalog(), schemaName, tableName, null)) {
+      result = tables.next();
+    } catch (SQLException e) {
+      log(MessageLevel.ERROR, "SQL error occured while checking table " + tableName + " in the database", e);
+    }
+
+    return result;
+  }
+
+  private void loadDriver() throws DerbyDatabaseException {
+    try {
+      Class.forName(DRIVER).newInstance();
+    } catch (ClassNotFoundException e) {
+      throw new DerbyDatabaseException("Unable to load the JDBC driver " + DRIVER + ". Check your CLASSPATH.", e);
+    } catch (InstantiationException e) {
+      throw new DerbyDatabaseException("Unable to instantiate the JDBC driver " + DRIVER, e);
+    } catch (IllegalAccessException e) {
+      throw new DerbyDatabaseException("Not allowed to access the JDBC driver " + DRIVER, e);
+    }
+  }
+
+  private void log(MessageLevel logLevel, String message) {
+    log(logLevel, message, null);
+  }
+
+  // being a test only class we now log into the STDOUT for now
+  private void log(MessageLevel logLevel, String message, Throwable error) {
+    System.out.println(logLevel.name() + " - " + message + (error == null ? "" : " - caused by " + error));
+  }
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/jdbc/derby/DerbyDatabaseException.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/jdbc/derby/DerbyDatabaseException.java
new file mode 100644
index 0000000..81e0e42
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/jdbc/derby/DerbyDatabaseException.java
@@ -0,0 +1,29 @@
+/*
+ * 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.knox.gateway.shell.jdbc.derby;
+
+import java.sql.SQLException;
+
+@SuppressWarnings("serial")
+public class DerbyDatabaseException extends SQLException {
+
+  DerbyDatabaseException(String message, Throwable throwable) {
+    super(message, throwable);
+  }
+
+}
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JDBCKnoxShellTableBuilder.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JDBCKnoxShellTableBuilder.java
index 2c5b747..63be4a1 100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JDBCKnoxShellTableBuilder.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/table/JDBCKnoxShellTableBuilder.java
@@ -77,18 +77,14 @@ public class JDBCKnoxShellTableBuilder extends KnoxShellTableBuilder {
 
   public KnoxShellTable sql(String sql) throws IOException, SQLException {
     conn = conn == null ? DriverManager.getConnection(connectionUrl) : conn;
-    if (conn != null) {
-      try (Statement statement = conn.createStatement(); ResultSet resultSet = statement.executeQuery(sql);) {
-        processResultSet(resultSet);
-      } finally {
-        if (conn != null && tableManagedConnection) {
-          conn.close();
-        }
+    try (Statement statement = conn.createStatement(); ResultSet resultSet = statement.executeQuery(sql);) {
+      processResultSet(resultSet);
+    } finally {
+      if (conn != null && tableManagedConnection) {
+        conn.close();
       }
-      return this.table;
-    } else {
-      return null;
     }
+    return this.table;
   }
 
   // added this as a private method so that KnoxShellTableHistoryAspect will not
diff --git a/gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableTest.java b/gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableTest.java
index 64fcd4d..2e2cd2d 100644
--- a/gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableTest.java
+++ b/gateway-shell/src/test/java/org/apache/knox/gateway/shell/table/KnoxShellTableTest.java
@@ -17,6 +17,8 @@
  */
 package org.apache.knox.gateway.shell.table;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.commons.io.FileUtils.readFileToString;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.expectLastCall;
@@ -26,23 +28,39 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import java.io.File;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
 import java.sql.Statement;
+import java.util.Arrays;
 import java.util.Collections;
 
 import javax.swing.SortOrder;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.knox.gateway.shell.jdbc.Database;
+import org.apache.knox.gateway.shell.jdbc.derby.DerbyDatabase;
 import org.easymock.IAnswer;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 
 public class KnoxShellTableTest {
+
+  @Rule
+  public final TemporaryFolder testFolder = new TemporaryFolder();
+
+  private static final String SYSTEM_PROPERTY_DERBY_STREAM_ERROR_FILE = "derby.stream.error.file";
+  private static final String SAMPLE_DERBY_DATABASE_NAME = "sampleDerbyDatabase";
+
   @Test
   public void testSimpleTableRendering() {
     String expectedResult = "+------------+------------+------------+\n"
@@ -365,6 +383,40 @@ public class KnoxShellTableTest {
     verify(connection, statement, resultSet, metadata);
   }
 
+  @Test
+  public void testJDBCBuilderUsingConnectionString() throws Exception {
+    System.setProperty(SYSTEM_PROPERTY_DERBY_STREAM_ERROR_FILE, "/dev/null");
+    final Path derbyDatabaseFolder = Paths.get(testFolder.newFolder().toPath().toString(), SAMPLE_DERBY_DATABASE_NAME);
+    Database derbyDatabase = null;
+    try {
+      derbyDatabase = prepareDerbyDatabase(derbyDatabaseFolder);
+      assertTrue(derbyDatabase.hasTable("BOOKS"));
+      final KnoxShellTable table = KnoxShellTable.builder().jdbc().driver(DerbyDatabase.DRIVER).connectTo(DerbyDatabase.PROTOCOL + derbyDatabaseFolder.toString())
+          .sql("select * from books");
+      assertEquals(2, table.getRows().size());
+      assertTrue(table.values("TITLE").containsAll(Arrays.asList("Apache Knox: The Definitive Guide", "Apache Knox: The Definitive Guide 2nd Edition")));
+    } finally {
+      if (derbyDatabase != null) {
+        derbyDatabase.shutdown();
+      }
+      System.clearProperty(SYSTEM_PROPERTY_DERBY_STREAM_ERROR_FILE);
+    }
+  }
+
+  private Database prepareDerbyDatabase(Path derbyDatabaseFolder) throws SQLException, IOException {
+    final Database derbyDatabase = new DerbyDatabase(derbyDatabaseFolder.toString());
+    derbyDatabase.create();
+    final String createTableSql = readFileToString(new File(getClass().getClassLoader().getResource("createBooksTable.sql").getFile()), UTF_8);
+    final String insertDataSql = readFileToString(new File(getClass().getClassLoader().getResource("insertBooks.sql").getFile()), UTF_8);
+    try (Connection connection = derbyDatabase.getConnection();
+        Statement createTableStatment = connection.createStatement();
+        Statement insertDataStatement = connection.createStatement();) {
+      createTableStatment.execute(createTableSql);
+      insertDataStatement.execute(insertDataSql);
+    }
+    return derbyDatabase;
+  }
+
   @Test (expected = IllegalArgumentException.class)
   public void testConversion() throws IllegalArgumentException {
       KnoxShellTable TABLE = new KnoxShellTable();
diff --git a/gateway-shell/src/test/resources/createBooksTable.sql b/gateway-shell/src/test/resources/createBooksTable.sql
new file mode 100644
index 0000000..1cbbc28
--- /dev/null
+++ b/gateway-shell/src/test/resources/createBooksTable.sql
@@ -0,0 +1,5 @@
+CREATE TABLE books (
+   book_id integer,
+   title varchar(64),
+   primary key(book_id)
+)
\ No newline at end of file
diff --git a/gateway-shell/src/test/resources/insertBooks.sql b/gateway-shell/src/test/resources/insertBooks.sql
new file mode 100644
index 0000000..d3905c9
--- /dev/null
+++ b/gateway-shell/src/test/resources/insertBooks.sql
@@ -0,0 +1,4 @@
+INSERT INTO books(book_id, title)
+VALUES
+(123, 'Apache Knox: The Definitive Guide'),
+(456, 'Apache Knox: The Definitive Guide 2nd Edition')
diff --git a/gateway-util-common/src/main/java/org/apache/knox/gateway/audit/api/CorrelationContext.java b/gateway-util-common/src/main/java/org/apache/knox/gateway/audit/api/CorrelationContext.java
index b8179b1..40f3a2b 100644
--- a/gateway-util-common/src/main/java/org/apache/knox/gateway/audit/api/CorrelationContext.java
+++ b/gateway-util-common/src/main/java/org/apache/knox/gateway/audit/api/CorrelationContext.java
@@ -17,7 +17,9 @@
  */
 package org.apache.knox.gateway.audit.api;
 
-public interface CorrelationContext {
+import java.io.Serializable;
+
+public interface CorrelationContext extends Serializable {
 
   /**
    * A unique value representing the current, active request.
diff --git a/gateway-util-common/src/main/java/org/apache/knox/gateway/util/X500PrincipalParser.java b/gateway-util-common/src/main/java/org/apache/knox/gateway/util/X500PrincipalParser.java
index c55c408..77c0c8b 100644
--- a/gateway-util-common/src/main/java/org/apache/knox/gateway/util/X500PrincipalParser.java
+++ b/gateway-util-common/src/main/java/org/apache/knox/gateway/util/X500PrincipalParser.java
@@ -128,6 +128,7 @@ public class X500PrincipalParser {
 
     rdnNameArray.clear();
 
+    boolean terminatedProperly = true;
     while(startIndex < dn.length()) {
       int endIndex;
       for(endIndex = startIndex; endIndex < dn.length(); endIndex++) {
@@ -150,13 +151,14 @@ public class X500PrincipalParser {
         rdnNameArray.add(nameValues);
         if(endIndex != dn.length()) {
           nameValues = new ArrayList<>();
+          terminatedProperly = false;
         } else {
-          nameValues = null;
+          terminatedProperly = true;
         }
       }
       startIndex = endIndex + 1;
     }
-    if(nameValues != null) {
+    if(!terminatedProperly) {
       throw new IllegalArgumentException("improperly terminated DN " + dn);
     }
   }
diff --git a/pom.xml b/pom.xml
index ed141a0..26ea966 100644
--- a/pom.xml
+++ b/pom.xml
@@ -177,6 +177,7 @@
         <cryptacular.version>1.2.3</cryptacular.version>
         <curator.version>4.2.0</curator.version>
         <dependency-check-maven.version>5.2.2</dependency-check-maven.version>
+        <derby.db.version>10.14.2.0</derby.db.version> <!-- 10.15.1.3 requires Java 9 -->
         <dockerfile-maven-plugin.version>1.4.13</dockerfile-maven-plugin.version>
         <dom4j.version>2.1.1</dom4j.version>
         <easymock.version>4.1</easymock.version>
@@ -2258,6 +2259,13 @@
             </dependency>
 
             <dependency>
+                <groupId>org.apache.derby</groupId>
+                <artifactId>derby</artifactId>
+                <version>${derby.db.version}</version>
+                <scope>test</scope>
+            </dependency>
+
+            <dependency>
                 <groupId>org.apache.velocity</groupId>
                 <artifactId>velocity</artifactId>
                 <version>${velocity.version}</version>