http://git-wip-us.apache.org/repos/asf/sqoop/blob/fa3c77b6/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleConnectionFactory.java ---------------------------------------------------------------------- diff --git a/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleConnectionFactory.java b/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleConnectionFactory.java new file mode 100644 index 0000000..3ebb0d4 --- /dev/null +++ b/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleConnectionFactory.java @@ -0,0 +1,246 @@ +/** + * 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.sqoop.connector.jdbc.oracle.util; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.apache.sqoop.connector.jdbc.oracle.OracleJdbcConnectorConstants; +import org.apache.sqoop.connector.jdbc.oracle.configuration.ConnectionConfig; + +/** + * Create and initialize connections to Oracle RDBMS. + */ +public class OracleConnectionFactory { + + protected OracleConnectionFactory() { + } + + private static final Logger LOG = + Logger.getLogger(OracleConnectionFactory.class); + + public static Connection makeConnection(ConnectionConfig config) throws SQLException { + + String connectStr = config.connectionString; + String username = config.username; + String password = config.password; + Properties additionalProps = new Properties(); + if(config.jdbcProperties != null) { + additionalProps.putAll(config.jdbcProperties); + } + + Connection connection = + OracleConnectionFactory.createOracleJdbcConnection( + OracleJdbcConnectorConstants.ORACLE_JDBC_DRIVER_CLASS, + connectStr, username, password, additionalProps); + //TODO: This is from the other Oracle Manager +// if (username == null) { +// username = OracleManager.getSessionUser(connection); +// } + OracleUtilities.setCurrentSessionUser(username); + return connection; + } + + public static Connection createOracleJdbcConnection( + String jdbcDriverClassName, String jdbcUrl, String username, + String password) throws SQLException { + Properties props = null; + return createOracleJdbcConnection(jdbcDriverClassName, jdbcUrl, username, + password, props); + } + + public static Connection createOracleJdbcConnection( + String jdbcDriverClassName, String jdbcUrl, String username, + String password, Properties additionalProps) throws SQLException { + + loadJdbcDriver(jdbcDriverClassName); + Connection connection = + createConnection(jdbcUrl, username, password, additionalProps); + + // Only OraOopDBRecordReader will call initializeOracleConnection(), as + // we only need to initialize the session(s) prior to the mapper starting + // it's job. + // i.e. We don't need to initialize the sessions in order to get the + // table's data-files etc. + + // initializeOracleConnection(connection, conf); + + return connection; + } + + private static void loadJdbcDriver(String jdbcDriverClassName) { + + try { + Class.forName(jdbcDriverClassName); + } catch (ClassNotFoundException ex) { + String errorMsg = + "Unable to load the jdbc driver class : " + jdbcDriverClassName; + LOG.error(errorMsg); + throw new RuntimeException(errorMsg); + } + } + + private static Connection createConnection(String jdbcUrl, String username, + String password, Properties additionalProps) throws SQLException { + + Properties props = new Properties(); + if (username != null) { + props.put("user", username); + } + + if (password != null) { + props.put("password", password); + } + + if (additionalProps != null && additionalProps.size() > 0) { + props.putAll(additionalProps); + } + + OracleUtilities.checkJavaSecurityEgd(); + + try { + Connection result = DriverManager.getConnection(jdbcUrl, props); + result.setAutoCommit(false); + return result; + } catch (SQLException ex) { + String errorMsg = String.format( + "Unable to obtain a JDBC connection to the URL \"%s\" as user \"%s\": ", + jdbcUrl, (username != null) ? username : "[null]"); + LOG.error(errorMsg, ex); + throw ex; + } + } + + public static void initializeOracleConnection(Connection connection, + ConnectionConfig config) throws SQLException { + + connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); + + connection.setAutoCommit(false); + + OracleQueries.setConnectionTimeZone(connection, config.timeZone); + + setSessionClientInfo(connection, config); + + OracleQueries.setJdbcFetchSize(connection, config.fetchSize); + + executeOraOopSessionInitializationStatements(connection, + config.initializationStatements); + } + + public static void setSessionClientInfo(Connection connection, + ConnectionConfig config) { + + String sql = ""; + try { + sql = + "begin \n" + + " dbms_application_info.set_module(module_name => " + + "'%s', action_name => '%s'); \n" + + "end;"; + + String oracleSessionActionName = config.actionName; + + sql = + String.format(sql, + OracleJdbcConnectorConstants.ORACLE_SESSION_MODULE_NAME, + oracleSessionActionName); + + Statement statement = connection.createStatement(); + statement.execute(sql); + LOG.info("Initializing Oracle session with SQL :\n" + sql); + } catch (Exception ex) { + LOG.error(String.format("An error occurred while attempting to execute " + + "the following Oracle session-initialization statement:" + "\n%s" + + "\nError:" + "\n%s", sql, ex.getMessage())); + } + } + + public static void executeOraOopSessionInitializationStatements( + Connection connection, String sessionInitializationStatements) { + String statementsStr = sessionInitializationStatements; + if(StringUtils.isEmpty(statementsStr)) { + statementsStr =OracleJdbcConnectorConstants. + ORACLE_SESSION_INITIALIZATION_STATEMENTS_DEFAULT; + } + + List<String> statements = + parseOraOopSessionInitializationStatements(statementsStr); + + if (statements.size() == 0) { + LOG.warn("No Oracle 'session initialization' statements were found to " + + "execute."); + } else { + for (String statement : statements) { + try { + connection.createStatement().execute(statement); + LOG.info("Initializing Oracle session with SQL : " + statement); + } catch (Exception ex) { + LOG.error(String.format( + "An error occurred while attempting to execute " + + "the following Oracle session-initialization statement:" + + "\n%s" + "\nError:" + "\n%s", statement, ex.getMessage())); + } + } + } + } + + public static List<String> parseOraOopSessionInitializationStatements( + String sessionInitializationStatements) { + + ArrayList<String> result = new ArrayList<String>(); + + if (sessionInitializationStatements != null + && !sessionInitializationStatements.isEmpty()) { + String[] initializationStatements = + sessionInitializationStatements.split(";"); + for (String initializationStatement : initializationStatements) { + initializationStatement = initializationStatement.trim(); + if (initializationStatement != null + && !initializationStatement.isEmpty() + && !initializationStatement + .startsWith(OracleJdbcConnectorConstants.Oracle. + ORACLE_SQL_STATEMENT_COMMENT_TOKEN)) { + + LOG.debug(String + .format( + "initializationStatement (quoted & pre-expression " + + "evaluation) = \"%s\"", + initializationStatement)); + + //TODO: Not supported in Sqoop 2? + /*initializationStatement = + OracleUtilities.replaceConfigurationExpression( + initializationStatement, conf);*/ + + result.add(initializationStatement); + } + } + } + return result; + } + +}
http://git-wip-us.apache.org/repos/asf/sqoop/blob/fa3c77b6/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleDataChunk.java ---------------------------------------------------------------------- diff --git a/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleDataChunk.java b/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleDataChunk.java new file mode 100644 index 0000000..5b24fe3 --- /dev/null +++ b/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleDataChunk.java @@ -0,0 +1,48 @@ +/** + * 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.sqoop.connector.jdbc.oracle.util; + +import org.apache.sqoop.job.etl.Partition; + +/** + * How data should be split between mappers. + */ +public abstract class OracleDataChunk extends Partition { + + private String id; + + public abstract long getNumberOfBlocks(); + + public String getWhereClause() { + return "1=1"; + } + + public String getPartitionClause() { + return ""; + } + + public String getId() { + return id; + } + + public void setId(String newId) { + this.id = newId; + } + +} http://git-wip-us.apache.org/repos/asf/sqoop/blob/fa3c77b6/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleDataChunkExtent.java ---------------------------------------------------------------------- diff --git a/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleDataChunkExtent.java b/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleDataChunkExtent.java new file mode 100644 index 0000000..2f794c2 --- /dev/null +++ b/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleDataChunkExtent.java @@ -0,0 +1,109 @@ +/** + * 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.sqoop.connector.jdbc.oracle.util; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +import org.apache.sqoop.connector.jdbc.oracle.OracleJdbcConnectorConstants; + +/** + * Data should be split by extent for ROWID scans. + */ +public class OracleDataChunkExtent extends OracleDataChunk { + + private int oracleDataObjectId; + private int relativeDatafileNumber; + private long startBlockNumber; + private long finishBlockNumber; + + public OracleDataChunkExtent() { + + } + + public OracleDataChunkExtent(String id, int oracleDataObjectId, + int relativeDatafileNumber, long startBlockNumber, + long finishBlockNumber) { + + this.setId(id); + this.oracleDataObjectId = oracleDataObjectId; + this.relativeDatafileNumber = relativeDatafileNumber; + this.startBlockNumber = startBlockNumber; + this.finishBlockNumber = finishBlockNumber; + } + + @Override + public String getWhereClause() { + return String.format( + "(rowid >= dbms_rowid.rowid_create(%d, %d, %d, %d, %d)", + OracleJdbcConnectorConstants.Oracle.ROWID_EXTENDED_ROWID_TYPE, + this.oracleDataObjectId, this.relativeDatafileNumber, + this.startBlockNumber, 0) + + String.format( + " AND rowid <= dbms_rowid.rowid_create(%d, %d, %d, %d, %d))", + OracleJdbcConnectorConstants.Oracle.ROWID_EXTENDED_ROWID_TYPE, + this.oracleDataObjectId, this.relativeDatafileNumber, + this.finishBlockNumber, + OracleJdbcConnectorConstants.Oracle.ROWID_MAX_ROW_NUMBER_PER_BLOCK); + } + + @Override + public void write(DataOutput output) throws IOException { + output.writeUTF(this.getId()); + output.writeInt(this.oracleDataObjectId); + output.writeInt(this.relativeDatafileNumber); + output.writeLong(this.startBlockNumber); + output.writeLong(this.finishBlockNumber); + } + + @Override + public void readFields(DataInput input) throws IOException { + this.setId(input.readUTF()); + this.oracleDataObjectId = input.readInt(); + this.relativeDatafileNumber = input.readInt(); + this.startBlockNumber = input.readLong(); + this.finishBlockNumber = input.readLong(); + } + + @Override + public long getNumberOfBlocks() { + if (this.finishBlockNumber == 0L && this.startBlockNumber == 0L) { + return 0; + } else { + return (this.finishBlockNumber - this.startBlockNumber) + 1L; + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("\n\t%s = %s", "id", getId())); + sb.append(String.format("\n\t%s = %s", + "oracleDataObjectId", oracleDataObjectId)); + sb.append(String.format("\n\t%s = %s", + "relativeDatafileNumber", relativeDatafileNumber)); + sb.append(String.format("\n\t%s = %s", + "startBlockNumber", startBlockNumber)); + sb.append(String.format("\n\t%s = %s", + "finishBlockNumber", finishBlockNumber)); + return sb.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/sqoop/blob/fa3c77b6/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleDataChunkPartition.java ---------------------------------------------------------------------- diff --git a/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleDataChunkPartition.java b/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleDataChunkPartition.java new file mode 100644 index 0000000..0a47e1f --- /dev/null +++ b/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleDataChunkPartition.java @@ -0,0 +1,85 @@ +/** + * 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.sqoop.connector.jdbc.oracle.util; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +/** + * Data should be split by partition. + */ +public class OracleDataChunkPartition extends OracleDataChunk { + + private boolean isSubPartition; + private long blocks; + + public OracleDataChunkPartition() { + + } + + OracleDataChunkPartition(String partitionName, boolean isSubPartition, + long blocks) { + this.setId(partitionName); + this.isSubPartition = isSubPartition; + this.blocks = blocks; + } + + @Override + public long getNumberOfBlocks() { + return this.blocks; + } + + @Override + public void write(DataOutput output) throws IOException { + output.writeUTF(this.getId()); + output.writeBoolean(this.isSubPartition); + output.writeLong(this.blocks); + } + + @Override + public void readFields(DataInput input) throws IOException { + this.setId(input.readUTF()); + this.isSubPartition = input.readBoolean(); + this.blocks = input.readLong(); + } + + @Override + public String getPartitionClause() { + StringBuilder sb = new StringBuilder(); + sb.append(" "); + if (this.isSubPartition) { + sb.append("SUBPARTITION"); + } else { + sb.append("PARTITION"); + } + sb.append("(\"").append(this.getId()).append("\")"); + return sb.toString(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("\n\t%s = %s", "id", getId())); + sb.append(String.format("\n\t%s = %s", "isSubPartition", isSubPartition)); + sb.append(String.format("\n\t%s = %s", "blocks", blocks)); + return sb.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/sqoop/blob/fa3c77b6/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleGenerics.java ---------------------------------------------------------------------- diff --git a/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleGenerics.java b/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleGenerics.java new file mode 100644 index 0000000..0ecf587 --- /dev/null +++ b/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleGenerics.java @@ -0,0 +1,64 @@ +/** + * 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.sqoop.connector.jdbc.oracle.util; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * Generic class to hold list of objects. + */ +public class OracleGenerics { + + /** + * Generic class to hold list of objects. + */ + public static class ObjectList<T> { + + private List<T> objects; + + public ObjectList() { + + this.objects = new ArrayList<T>(); + } + + public void add(T item) { + + this.objects.add(item); + } + + public int size() { + + return this.objects.size(); + } + + public T get(int index) { + + return this.objects.get(index); + } + + public Iterator<T> iterator() { + + return this.objects.iterator(); + } + + } + +} http://git-wip-us.apache.org/repos/asf/sqoop/blob/fa3c77b6/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleJdbcUrl.java ---------------------------------------------------------------------- diff --git a/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleJdbcUrl.java b/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleJdbcUrl.java new file mode 100644 index 0000000..65d0092 --- /dev/null +++ b/connector/connector-oracle-jdbc/src/main/java/org/apache/sqoop/connector/jdbc/oracle/util/OracleJdbcUrl.java @@ -0,0 +1,244 @@ +/** + * 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.sqoop.connector.jdbc.oracle.util; + +import org.apache.sqoop.connector.jdbc.oracle.util.OracleUtilities.JdbcOracleThinConnection; +import org.apache.sqoop.connector.jdbc.oracle.util.OracleUtilities + .JdbcOracleThinConnectionParsingError; + +/** + * Parses the Oracle connection string. + */ +public class OracleJdbcUrl { + + private String jdbcConnectString; + + public OracleJdbcUrl(String jdbcConnectString) { + + if (jdbcConnectString == null) { + throw new IllegalArgumentException( + "The jdbcConnectionString argument must not be null."); + } + + if (jdbcConnectString.isEmpty()) { + throw new IllegalArgumentException( + "The jdbcConnectionString argument must not be empty."); + } + + this.jdbcConnectString = jdbcConnectString; + } + + public JdbcOracleThinConnection parseJdbcOracleThinConnectionString() + throws JdbcOracleThinConnectionParsingError { + + /* + * http://wiki.oracle.com/page/JDBC + * + * There are different flavours of JDBC connections for Oracle, including: + * Thin E.g. jdbc:oracle:thin:@localhost.locadomain:1521:orcl + * + * A pure Java driver used on the client side that does not need an Oracle + * client installation. It is recommended that you use this driver unless + * you need support for non-TCP/IP networks because it provides for maximum + * portability and performance. + * + * Oracle Call Interface driver (OCI). E.g. jdbc:oracle:oci8:@orcl.world + * //<- "orcl.world" is a TNS entry + * + * This uses the Oracle client installation libraries and interfaces. If you + * want to support connection pooling or client side caching of requests, + * use this driver. You will also need this driver if you are using + * transparent application failover (TAF) from your application as well as + * strong authentication like Kerberos and PKI certificates. + * + * JDBC-ODBC bridge. E.g. jdbc:odbc:mydatabase //<- "mydatabase" is an ODBC + * data source. + * + * This uses the ODBC driver in Windows to connect to the database. + */ + + String hostName = null; + int port = 0; + String sid = null; + String service = null; + + String jdbcUrl = this.jdbcConnectString.trim(); + + // If there are any parameters included at the end of the connection URL, + // let's remove them now... + int paramsIdx = jdbcUrl.indexOf("?"); + if (paramsIdx > -1) { + jdbcUrl = jdbcUrl.substring(0, paramsIdx); + } + + /* + * The format of an Oracle jdbc URL is one of: + * jdbc:oracle:<driver-type>:@tnsname - for tnsname based login + * jdbc:oracle:<driver-type>:@<host>:<port>:<sid> + * jdbc:oracle:<driver-type>:@<host>:<port>/<service> + * jdbc:oracle:<driver-type>:@<host>:<port>/<service>?<parameters> + * jdbc:oracle:<driver-type>:@//<host>:<port>/<service> + * jdbc:oracle:<driver-type>:@//<host>:<port>/<service>?<parameters> + */ + + // Split the URL on its ":" characters... + String[] jdbcFragments = jdbcUrl.trim().split(":"); + + // Clean up each fragment of the URL... + for (int idx = 0; idx < jdbcFragments.length; idx++) { + jdbcFragments[idx] = jdbcFragments[idx].trim(); + } + + // Check we can proceed... + if (jdbcFragments.length < 4 || jdbcFragments.length > 6) { + throw new JdbcOracleThinConnectionParsingError( + String.format( + "There should be 4, 5 or 6 colon-separated pieces of data in the " + + "JDBC URL, such as:\n\tjdbc:oracle:<driver-type>:@tnsname\n" + + "\tjdbc:oracle:<driver-type>:@<host>:<port>:<sid>\n" + + "\tjdbc:oracle:<driver-type>:@<host>:<port>/<service>\n" + + "\tjdbc:oracle:<driver-type>:@<host>:<port>/<service>?<parameters>\n" + + "The JDBC URL specified was:\n" + + "%s\n" + + "which contains %d pieces of colon-separated data.", + this.jdbcConnectString, jdbcFragments.length)); + } + + // jdbc + if (!jdbcFragments[0].equalsIgnoreCase("jdbc")) { + throw new JdbcOracleThinConnectionParsingError( + "The first item in the colon-separated JDBC URL must be \"jdbc\"."); + } + + // jdbc:oracle + if (!jdbcFragments[1].equalsIgnoreCase("oracle")) { + throw new JdbcOracleThinConnectionParsingError( + "The second item in the colon-separated JDBC URL must be \"oracle\"."); + } + + // jdbc:oracle:thin + if (!jdbcFragments[2].equalsIgnoreCase("thin")) { + throw new JdbcOracleThinConnectionParsingError( + String + .format( + "The Oracle \"thin\" JDBC driver is not being used.\n" + + "The third item in the colon-separated JDBC URL must " + + "be \"thin\", not \"%s\".", + jdbcFragments[2])); + } + + // jdbc:oracle:thin:@<host> + hostName = jdbcFragments[3]; + if (hostName.isEmpty() || hostName.equalsIgnoreCase("@")) { + throw new JdbcOracleThinConnectionParsingError( + "The fourth item in the colon-separated JDBC URL (the host name) " + + "must not be empty."); + } + + if (!hostName.startsWith("@")) { + throw new JdbcOracleThinConnectionParsingError( + "The fourth item in the colon-separated JDBC URL (the host name) " + + "must a prefixed with the \"@\" character."); + } + + String portStr = ""; + String tnsName = ""; + + switch (jdbcFragments.length) { + case 6: + // jdbc:oracle:<driver-type>:@<host>:<port>:<sid> + portStr = jdbcFragments[4]; + sid = jdbcFragments[5]; + break; + + case 5: + // jdbc:oracle:<driver-type>:@<host>:<port>/<service> + String[] portAndService = jdbcFragments[4].split("/"); + if (portAndService.length != 2) { + throw new JdbcOracleThinConnectionParsingError( + "The fifth colon-separated item in the JDBC URL " + + "(<port>/<service>) must contain two items " + + "separated by a \"/\"."); + } + portStr = portAndService[0].trim(); + service = portAndService[1].trim(); + break; + + case 4: + // jdbc:oracle:<driver-type>:@tnsname + tnsName = jdbcFragments[3].trim(); + break; + + default: + throw new JdbcOracleThinConnectionParsingError("Internal error parsing " + + "JDBC connection string."); + } + + if (jdbcFragments.length > 4) { + if (portStr.isEmpty()) { + throw new JdbcOracleThinConnectionParsingError( + "The fifth item in the colon-separated JDBC URL (the port) must not" + + " be empty."); + } + + try { + port = Integer.parseInt(portStr); + } catch (NumberFormatException ex) { + throw new JdbcOracleThinConnectionParsingError( + String + .format( + "The fifth item in the colon-separated JDBC URL (the port) " + + "must be a valid number.\n" + + "\"%s\" could not be parsed as an integer.", portStr)); + } + + if (port <= 0) { + throw new JdbcOracleThinConnectionParsingError( + String + .format( + "The fifth item in the colon-separated JDBC URL (the port) " + + "must be greater than zero.\n" + + "\"%s\" was specified.", portStr)); + } + } + + if (sid == null && service == null && tnsName == null) { + throw new JdbcOracleThinConnectionParsingError( + "The JDBC URL does not contain a SID or SERVICE. The URL should look " + + "like one of these:\n\tjdbc:oracle:<driver-type>:@tnsname\n" + + "\tjdbc:oracle:<driver-type>:@<host>:<port>:<sid>\n" + + "\tjdbc:oracle:<driver-type>:@<host>:<port>/<service>\n" + + "\tjdbc:oracle:<driver-type>:@<host>:<port>/<service>?<parameters>\n" + + "\tjdbc:oracle:<driver-type>:@//<host>:<port>/<service>\n" + + "\tjdbc:oracle:<driver-type>:@<host>:<port>/<service>?<parameters>"); + } + + // Remove the "@" prefix of the hostname + JdbcOracleThinConnection result = + new JdbcOracleThinConnection(hostName.replaceFirst("^[@][/]{0,2}", "") + , port, sid, service, tnsName.replaceFirst("^[@][/]{0,2}", "")); + + return result; + } + + public String getConnectionUrl() { + return this.jdbcConnectString; + } + +}
