/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.jdbc.metadata.impl; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.designer.jdbc.JdbcException; import org.teiid.designer.jdbc.JdbcPlugin; import org.teiid.designer.jdbc.metadata.Capabilities; import org.teiid.designer.jdbc.metadata.DatabaseInfo; import org.teiid.designer.jdbc.metadata.Includes; import org.teiid.designer.jdbc.metadata.JdbcCatalog; import org.teiid.designer.jdbc.metadata.JdbcDatabase; import org.teiid.designer.jdbc.metadata.JdbcNode; import org.teiid.designer.jdbc.metadata.JdbcNodeVisitor; import org.teiid.designer.jdbc.metadata.JdbcSchema; import org.teiid.designer.jdbc.metadata.JdbcTableType; /** * JdbcDatabaseImpl * * @since 8.0 */ public class JdbcDatabaseImpl extends JdbcNodeImpl implements JdbcDatabase, InternalJdbcDatabase { private final Connection connection; private DatabaseMetaData metadata; private Capabilities capabilities; private DatabaseInfo databaseInfo; private final JdbcNodeCache cache; private final JdbcNodeSelections selections; private final IncludesImpl includes; private final Object capabilitiesLock = new Object(); private final Object databaseInfoLock = new Object(); /** * Construct an instance of JdbcDatabaseImpl. */ public JdbcDatabaseImpl( final Connection connection, final String name ) { this(connection, name, new JdbcNodeSelections()); } /** * Construct an instance of JdbcDatabaseImpl. */ public JdbcDatabaseImpl( final Connection connection, final String name, final JdbcNodeSelections selections ) { super(DATABASE, name, null); CoreArgCheck.isNotNull(connection); this.connection = connection; this.includes = new IncludesImpl(this); this.cache = new JdbcNodeCache(); // Put this node into the cache this.cache.put(this); // Set up the selections this.selections = selections; doSetSelectionMode(PARTIALLY_SELECTED); // requires this.selections } /** * @see org.teiid.designer.jdbc.metadata.impl.JdbcNodeImpl#accept(org.teiid.designer.jdbc.metadata.JdbcNodeVisitor, * int) * @since 4.3 */ @Override public void accept( JdbcNodeVisitor theVisitor, int theDepth ) throws JdbcException { if (theVisitor instanceof SelectSchemasAndCatalogs) { // need to search siblings before searching their children so that we find matches at the highest level first if (theDepth != DEPTH_ONE) { super.accept(theVisitor, DEPTH_ONE); } // search children's children now if no match found yet if (!((SelectSchemasAndCatalogs)theVisitor).foundMatch()) { super.accept(theVisitor, theDepth); } } else { super.accept(theVisitor, theDepth); } } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.impl.InternalJdbcDatabase#getJdbcNodeSelections() */ @Override public JdbcNodeSelections getJdbcNodeSelections() { return this.selections; } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.JdbcNode#getPathInSource() */ @Override public IPath getPathInSource() { return null; } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.JdbcNode#getPathInSource(boolean, boolean) */ @Override public IPath getPathInSource( final boolean includeCatalog, final boolean includeSchema ) { return null; } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.JdbcNode#isDatabaseObject() */ @Override public boolean isDatabaseObject() { return false; } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.JdbcNode#getParentDatabaseObject(boolean, boolean) */ @Override public JdbcNode getParentDatabaseObject( boolean includeCatalog, boolean includeSchema ) { return null; // there is never a parent of a database } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.JdbcNode#getJdbcDatabase() */ @Override public JdbcDatabase getJdbcDatabase() { return this; } @Override public JdbcNodeCache getJdbcNodeCache() { return this.cache; } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.JdbcDatabase#findJdbcNode(org.eclipse.core.runtime.IPath) */ @Override public JdbcNode findJdbcNode( IPath path ) { return this.cache.get(path); } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.JdbcDatabase#findJdbcNode(java.lang.String) */ @Override public JdbcNode findJdbcNode( String path ) { return findJdbcNode(new Path(path)); } @Override public JdbcNode[] getSelectedChildren() throws JdbcException { List selectedNodes = new ArrayList(); final JdbcNode[] nodes = this.getChildren(); for (int ndx = 0; ndx < nodes.length; ++ndx) { final JdbcNode node = nodes[ndx]; if (node.getSelectionMode() != JdbcNode.UNSELECTED) { selectedNodes.add(node); } } // now that we've got the list if (selectedNodes.size() > 0) { JdbcNode[] nodeArray = new JdbcNode[selectedNodes.size()]; int iNode = 0; for (Iterator iter = selectedNodes.iterator(); iter.hasNext();) { nodeArray[iNode] = (JdbcNode)iter.next(); iNode++; } return nodeArray; } return new JdbcNode[0]; } @Override public Connection getConnection() { return this.connection; } @Override public DatabaseMetaData getDatabaseMetaData() throws JdbcException { if (this.metadata == null) { try { this.metadata = this.connection.getMetaData(); } catch (SQLException e) { throw new JdbcException( e, JdbcPlugin.Util.getString("JdbcDatabaseImpl.Error_while_getting_the_database_metadata_component")); //$NON-NLS-1$ } } return this.metadata; } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.JdbcDatabase#getCapabilities() */ @Override public Capabilities getCapabilities() throws JdbcException { if (capabilities == null) { synchronized (capabilitiesLock) { if (capabilities == null) { capabilities = loadCapabilities(this.getDatabaseMetaData()); } } } return capabilities; } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.JdbcDatabase#getDatabaseInfo() */ @Override public DatabaseInfo getDatabaseInfo() throws JdbcException { if (databaseInfo == null) { synchronized (databaseInfoLock) { if (databaseInfo == null) { databaseInfo = loadDatabaseInfo(this.getDatabaseMetaData()); } } } return databaseInfo; } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.impl.JdbcNodeImpl#getTypeName() */ @Override public String getTypeName() { return JdbcPlugin.Util.getString("JdbcDatabaseImpl.DatabaseTypeName"); //$NON-NLS-1$ } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.impl.JdbcNodeImpl#computeChildren() */ @Override protected JdbcNode[] computeChildren() throws JdbcException { final Capabilities capabilities = getCapabilities(); // See if the driver/database supports catalogs ... boolean supportsCatalogs = false; try { // supportsCatalogs = md.supportsCatalogsInTableDefinitions(); supportsCatalogs = capabilities.supportsCatalogs(); } catch (Throwable t) { final Object[] params = new Object[] {this.getConnection()}; final String msg = JdbcPlugin.Util.getString("JdbcDatabaseImpl.Unexpected_exception_while_discovering_support_for_catalogs", params); //$NON-NLS-1$ JdbcPlugin.Util.log(IStatus.WARNING, t, msg); } // See if the driver/database supports schemas ... boolean supportsSchemas = false; try { // supportsSchemas = md.supportsSchemasInTableDefinitions(); supportsSchemas = capabilities.supportsSchemas(); } catch (Throwable t) { final Object[] params = new Object[] {this.getConnection()}; final String msg = JdbcPlugin.Util.getString("JdbcDatabaseImpl.Unexpected_exception_while_discovering_support_for_schemas", params); //$NON-NLS-1$ JdbcPlugin.Util.log(IStatus.WARNING, t, msg); } // Create the children ... final DatabaseMetaData metadata = getDatabaseMetaData(); final List children = new ArrayList(); if (supportsCatalogs) { ResultSet resultSet = null; try { resultSet = metadata.getCatalogs(); while (resultSet.next()) { final String catalogName = resultSet.getString(1); if (catalogName!=null && catalogName.length() > 0) { children.add(new JdbcCatalogImpl(this, catalogName)); } } if (children.isEmpty()) { supportsCatalogs = false; } } catch (UnsupportedOperationException e) { supportsCatalogs = false; } catch (Throwable t) { final Object[] params = new Object[] {metadata.getClass().getName(), this.getConnection()}; final String msg = JdbcPlugin.Util.getString("JdbcDatabaseImpl.Unexpected_exception_while_calling_getCatalogs()_and_processing_results", params); //$NON-NLS-1$ JdbcPlugin.Util.log(IStatus.WARNING, t, msg); supportsCatalogs = false; } finally { if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { JdbcPlugin.Util.log(e); } } } } // If DatabaseMetadata supports schemas but not catalogs ... if (supportsSchemas && !supportsCatalogs) { ResultSet resultSet = null; try { resultSet = metadata.getSchemas(); while (resultSet.next()) { final String catalogName = resultSet.getString(1); children.add(new JdbcSchemaImpl(this, catalogName)); } } catch (UnsupportedOperationException e) { supportsSchemas = false; } catch (Throwable t) { final Object[] params = new Object[] {metadata.getClass().getName(), this.getConnection()}; final String msg = JdbcPlugin.Util.getString("JdbcDatabaseImpl.Unexpected_exception_while_calling_getSchemas()_and_processing_results", params); //$NON-NLS-1$ JdbcPlugin.Util.log(IStatus.WARNING, t, msg); supportsSchemas = false; } finally { if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { JdbcPlugin.Util.log(e); } } } } // If DatabaseMetadata does not support schemas or catalogs then load the table metadata if (!supportsSchemas && !supportsCatalogs) { // Get the table types ... try { String[] tableTypes = getJdbcDatabase().getIncludes().getIncludedTableTypes(); if (tableTypes == null) { tableTypes = getJdbcDatabase().getCapabilities().getTableTypes(); } for (int i = 0; i < tableTypes.length; ++i) { children.add(new JdbcTableTypeImpl(this, tableTypes[i])); } } catch (Throwable t) { final Object[] params = new Object[] {metadata.getClass().getName(), this.getConnection()}; final String msg = JdbcPlugin.Util.getString("JdbcDatabaseImpl.Unexpected_exception_while_calling_getTableTypes()_and_processing_results", params); //$NON-NLS-1$ JdbcPlugin.Util.log(IStatus.WARNING, t, msg); } // Add the procedure type ... if (getIncludes().includeProcedures()) { String procTerm = null; try { procTerm = getCapabilities().getProcedureTerm(); } catch (Throwable t) { procTerm = JdbcPlugin.Util.getString("JdbcDatabaseImpl.ProcedureTypeName"); //$NON-NLS-1$ } children.add(new JdbcProcedureTypeImpl(this, procTerm)); } } // Convert the list to an array and return ... return (JdbcNode[])children.toArray(new JdbcNode[children.size()]); } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.JdbcNode#getFullyQualifiedName() */ @Override public String getFullyQualifiedName() { return NOT_APPLICABLE; } /** * Method to load the values of a {@link DatabaseInfoImpl} instance given a {@link DatabaseMetaData} reference. * * @param obj * @param metadata * @return */ public Capabilities loadCapabilities( final DatabaseMetaData metadata ) { return new CapabilitiesImpl(metadata); } /** * Method to load the values of a {@link DatabaseInfoImpl} instance given a {@link DatabaseMetaData} reference. * * @param obj * @param metadata * @return */ public DatabaseInfo loadDatabaseInfo( final DatabaseMetaData metadata ) throws JdbcException { CoreArgCheck.isNotNull(metadata); final DatabaseInfoImpl obj = new DatabaseInfoImpl(); final List statuses = new ArrayList(); // Load the product name try { obj.setProductName(metadata.getDatabaseProductName()); } catch (SQLException e) { final IStatus status = new Status( IStatus.ERROR, JdbcPlugin.PLUGIN_ID, 0, JdbcPlugin.Util.getString("DatabaseInfoImpl.Unable_to_obtain_the_product_name", metadata), e); //$NON-NLS-1$ statuses.add(status); } // Load the product version try { obj.setProductVersion(metadata.getDatabaseProductVersion()); } catch (SQLException e) { final IStatus status = new Status( IStatus.ERROR, JdbcPlugin.PLUGIN_ID, 0, JdbcPlugin.Util.getString("DatabaseInfoImpl.Unable_to_obtain_the_product_version", metadata), e); //$NON-NLS-1$ statuses.add(status); } // Load the database major version obj.setDriverMajorVersion(metadata.getDriverMajorVersion()); obj.setDriverMinorVersion(metadata.getDriverMinorVersion()); // Load the driver name try { obj.setDriverName(metadata.getDriverName()); } catch (SQLException e) { final IStatus status = new Status( IStatus.ERROR, JdbcPlugin.PLUGIN_ID, 0, JdbcPlugin.Util.getString("DatabaseInfoImpl.Unable_to_obtain_the_driver_name", metadata), e); //$NON-NLS-1$ statuses.add(status); } // Load the driver version try { obj.setDriverVersion(metadata.getDriverVersion()); } catch (SQLException e) { final IStatus status = new Status( IStatus.ERROR, JdbcPlugin.PLUGIN_ID, 0, JdbcPlugin.Util.getString("DatabaseInfoImpl.Unable_to_obtain_the_driver_version", metadata), e); //$NON-NLS-1$ statuses.add(status); } // Load the database URL try { obj.setDatabaseURL(metadata.getURL()); } catch (SQLException e) { final IStatus status = new Status( IStatus.ERROR, JdbcPlugin.PLUGIN_ID, 0, JdbcPlugin.Util.getString("DatabaseInfoImpl.Unable_to_obtain_the_database_URL", metadata), e); //$NON-NLS-1$ statuses.add(status); } // Load the database read-only status try { obj.setReadOnly(metadata.isReadOnly()); } catch (SQLException e) { final IStatus status = new Status( IStatus.ERROR, JdbcPlugin.PLUGIN_ID, 0, JdbcPlugin.Util.getString("DatabaseInfoImpl.Unable_to_obtain_the_database_read-only_mode", metadata), e); //$NON-NLS-1$ statuses.add(status); } // Load the username try { obj.setUserName(metadata.getUserName()); } catch (SQLException e) { final IStatus status = new Status( IStatus.ERROR, JdbcPlugin.PLUGIN_ID, 0, JdbcPlugin.Util.getString("DatabaseInfoImpl.Unable_to_obtain_the_username", metadata), e); //$NON-NLS-1$ statuses.add(status); } // Assemble all of the individual errors into the MultiStatus if (statuses.size() != 0) { final MultiStatus mstatus = new MultiStatus( JdbcPlugin.PLUGIN_ID, 0, JdbcPlugin.Util.getString("DatabaseInfoImpl.Error_while_processing_database_information", metadata), null); //$NON-NLS-1$ final Iterator iter = statuses.iterator(); while (iter.hasNext()) { final IStatus status = (IStatus)iter.next(); mstatus.add(status); } throw new JdbcException(mstatus); } // Nothing went wront, so return null return obj; } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.JdbcNode#refresh() */ @Override public void refresh() { super.refresh(); if (capabilities != null) { synchronized (capabilitiesLock) { capabilities = null; } } if (databaseInfo != null) { synchronized (databaseInfoLock) { databaseInfo = null; } } } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { final StringBuffer sb = new StringBuffer(); sb.append(getName()); sb.append(" - "); //$NON-NLS-1$ try { final String url = this.connection.getMetaData().getURL(); sb.append(url); } catch (SQLException e) { // do nothing } return super.toString(); } /* (non-Javadoc) * @See org.teiid.designer.jdbc.metadata.JdbcDatabase#getIncludes() */ @Override public Includes getIncludes() { return this.includes; } /** * Find and select those nodes that should be selected by default. * * @param username the username or (<code>null</code> if the metadata user name should be used to select nodes * @return the status of the operation */ public IStatus selectDefaultNodes( String username ) { try { String driverName = this.getDatabaseInfo().getDriverName(); if( driverName != null && driverName.toUpperCase().contains("TEIID") ) { //$NON-NLS-1$ return Status.OK_STATUS; } boolean isExcelOrAccess = false; final boolean ignoreCase = true; String productName = this.getDatabaseInfo().getProductName(); if (productName != null && (productName.equalsIgnoreCase("excel") || productName.equalsIgnoreCase("access"))) { //$NON-NLS-1$ //$NON-NLS-2$ isExcelOrAccess = true; } // Determine the username String user = (username == null ? getDatabaseInfo().getUserName() : username); SelectSchemasAndCatalogs visitor = new SelectSchemasAndCatalogs(user, ignoreCase, isExcelOrAccess); this.accept(visitor, DEPTH_INFINITE); // try metadata username if no matches found if (!visitor.foundMatch() && (username == null)) { String temp = getDatabaseInfo().getUserName(); // only do second search if name is different if (!temp.equals(user)) { user = temp; visitor = new SelectSchemasAndCatalogs(user, ignoreCase, isExcelOrAccess); this.accept(visitor, DEPTH_INFINITE); } } // If there were no new selections ... if (!visitor.foundMatch()) { final Object[] params = new Object[] {username == null ? user : username}; final String msg = JdbcPlugin.Util.getString("JdbcDatabaseImpl.Unable_to_select_any_database_objects", params); //$NON-NLS-1$ return new Status(IStatus.ERROR, JdbcPlugin.PLUGIN_ID, NO_OBJS_SELECTED_CODE, msg, null); } // Else we were able to find at least some selections ... final Object[] params = new Object[] {new Integer(1)}; final String msg = JdbcPlugin.Util.getString("JdbcDatabaseImpl.Selected_database_objects", params); //$NON-NLS-1$ return new Status(IStatus.OK, JdbcPlugin.PLUGIN_ID, 0, msg, null); } catch (JdbcException e) { final String msg = JdbcPlugin.Util.getString("JdbcDatabaseImpl.Error_while_selecting_default_database_objects"); //$NON-NLS-1$ return new Status(IStatus.ERROR, JdbcPlugin.PLUGIN_ID, 0, msg, e); } } /** * Find and select those nodes that should be selected by default. Uses the database metadata user name to select nodes. * * @return the status of the operation */ public IStatus selectDefaultNodes() { return selectDefaultNodes(null); } /** * The visitor that walks the JdbcNode tree, visiting only schemas and catalogs (never their children), and marks those that * match the supplied username. Excel is handled differently, since username is not required */ protected class SelectSchemasAndCatalogs implements JdbcNodeVisitor { private final String matchName; private final boolean ignoreCase; private final boolean isExcelOrAccess; private boolean match = false; public SelectSchemasAndCatalogs( final String matchName, final boolean ignoreCase, final boolean isExcelOrAccess ) { CoreArgCheck.isNotNull(matchName); this.matchName = matchName; this.ignoreCase = ignoreCase; this.isExcelOrAccess = isExcelOrAccess; } public boolean foundMatch() { return this.match; } @Override public boolean visit( final JdbcNode node ) { // return if we already found a match if (foundMatch()) { return false; } // If visiting the database, then we want to visit children (but nothing else to do) if (node instanceof JdbcDatabase) { return true; } // If visiting a schema or catalog don't visit children if match found if (node instanceof JdbcCatalog || node instanceof JdbcSchema || (this.isExcelOrAccess && (node instanceof JdbcTableType))) { // If excel then set match to true if (this.isExcelOrAccess) { this.match = true; // everything else, check name match } else { // Compare the name ... final String name = node.getName(); this.match = ignoreCase ? matchName.equalsIgnoreCase(name) : matchName.equals(name); } if (this.match) { // Select the node ... node.setSelected(true); return false; } // visit children return true; } // don't walk to the children of any other return false; } } }