| /******************************************************************************* |
| * Copyright (c) 2006, 2009 Oracle. All rights reserved. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v1.0, which accompanies this distribution |
| * and is available at http://www.eclipse.org/legal/epl-v10.html. |
| * |
| * Contributors: |
| * Oracle - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.jpt.db.internal; |
| |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.datatools.connectivity.ConnectEvent; |
| import org.eclipse.datatools.connectivity.IConnectionProfile; |
| import org.eclipse.datatools.connectivity.IManagedConnection; |
| import org.eclipse.datatools.connectivity.IManagedConnectionOfflineListener; |
| import org.eclipse.datatools.connectivity.drivers.DriverManager; |
| import org.eclipse.datatools.connectivity.drivers.jdbc.IJDBCDriverDefinitionConstants; |
| import org.eclipse.datatools.connectivity.sqm.core.connection.ConnectionInfo; |
| import org.eclipse.datatools.sqltools.core.DatabaseIdentifier; |
| import org.eclipse.datatools.sqltools.core.profile.ProfileUtil; |
| import org.eclipse.jpt.db.ConnectionListener; |
| import org.eclipse.jpt.db.ConnectionProfile; |
| import org.eclipse.jpt.db.DatabaseIdentifierAdapter; |
| import org.eclipse.jpt.utility.internal.ListenerList; |
| import org.eclipse.jpt.utility.internal.StringTools; |
| |
| /** |
| * Wrap a DTP ConnectionProfile |
| */ |
| final class DTPConnectionProfileWrapper |
| implements DTPDatabaseObject, ConnectionProfile |
| { |
| /** the wrapped DTP connection profile */ |
| private final IConnectionProfile dtpConnectionProfile; |
| |
| /** adapter supplied by the client (determines identifier delimiters, etc.) */ |
| private final DatabaseIdentifierAdapter identifierAdapter; |
| |
| /** callback passed to the identifier adapter */ |
| private final DatabaseIdentifierAdapter.DefaultCallback identifierAdapterCallback; |
| |
| /** the DTP managed connection we listen to */ |
| private final IManagedConnection dtpManagedConnection; |
| |
| /** |
| * forward events from the DTP managed connection above; |
| * we listen and propagate events iff we have listeners ourselves |
| */ |
| private final LocalConnectionListener connectionListener; |
| |
| /** lazy-initialized, and deleted at disconnect */ |
| private DTPDatabaseWrapper database; |
| |
| |
| // ********** constants ********** |
| |
| private static final String LIVE_DTP_CONNECTION_TYPE = "java.sql.Connection"; //$NON-NLS-1$ |
| |
| private static final String OFFLINE_DTP_CONNECTION_TYPE = ConnectionInfo.class.getName(); |
| |
| private static final String DATABASE_PRODUCT_PROP_ID = "org.eclipse.datatools.connectivity.server.version"; //$NON-NLS-1$ |
| |
| |
| // ********** constructor ********** |
| |
| DTPConnectionProfileWrapper(IConnectionProfile dtpConnectionProfile, DatabaseIdentifierAdapter adapter) { |
| super(); |
| this.dtpConnectionProfile = dtpConnectionProfile; |
| this.identifierAdapter = adapter; |
| this.identifierAdapterCallback = new IdentifierAdapterCallback(); |
| this.dtpManagedConnection = this.buildDTPManagedConnection(); |
| this.connectionListener = new LocalConnectionListener(); |
| // don't listen to the managed connection yet |
| } |
| |
| private IManagedConnection buildDTPManagedConnection() { |
| String connectionType = this.dtpConnectionProfile.supportsWorkOfflineMode() ? |
| OFFLINE_DTP_CONNECTION_TYPE : LIVE_DTP_CONNECTION_TYPE; |
| return this.dtpConnectionProfile.getManagedConnection(connectionType); |
| } |
| |
| |
| // ********** DatabaseObject implementation ********** |
| |
| public String getName() { |
| return this.dtpConnectionProfile.getName(); |
| } |
| |
| public String getIdentifier(String javaIdentifier) { |
| // connection profiles do not have "identifiers" |
| throw new UnsupportedOperationException(); |
| } |
| |
| public String getIdentifier() { |
| // connection profiles do not have "identifiers" |
| throw new UnsupportedOperationException(); |
| } |
| |
| |
| // ********** DTPDatabaseObject implementation ********** |
| |
| public DTPConnectionProfileWrapper getConnectionProfile() { |
| return this; |
| } |
| |
| public synchronized DTPDatabaseWrapper getDatabase() { |
| if (this.database == null) { |
| this.database = this.buildDatabase(); |
| } |
| return this.database; |
| } |
| |
| |
| // ********** ConnectionProfile implementation ********** |
| |
| // ***** properties |
| public String getProviderID() { |
| return this.dtpConnectionProfile.getProviderId(); |
| } |
| |
| public String getInstanceID() { |
| return this.dtpConnectionProfile.getInstanceID(); |
| } |
| |
| public String getDatabaseName() { |
| return this.getProperty(IJDBCDriverDefinitionConstants.DATABASE_NAME_PROP_ID); |
| } |
| |
| public String getDatabaseProduct() { |
| return this.getProperty(DATABASE_PRODUCT_PROP_ID); |
| } |
| |
| public String getDatabaseVendor() { |
| return this.getProperty(IJDBCDriverDefinitionConstants.DATABASE_VENDOR_PROP_ID); |
| } |
| |
| public String getDatabaseVersion() { |
| return this.getProperty(IJDBCDriverDefinitionConstants.DATABASE_VERSION_PROP_ID); |
| } |
| |
| public String getDriverClassName() { |
| return this.getProperty(IJDBCDriverDefinitionConstants.DRIVER_CLASS_PROP_ID); |
| } |
| |
| public String getURL() { |
| return this.getProperty(IJDBCDriverDefinitionConstants.URL_PROP_ID); |
| } |
| |
| /** |
| * Returns the user name. |
| * Allows user name composed by more than one word. |
| * If the user name contains a keyword, it returns the first word only. |
| */ |
| public String getUserName() { |
| String userName = this.getProperty(IJDBCDriverDefinitionConstants.USERNAME_PROP_ID); |
| userName = userName.trim(); |
| String[] names = userName.split("\\s+"); //$NON-NLS-1$ |
| if(names.length == 3) { // 208946 handle username like "sys as sysdba" on Oracle |
| if(this.nameIsKeyword(names[1])) { |
| return names[0]; |
| } |
| } |
| return userName; |
| } |
| |
| public String getUserPassword() { |
| return this.getProperty(IJDBCDriverDefinitionConstants.PASSWORD_PROP_ID); |
| } |
| |
| public String getDriverDefinitionID() { |
| return this.getProperty(DRIVER_DEFINITION_PROP_ID); |
| } |
| |
| public String getDriverJarList() { |
| return DriverManager.getInstance().getDriverInstanceByID(this.getDriverDefinitionID()).getJarList(); |
| } |
| |
| public String getDriverName() { |
| return DriverManager.getInstance().getDriverInstanceByID(this.getDriverDefinitionID()).getName(); |
| } |
| |
| // ***** connection |
| public boolean isActive() { |
| return this.isConnected() || this.isWorkingOffline(); |
| } |
| |
| public boolean isInactive() { |
| return ! this.isActive(); |
| } |
| |
| public boolean isConnected() { |
| return this.dtpManagedConnection.isConnected() |
| && ! this.dtpManagedConnection.isWorkingOffline(); |
| } |
| |
| public boolean isDisconnected() { |
| return ! this.isConnected(); |
| } |
| |
| public void connect() { |
| if (this.isDisconnected()) { |
| this.checkStatus(this.dtpConnectionProfile.connect()); |
| } |
| } |
| |
| public void disconnect() { |
| this.checkStatus(this.dtpConnectionProfile.disconnect()); |
| } |
| |
| // ***** off-line support |
| public boolean isWorkingOffline() { |
| return this.dtpManagedConnection.isWorkingOffline(); |
| } |
| |
| public boolean supportsWorkOfflineMode() { |
| return this.dtpConnectionProfile.supportsWorkOfflineMode(); |
| } |
| |
| public IStatus saveWorkOfflineData() { |
| return this.dtpConnectionProfile.saveWorkOfflineData(); |
| } |
| |
| public boolean canWorkOffline() { |
| return this.dtpConnectionProfile.canWorkOffline(); |
| } |
| |
| public IStatus workOffline() { |
| return this.dtpConnectionProfile.workOffline(); |
| } |
| |
| // ***** listeners |
| public synchronized void addConnectionListener(ConnectionListener listener) { |
| if (this.hasNoListeners()) { // first listener added |
| this.startListening(); |
| } |
| this.connectionListener.addConnectionListener(listener); |
| } |
| |
| private void startListening() { |
| this.dtpManagedConnection.addConnectionListener(this.connectionListener); |
| if (this.database != null) { // don't trigger database creation |
| if (this.isConnected()) { // DTP does not change when off-line |
| this.database.startListening(); |
| } |
| } |
| } |
| |
| public synchronized void removeConnectionListener(ConnectionListener listener) { |
| this.connectionListener.removeConnectionListener(listener); |
| if (this.hasNoListeners()) { // last listener removed |
| this.stopListening(); |
| } |
| } |
| |
| private void stopListening() { |
| if (this.database != null) { // don't trigger database creation |
| if (this.isConnected()) { // DTP does not change when off-line |
| this.database.stopListening(); |
| } |
| } |
| this.dtpManagedConnection.removeConnectionListener(this.connectionListener); |
| } |
| |
| boolean hasNoListeners() { |
| return this.connectionListener.hasNoListeners(); |
| } |
| |
| boolean hasAnyListeners() { |
| return this.connectionListener.hasAnyListeners(); |
| } |
| |
| |
| // ********** internal methods ********** |
| |
| private void checkStatus(IStatus status) { |
| if (status.isOK()) { |
| return; |
| } |
| if (status.isMultiStatus()) { |
| for (IStatus child : status.getChildren()) { |
| this.checkStatus(child); // recurse, looking for the first error |
| } |
| } |
| throw new RuntimeException(status.getMessage(), status.getException()); |
| } |
| |
| private DTPDatabaseWrapper buildDatabase() { |
| if (this.isInactive()) { |
| return null; |
| } |
| |
| if (this.isWorkingOffline()) { |
| ConnectionInfo connectionInfo = (ConnectionInfo) this.dtpManagedConnection.getConnection().getRawConnection(); |
| return new DTPDatabaseWrapper(this, connectionInfo.getSharedDatabase()); |
| } |
| |
| // TODO see DTP bug 202306 |
| // pass connect=true in to ProfileUtil.getDatabase() |
| // there is a bug mentioned in a comment: |
| // "during the profile connected event notification, |
| // IManagedConnection is connected while IConnectionProfile is not" |
| // so, some hackery here to handle hackery there |
| return new DTPDatabaseWrapper(this, ProfileUtil.getDatabase(new DatabaseIdentifier(this.getName(), this.getDatabaseName()), true)); |
| } |
| |
| synchronized void clearDatabase() { |
| if (this.database != null) { |
| if (this.isConnected()) { // DTP does not change when off-line |
| this.database.stopListening(); |
| } |
| this.database = null; |
| } |
| } |
| |
| /** |
| * This is called whenever we need to convert an identifier to a name |
| * (e.g. {@link org.eclipse.jpt.db.Table#getColumnForIdentifier(String)}). |
| * We channel all the calls to here and then we delegate to the |
| * client-supplied "database identifier adapter". |
| */ |
| String convertIdentifierToName(String identifier) { |
| return this.identifierAdapter.convertIdentifierToName(identifier, this.identifierAdapterCallback); |
| } |
| |
| /** |
| * The default "database identifier adapter" calls back to here so we can delegate to |
| * the database, which contains all the information necessary to properly |
| * convert identifiers. |
| */ |
| String convertIdentifierToName_(String identifier) { |
| // the database should not be null here - call its internal method |
| return this.database.convertIdentifierToName_(identifier); |
| } |
| |
| /** |
| * This is called whenever we need to convert a name to an identifier |
| * (e.g. {@link org.eclipse.jpt.db.Table#getColumnForIdentifier(String)}). |
| * We channel all the calls to here and then we delegate to the |
| * client-supplied "database identifier adapter". |
| */ |
| String convertNameToIdentifier(String name) { |
| return this.identifierAdapter.convertNameToIdentifier(name, this.identifierAdapterCallback); |
| } |
| |
| /** |
| * The default "database identifier adapter" calls back to here so we can delegate to |
| * the database, which contains all the information necessary to properly |
| * convert names. |
| */ |
| String convertNameToIdentifier_(String name) { |
| // the database should not be null here - call its internal method |
| return this.database.convertNameToIdentifier_(name); |
| } |
| |
| void databaseChanged(DTPDatabaseWrapper db) { |
| this.connectionListener.databaseChanged(db); |
| } |
| |
| void catalogChanged(DTPCatalogWrapper catalog) { |
| this.connectionListener.catalogChanged(catalog); |
| } |
| |
| void schemaChanged(DTPSchemaWrapper schema) { |
| this.connectionListener.schemaChanged(schema); |
| } |
| |
| void sequenceChanged(DTPSequenceWrapper sequence) { |
| this.connectionListener.sequenceChanged(sequence); |
| } |
| |
| void tableChanged(DTPTableWrapper table) { |
| this.connectionListener.tableChanged(table); |
| } |
| |
| void columnChanged(DTPColumnWrapper column) { |
| this.connectionListener.columnChanged(column); |
| } |
| |
| void foreignKeyChanged(DTPForeignKeyWrapper foreignKey) { |
| this.connectionListener.foreignKeyChanged(foreignKey); |
| } |
| |
| private String getProperty(String propertyName) { |
| return this.dtpConnectionProfile.getBaseProperties().getProperty(propertyName); |
| } |
| |
| private boolean nameIsKeyword(String name) { |
| return name.equalsIgnoreCase("as"); //$NON-NLS-1$ |
| } |
| |
| |
| // ********** overrides ********** |
| |
| @Override |
| public String toString() { |
| return StringTools.buildToStringFor(this, this.getName()); |
| } |
| |
| |
| // ********** DTP connection listener ********** |
| |
| /** |
| * This listener translates and forwards {@link org.eclipse.datatools.connectivity.IManagedConnectionListener} and |
| * {@link IManagedConnectionOfflineListener} events to {@link ConnectionListener}s. |
| */ |
| class LocalConnectionListener implements IManagedConnectionOfflineListener { |
| private ListenerList<ConnectionListener> listenerList = new ListenerList<ConnectionListener>(ConnectionListener.class); |
| |
| LocalConnectionListener() { |
| super(); |
| } |
| |
| void addConnectionListener(ConnectionListener listener) { |
| this.listenerList.add(listener); |
| } |
| |
| void removeConnectionListener(ConnectionListener listener) { |
| this.listenerList.remove(listener); |
| } |
| |
| boolean hasNoListeners() { |
| return this.listenerList.isEmpty(); |
| } |
| |
| boolean hasAnyListeners() { |
| return ! this.listenerList.isEmpty(); |
| } |
| |
| |
| // ********** IManagedConnectionListener implementation ********** |
| |
| // off-line or inactive => live |
| public void opened(ConnectEvent event) { |
| // do not build the database here - it is built on-demand |
| // forward event to listeners |
| for (ConnectionListener listener : this.listenerList.getListeners()) { |
| listener.opened(DTPConnectionProfileWrapper.this); |
| } |
| } |
| |
| /** |
| * This method is never called from the base DTP code. |
| * Perhaps DTP extenders call it.... |
| * @see ManagedConnection#fireModifiedEvent(Object) |
| * which is never called... |
| */ |
| public void modified(ConnectEvent event) { |
| // forward event to listeners |
| for (ConnectionListener listener : this.listenerList.getListeners()) { |
| listener.modified(DTPConnectionProfileWrapper.this); |
| } |
| } |
| |
| public boolean okToClose(ConnectEvent event) { |
| // forward event to listeners |
| for (ConnectionListener listener : this.listenerList.getListeners()) { |
| if ( ! listener.okToClose(DTPConnectionProfileWrapper.this)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public void aboutToClose(ConnectEvent event) { |
| // forward event to listeners |
| for (ConnectionListener listener : this.listenerList.getListeners()) { |
| listener.aboutToClose(DTPConnectionProfileWrapper.this); |
| } |
| } |
| |
| // live or off-line => inactive |
| public void closed(ConnectEvent event) { |
| // clear the database |
| DTPConnectionProfileWrapper.this.clearDatabase(); |
| // forward event to listeners |
| for (ConnectionListener listener : this.listenerList.getListeners()) { |
| listener.closed(DTPConnectionProfileWrapper.this); |
| } |
| } |
| |
| |
| // ********** IManagedConnectionOfflineListener implementation ********** |
| |
| // live => off-line |
| public boolean okToDetach(ConnectEvent event) { |
| // convert the event to an "ok to close" event; |
| // we are "closing" the live connection |
| return this.okToClose(event); |
| } |
| |
| // live => off-line |
| public void aboutToDetach(ConnectEvent event) { |
| // convert the event to a "close" event; |
| // we are "closing" the live connection |
| this.closed(event); |
| } |
| |
| // inactive or live => off-line |
| public void workingOffline(ConnectEvent event) { |
| // convert the event to an "open" event; |
| // we are "opening" the off-line connection |
| this.opened(event); |
| } |
| |
| // off-line => live |
| public void aboutToAttach(ConnectEvent event) { |
| // convert the event to an "close" event; |
| // we are "closing" the off-line connection |
| this.closed(event); |
| } |
| |
| |
| // ********** internal methods ********** |
| |
| void databaseChanged(DTPDatabaseWrapper db) { |
| for (ConnectionListener listener : this.listenerList.getListeners()) { |
| listener.databaseChanged(DTPConnectionProfileWrapper.this, db); |
| } |
| } |
| |
| void catalogChanged(DTPCatalogWrapper catalog) { |
| for (ConnectionListener listener : this.listenerList.getListeners()) { |
| listener.catalogChanged(DTPConnectionProfileWrapper.this, catalog); |
| } |
| } |
| |
| void schemaChanged(DTPSchemaWrapper schema) { |
| for (ConnectionListener listener : this.listenerList.getListeners()) { |
| listener.schemaChanged(DTPConnectionProfileWrapper.this, schema); |
| } |
| } |
| |
| void sequenceChanged(DTPSequenceWrapper sequence) { |
| for (ConnectionListener listener : this.listenerList.getListeners()) { |
| listener.sequenceChanged(DTPConnectionProfileWrapper.this, sequence); |
| } |
| } |
| |
| void tableChanged(DTPTableWrapper table) { |
| for (ConnectionListener listener : this.listenerList.getListeners()) { |
| listener.tableChanged(DTPConnectionProfileWrapper.this, table); |
| } |
| } |
| |
| void columnChanged(DTPColumnWrapper column) { |
| for (ConnectionListener listener : this.listenerList.getListeners()) { |
| listener.columnChanged(DTPConnectionProfileWrapper.this, column); |
| } |
| } |
| |
| void foreignKeyChanged(DTPForeignKeyWrapper foreignKey) { |
| for (ConnectionListener listener : this.listenerList.getListeners()) { |
| listener.foreignKeyChanged(DTPConnectionProfileWrapper.this, foreignKey); |
| } |
| } |
| |
| } |
| |
| |
| // ********** default DatabaseFinder ********** |
| |
| class IdentifierAdapterCallback implements DatabaseIdentifierAdapter.DefaultCallback { |
| public String convertIdentifierToName(String identifier) { |
| // call back to the internal method |
| return DTPConnectionProfileWrapper.this.convertIdentifierToName_(identifier); |
| } |
| public String convertNameToIdentifier(String name) { |
| // call back to the internal method |
| return DTPConnectionProfileWrapper.this.convertNameToIdentifier_(name); |
| } |
| } |
| |
| } |