Merge pull request #647 from ascrutae/feature/support-oracle-tns-url

support TNS URL of oracle plugin
diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/AbstractURLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/AbstractURLParser.java
index 74a897d..2d5d694 100644
--- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/AbstractURLParser.java
+++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/AbstractURLParser.java
@@ -31,14 +31,14 @@
      *
      * @return index range that database hosts.
      */
-    protected abstract int[] fetchDatabaseHostsIndexRange();
+    protected abstract URLLocation fetchDatabaseHostsIndexRange();
 
     /**
      * Fetch the index range that database name from connection url.
      *
      * @return index range that database name.
      */
-    protected abstract int[] fetchDatabaseNameIndexRange();
+    protected abstract URLLocation fetchDatabaseNameIndexRange();
 
     /**
      * Fetch database host(s) from connection url.
@@ -46,8 +46,8 @@
      * @return database host(s).
      */
     protected String fetchDatabaseHostsFromURL() {
-        int[] indexRange = fetchDatabaseHostsIndexRange();
-        return url.substring(indexRange[0], indexRange[1]);
+        URLLocation hostsLocation = fetchDatabaseHostsIndexRange();
+        return url.substring(hostsLocation.startIndex(), hostsLocation.endIndex());
     }
 
     /**
@@ -56,8 +56,8 @@
      * @return database name.
      */
     protected String fetchDatabaseNameFromURL() {
-        int[] indexRange = fetchDatabaseNameIndexRange();
-        return url.substring(indexRange[0], indexRange[1]);
+        URLLocation hostsLocation = fetchDatabaseNameIndexRange();
+        return url.substring(hostsLocation.startIndex(), hostsLocation.endIndex());
     }
 
     /**
diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/H2URLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/H2URLParser.java
index 416b486..1e79d8e 100644
--- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/H2URLParser.java
+++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/H2URLParser.java
@@ -55,20 +55,20 @@
     }
 
     @Override
-    protected int[] fetchDatabaseHostsIndexRange() {
+    protected URLLocation fetchDatabaseHostsIndexRange() {
         int hostLabelStartIndex = url.indexOf("//");
         int hostLabelEndIndex = url.indexOf("/", hostLabelStartIndex + 2);
-        return new int[] {hostLabelStartIndex + 2, hostLabelEndIndex};
+        return new URLLocation(hostLabelStartIndex + 2, hostLabelEndIndex);
     }
 
     @Override
-    protected int[] fetchDatabaseNameIndexRange() {
+    protected URLLocation fetchDatabaseNameIndexRange() {
         int databaseStartTag = url.lastIndexOf("/");
         int databaseEndTag = url.indexOf(";");
         if (databaseEndTag == -1) {
             databaseEndTag = url.length();
         }
-        return new int[] {databaseStartTag + 1, databaseEndTag};
+        return new URLLocation(databaseStartTag + 1, databaseEndTag);
     }
 
     @Override
diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/MysqlURLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/MysqlURLParser.java
index 0f71dd8..d463c05 100644
--- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/MysqlURLParser.java
+++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/MysqlURLParser.java
@@ -41,26 +41,26 @@
     }
 
     @Override
-    protected int[] fetchDatabaseHostsIndexRange() {
+    protected URLLocation fetchDatabaseHostsIndexRange() {
         int hostLabelStartIndex = url.indexOf("//");
         int hostLabelEndIndex = url.indexOf("/", hostLabelStartIndex + 2);
-        return new int[] {hostLabelStartIndex + 2, hostLabelEndIndex};
+        return new URLLocation(hostLabelStartIndex + 2, hostLabelEndIndex);
     }
 
     @Override
-    protected int[] fetchDatabaseNameIndexRange() {
+    protected URLLocation fetchDatabaseNameIndexRange() {
         int databaseStartTag = url.lastIndexOf("/");
         int databaseEndTag = url.indexOf("?", databaseStartTag);
         if (databaseEndTag == -1) {
             databaseEndTag = url.length();
         }
-        return new int[] {databaseStartTag + 1, databaseEndTag};
+        return new URLLocation(databaseStartTag + 1, databaseEndTag);
     }
 
     @Override
     public ConnectionInfo parse() {
-        int[] hostRangeIndex = fetchDatabaseHostsIndexRange();
-        String hosts = url.substring(hostRangeIndex[0], hostRangeIndex[1]);
+        URLLocation location = fetchDatabaseHostsIndexRange();
+        String hosts = url.substring(location.startIndex(), location.endIndex());
         String[] hostSegment = hosts.split(",");
         if (hostSegment.length > 1) {
             StringBuilder sb = new StringBuilder();
diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/OracleURLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/OracleURLParser.java
index 78523da..bf3c91e 100644
--- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/OracleURLParser.java
+++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/OracleURLParser.java
@@ -18,8 +18,11 @@
 
 package org.skywalking.apm.plugin.jdbc.connectionurl.parser;
 
+import java.util.ArrayList;
+import java.util.List;
 import org.skywalking.apm.network.trace.component.ComponentsDefine;
 import org.skywalking.apm.plugin.jdbc.trace.ConnectionInfo;
+import org.skywalking.apm.util.StringUtil;
 
 /**
  * {@link OracleURLParser} presents that how to parse oracle connection url.
@@ -38,29 +41,61 @@
 
     private static final String DB_TYPE = "Oracle";
     private static final int DEFAULT_PORT = 1521;
+    public static final String SERVICE_NAME_FLAG = "@//";
+    public static final String TNSNAME_URL_FLAG = "DESCRIPTION";
 
     public OracleURLParser(String url) {
         super(url);
     }
 
     @Override
-    protected int[] fetchDatabaseHostsIndexRange() {
-        int hostLabelStartIndex = url.indexOf("@");
+    protected URLLocation fetchDatabaseHostsIndexRange() {
+        int hostLabelStartIndex;
+        if (isServiceNameURL()) {
+            hostLabelStartIndex = url.indexOf(SERVICE_NAME_FLAG) + 3;
+        } else {
+            hostLabelStartIndex = url.indexOf("@") + 1;
+        }
         int hostLabelEndIndex = url.lastIndexOf(":");
-        return new int[] {hostLabelStartIndex + 1, hostLabelEndIndex};
+        return new URLLocation(hostLabelStartIndex, hostLabelEndIndex);
     }
 
     @Override
-    protected int[] fetchDatabaseNameIndexRange() {
-        return new int[0];
+    protected URLLocation fetchDatabaseNameIndexRange() {
+        int hostLabelStartIndex;
+        int hostLabelEndIndex = url.length();
+        if (isServiceNameURL()) {
+            hostLabelStartIndex = url.lastIndexOf("/") + 1;
+        } else if (isTNSNameURL()) {
+            hostLabelStartIndex = url.indexOf("=", url.indexOf("SERVICE_NAME")) + 1;
+            hostLabelEndIndex = url.indexOf(")", hostLabelStartIndex);
+        } else {
+            hostLabelStartIndex = url.lastIndexOf(":") + 1;
+        }
+        return new URLLocation(hostLabelStartIndex, hostLabelEndIndex);
+    }
+
+    private boolean isServiceNameURL() {
+        return url.contains(SERVICE_NAME_FLAG);
+    }
+
+    private boolean isTNSNameURL() {
+        return url.contains(TNSNAME_URL_FLAG);
     }
 
     @Override
     public ConnectionInfo parse() {
-        int[] hostRangeIndex = fetchDatabaseHostsIndexRange();
+        if (isTNSNameURL()) {
+            return tnsNameURLParse();
+        } else {
+            return commonsURLParse();
+        }
+    }
+
+    private ConnectionInfo commonsURLParse() {
         String host = fetchDatabaseHostsFromURL();
         String[] hostSegment = splitDatabaseAddress(host);
-        String databaseName = url.substring(hostRangeIndex[1] + 1);
+        String databaseName = fetchDatabaseNameFromURL();
         if (hostSegment.length == 1) {
             return new ConnectionInfo(ComponentsDefine.ORACLE, DB_TYPE, host, DEFAULT_PORT, databaseName);
         } else {
@@ -68,6 +103,40 @@
         }
     }
 
+    private ConnectionInfo tnsNameURLParse() {
+        String host = parseDatabaseHostsFromURL();
+        String databaseName = fetchDatabaseNameFromURL();
+        return new ConnectionInfo(ComponentsDefine.ORACLE, DB_TYPE, host, databaseName);
+    }
+
+    private String parseDatabaseHostsFromURL() {
+        int beginIndex = url.indexOf("DESCRIPTION");
+        List<String> hosts = new ArrayList<String>();
+        do {
+            int hostStartIndex = url.indexOf("HOST", beginIndex);
+            if (hostStartIndex == -1) {
+                break;
+            }
+            int equalStartIndex = url.indexOf("=", hostStartIndex);
+            int hostEndIndex = url.indexOf(")", hostStartIndex);
+            String host = url.substring(equalStartIndex + 1, hostEndIndex);
+
+            int port = DEFAULT_PORT;
+            int portStartIndex = url.indexOf("PORT", hostEndIndex);
+            int portEndIndex = url.length();
+            if (portStartIndex != -1) {
+                int portEqualStartIndex = url.indexOf("=", portStartIndex);
+                portEndIndex = url.indexOf(")", portEqualStartIndex);
+                port = Integer.parseInt(url.substring(portEqualStartIndex + 1, portEndIndex).trim());
+            }
+            hosts.add(host.trim() + ":" + port);
+            beginIndex = portEndIndex;
+        }
+        while (true);
+
+        return StringUtil.join(',', hosts.toArray(new String[0]));
+    }
+
     private String[] splitDatabaseAddress(String address) {
         String[] hostSegment = address.split(":");
         return hostSegment;
diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/PostgreSQLURLParser.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/PostgreSQLURLParser.java
index 2b2451a..4509d73 100644
--- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/PostgreSQLURLParser.java
+++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/PostgreSQLURLParser.java
@@ -41,26 +41,26 @@
     }
 
     @Override
-    protected int[] fetchDatabaseHostsIndexRange() {
+    protected URLLocation fetchDatabaseHostsIndexRange() {
         int hostLabelStartIndex = url.indexOf("//");
         int hostLabelEndIndex = url.indexOf("/", hostLabelStartIndex + 2);
-        return new int[] {hostLabelStartIndex + 2, hostLabelEndIndex};
+        return new URLLocation(hostLabelStartIndex + 2, hostLabelEndIndex);
     }
 
     @Override
-    protected int[] fetchDatabaseNameIndexRange() {
+    protected URLLocation fetchDatabaseNameIndexRange() {
         int databaseStartTag = url.lastIndexOf("/");
         int databaseEndTag = url.indexOf("?", databaseStartTag);
         if (databaseEndTag == -1) {
             databaseEndTag = url.length();
         }
-        return new int[] {databaseStartTag + 1, databaseEndTag};
+        return new URLLocation(databaseStartTag + 1, databaseEndTag);
     }
 
     @Override
     public ConnectionInfo parse() {
-        int[] hostRangeIndex = fetchDatabaseHostsIndexRange();
-        String hosts = url.substring(hostRangeIndex[0], hostRangeIndex[1]);
+        URLLocation location = fetchDatabaseHostsIndexRange();
+        String hosts = url.substring(location.startIndex(), location.endIndex());
         String[] hostSegment = hosts.split(",");
         if (hostSegment.length > 1) {
             StringBuilder sb = new StringBuilder();
diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/URLLocation.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/URLLocation.java
new file mode 100644
index 0000000..6890879
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/main/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/URLLocation.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017, OpenSkywalking Organization All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Project repository: https://github.com/OpenSkywalking/skywalking
+ */
+
+package org.skywalking.apm.plugin.jdbc.connectionurl.parser;
+
+public class URLLocation {
+    private final int startIndex;
+    private final int endIndex;
+
+    public URLLocation(int startIndex, int endIndex) {
+        this.startIndex = startIndex;
+        this.endIndex = endIndex;
+    }
+
+    public int startIndex() {
+        return startIndex;
+    }
+
+    public int endIndex() {
+        return endIndex;
+    }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParserTest.java b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParserTest.java
index 7f15d33..2f75988 100644
--- a/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParserTest.java
+++ b/apm-sniffer/apm-sdk-plugin/jdbc-commons/src/test/java/org/skywalking/apm/plugin/jdbc/connectionurl/parser/URLParserTest.java
@@ -74,6 +74,30 @@
     }
 
     @Test
+    public void testParseOracleServiceName() {
+        ConnectionInfo connectionInfo = new URLParser().parser("jdbc:oracle:thin:@//localhost:1521/orcl");
+        assertThat(connectionInfo.getDBType(), is("Oracle"));
+        assertThat(connectionInfo.getDatabaseName(), is("orcl"));
+        assertThat(connectionInfo.getDatabasePeer(), is("localhost:1521"));
+    }
+
+    @Test
+    public void testParseOracleTNSName() {
+        ConnectionInfo connectionInfo = new URLParser().parser("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST= localhost )(PORT= 1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orcl)))");
+        assertThat(connectionInfo.getDBType(), is("Oracle"));
+        assertThat(connectionInfo.getDatabaseName(), is("orcl"));
+        assertThat(connectionInfo.getDatabasePeer(), is("localhost:1521"));
+    }
+
+    @Test
+    public void testParseOracleTNSNameWithMultiAddress() {
+        ConnectionInfo connectionInfo = new URLParser().parser("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL= TCP)(HOST=hostA)(PORT= 1523 ))(ADDRESS=(PROTOCOL=TCP)(HOST=hostB)(PORT= 1521 )))(SOURCE_ROUTE=yes)(CONNECT_DATA=(SERVICE_NAME=orcl)))");
+        assertThat(connectionInfo.getDBType(), is("Oracle"));
+        assertThat(connectionInfo.getDatabaseName(), is("orcl"));
+        assertThat(connectionInfo.getDatabasePeer(), is("hostA:1523,hostB:1521"));
+    }
+
+    @Test
     public void testParseOracleJDBCURLWithUserNameAndPassword() {
         ConnectionInfo connectionInfo = new URLParser().parser("jdbc:oracle:thin:scott/tiger@myhost:1521:orcl");
         assertThat(connectionInfo.getDBType(), is("Oracle"));