| /******************************************************************************* |
| * Copyright (c) 1998, 2012 Oracle. All rights reserved. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 |
| * which accompanies this distribution. |
| * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html |
| * and the Eclipse Distribution License is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * Contributors: |
| * Oracle - initial API and implementation from Oracle TopLink |
| ******************************************************************************/ |
| package org.eclipse.persistence.tools.db.relational.spi.jdbc; |
| |
| import java.sql.Connection; |
| import java.sql.DatabaseMetaData; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.util.List; |
| import org.eclipse.persistence.tools.db.relational.spi.ExternalDatabase; |
| import org.eclipse.persistence.tools.db.relational.spi.ExternalTableDescription; |
| import org.eclipse.persistence.tools.utility.ObjectTools; |
| import org.eclipse.persistence.tools.utility.collection.ListTools; |
| import org.eclipse.persistence.tools.utility.iterator.ResultSetIterator; |
| |
| /** |
| * This external database derives all of its metadata from a client-supplied JDBC connection. |
| * |
| * All the JDBC implementation classes have an embarassing |
| * preponderance of "defensive" programming that allow us to |
| * work with even the most misbehaved of JDBC drivers. This is |
| * because most of the exceptions encountered are the result |
| * of bugs in the wide variety of JDBC drivers we interact with, |
| * not bugs in Workbench code. (At least that's the hope....) |
| * An alternative to the various bits of defensive programming, |
| * we could pass in a listener whenever there is the possibility |
| * of an (SQL) exception. The listener would be notified of any |
| * exceptions, but we would continue processing with pre- |
| * determined defaults. |
| * ~bjv |
| * |
| * @version 2.6 |
| */ |
| @SuppressWarnings("nls") |
| final class JDBCExternalDatabase implements ExternalDatabase { |
| private final Connection connection; |
| private static final String[] EMPTY_STRING_ARRAY = new String[0]; |
| |
| /** |
| * |
| */ |
| JDBCExternalDatabase(Connection connection) { |
| super(); |
| this.connection = connection; |
| } |
| |
| |
| // ********** ExternalDatabase implementation ********** |
| |
| /** |
| * @see org.eclipse.persistence.tools.db.relational.spi.ExternalDatabase#getCatalogNames() |
| */ |
| @Override |
| public String[] getCatalogNames() { |
| ResultSet resultSet; |
| try { |
| resultSet = this.metaData().getCatalogs(); |
| } catch (SQLException ex) { |
| return EMPTY_STRING_ARRAY; // defensive - this is not fatal if unsupported by driver |
| } |
| return this.buildStringArray(resultSet); |
| } |
| |
| /** |
| * @see org.eclipse.persistence.tools.db.relational.spi.ExternalDatabase#getSchemaNames() |
| */ |
| @Override |
| public String[] getSchemaNames() { |
| ResultSet resultSet; |
| try { |
| resultSet = this.metaData().getSchemas(); |
| } catch (SQLException ex) { |
| return EMPTY_STRING_ARRAY; // defensive - this is not fatal if unsupported by driver |
| } |
| return this.buildStringArray(resultSet); |
| } |
| |
| /** |
| * @see org.eclipse.persistence.tools.db.relational.spi.ExternalDatabase#getTableTypeNames() |
| */ |
| @Override |
| public String[] getTableTypeNames() { |
| ResultSet resultSet; |
| try { |
| resultSet = this.metaData().getTableTypes(); |
| } catch (SQLException ex) { |
| return EMPTY_STRING_ARRAY; // defensive - this is not fatal if unsupported by driver |
| } |
| return this.buildStringArray(resultSet); |
| } |
| |
| /** |
| * @see org.eclipse.persistence.tools.db.relational.spi.ExternalDatabase#getTableDescriptions(String, String, String, String[]) |
| */ |
| @Override |
| public ExternalTableDescription[] getTableDescriptions(String catalogName, String schemaNamePattern, String tableNamePattern, String[] tableTypeNames) { |
| // #setCatalog(String) is used by Sybase, MS SQL Server, and others(?) |
| // to set a "local context" for later interactions with the server; |
| // "If the driver does not support catalogs, it will silently ignore this request." |
| if (catalogName != null) { |
| try { |
| this.getConnection().setCatalog(catalogName); |
| } catch (SQLException ex) { |
| // see comment above... |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| ResultSet resultSet; |
| try { |
| resultSet = this.metaData().getTables(catalogName, schemaNamePattern, tableNamePattern, tableTypeNames); |
| } catch (SQLException ex) { |
| // if #getTables() does not work, there's not much we can do... |
| throw new RuntimeException(ex); |
| } |
| List<ExternalTableDescription> tableDescriptions = ListTools.list(new ResultSetIterator<ExternalTableDescription>( |
| resultSet, |
| new ExternalTableDescriptionResultSetAdapter()) |
| ); |
| return tableDescriptions.toArray(new ExternalTableDescription[tableDescriptions.size()]); |
| } |
| |
| /** |
| * @see org.eclipse.persistence.tools.db.relational.spi.ExternalDatabase#getTableDescriptions() |
| */ |
| @Override |
| public ExternalTableDescription[] getTableDescriptions() { |
| // query for *all* the tables on the database - could be painful... |
| return this.getTableDescriptions(null, null, "%", null); |
| } |
| |
| |
| // ********** queries ********** |
| |
| private Connection getConnection() { |
| if (this.connection == null) { |
| throw new IllegalStateException("not connected"); |
| } |
| return this.connection; |
| } |
| |
| DatabaseMetaData metaData() { |
| try { |
| return this.getConnection().getMetaData(); |
| } catch (SQLException ex) { |
| // if we can't get meta-data, there's not much we can do... |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| /** |
| * @see Object#toString() |
| */ |
| @Override |
| public String toString() { |
| return ObjectTools.toString(this); |
| } |
| |
| |
| // ********** behavior ********** |
| |
| /** |
| * convert the specified single-column result set to an array of strings |
| */ |
| private String[] buildStringArray(ResultSet resultSet) { |
| List<String> strings = ListTools.list(new ResultSetIterator<String>(resultSet, new StringResultSetAdapter())); |
| return strings.toArray(new String[strings.size()]); |
| } |
| |
| |
| // ********** inner classes ********** |
| |
| /** |
| * Trim a single-column result set of strings. |
| */ |
| private static class StringResultSetAdapter implements ResultSetIterator.Adapter<String> { |
| @Override |
| public String buildNext(ResultSet rs) throws SQLException { |
| // result set column indexes are 1-based |
| String string = rs.getString(1); |
| // * Returns fixed-length strings |
| return (string == null) ? null : string.trim(); |
| } |
| } |
| |
| /** |
| * Convert the rows into external table descriptions. |
| * @see java.sql.DatabaseMetaData#getTables(String, String, String, String[]) |
| */ |
| private class ExternalTableDescriptionResultSetAdapter implements ResultSetIterator.Adapter<ExternalTableDescription> { |
| @Override |
| public ExternalTableDescription buildNext(ResultSet rs) throws SQLException { |
| return new JDBCExternalTableDescription(rs, JDBCExternalDatabase.this); |
| } |
| } |
| } |