blob: 470f50db3896a4be7a2500b424c34833460cfe6a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2015 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.jpa.ui.internal.wizards.gen;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.EventListener;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jface.wizard.IWizardContainer;
import org.eclipse.jpt.common.ui.JptCommonUiImages;
import org.eclipse.jpt.common.utility.internal.collection.CollectionTools;
import org.eclipse.jpt.common.utility.internal.iterable.EmptyIterable;
import org.eclipse.jpt.common.utility.internal.iterable.IterableTools;
import org.eclipse.jpt.common.utility.internal.iterator.IteratorTools;
import org.eclipse.jpt.common.utility.internal.reference.SynchronizedBoolean;
import org.eclipse.jpt.jpa.core.JpaProject;
import org.eclipse.jpt.jpa.db.ConnectionAdapter;
import org.eclipse.jpt.jpa.db.ConnectionListener;
import org.eclipse.jpt.jpa.db.ConnectionProfile;
import org.eclipse.jpt.jpa.db.ConnectionProfileFactory;
import org.eclipse.jpt.jpa.db.Schema;
import org.eclipse.jpt.jpa.db.SchemaContainer;
import org.eclipse.jpt.jpa.db.ui.internal.DTPUiTools;
import org.eclipse.jpt.jpa.ui.internal.plugin.JptJpaUiPlugin;
import org.eclipse.jpt.jpa.ui.wizards.gen.JptJpaUiWizardsEntityGenMessages;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
/**
* A composite used to connect to database, includes following UI controls:
* - connection combo-box
* - schema combo-box
* - add connection button
* - reconnect button
*/
public class DatabaseGroup
{
private final JpaProject jpaProject;
private final Set<Listener> listeners = Collections.synchronizedSet(new HashSet<Listener>());
// these are kept in sync with the selection
private ConnectionProfile selectedConnectionProfile;
private Schema selectedSchema;
private final Combo connectionComboBox;
private final Combo schemaComboBox;
private final Button reconnectButton;
private final ConnectionListener connectionListener;
private IWizardContainer wizardContainer;
protected final ResourceManager resourceManager;
// ********** construction **********
public DatabaseGroup(IWizardContainer wizardContainer, JpaProject jpaProject, Composite parent, ResourceManager resourceManager, int widthHint) {
super();
this.wizardContainer = wizardContainer;
this.jpaProject = jpaProject;
this.resourceManager = resourceManager;
// connection combo-box
this.buildLabel(parent, 1, JptJpaUiWizardsEntityGenMessages.CONNECTION);
this.connectionComboBox = this.buildConnectionComboBox(parent, widthHint);
// add connection button
Button addConnectionButton = this.buildButton(parent, JptJpaUiWizardsEntityGenMessages.ADD_CONNECTION_LINK, this.resourceManager.createImage(JptCommonUiImages.ADD_CONNECTION_BUTTON), this.buildAddConnectionLinkSelectionListener());
addConnectionButton.setLayoutData(new GridData());
// A composite holds the reconnect button & text
this.buildLabel(parent, 1, ""); //$NON-NLS-1$
Composite comp = new Composite( parent , SWT.NONE );
GridData gridData = new GridData();
gridData.grabExcessHorizontalSpace = true ;
gridData.horizontalSpan = 2;
comp.setLayoutData(gridData);
GridLayout gridLayout = new GridLayout(2, false);
// Make the reconnect button to be closer to the connection combo.
gridLayout.marginTop = -5;
comp.setLayout(gridLayout);
// reconnection button
this.reconnectButton = this.buildButton(comp, JptJpaUiWizardsEntityGenMessages.CONNECT_LINK, this.resourceManager.createImage(JptCommonUiImages.RECONNECT_BUTTON), this.buildReconnectLinkSelectionListener());
this.buildLabel(comp, 1, JptJpaUiWizardsEntityGenMessages.SCHEMA_INFO);
// schema combo-box
this.buildLabel(parent, 1, JptJpaUiWizardsEntityGenMessages.SCHEMA);
this.schemaComboBox = this.buildSchemaComboBox(parent);
// filler
new Label(parent, SWT.NULL);
this.connectionListener = this.buildConnectionListener();
}
public void init()
{
// initialize state, based on JPA project
this.selectedConnectionProfile = this.getJpaProjectConnectionProfile();
this.selectedSchema = this.getDefaultSchema();
if(this.selectedSchema != null) {
this.fireSchemaChanged(this.selectedSchema);
}
if(this.selectedConnectionProfile != null) {
this.selectedConnectionProfile.addConnectionListener(this.connectionListener);
this.fireConnectionProfileChanged(this.selectedConnectionProfile);
}
this.updateConnectionComboBox();
this.updateSchemaComboBox();
this.updateReconnectLink();
}
// ********** intra-wizard methods **********
public Schema getSelectedSchema() {
return this.selectedSchema;
}
public void dispose() {
if(this.selectedConnectionProfile != null) {
this.selectedConnectionProfile.removeConnectionListener(this.connectionListener);
}
}
public boolean connectionIsActive() {
return (this.selectedConnectionProfile != null) && (this.selectedConnectionProfile.isActive());
}
// ********** internal methods **********
/**
* this can return null;
* called at start-up and when the selected connection profile changes
*/
private ConnectionProfile getJpaProjectConnectionProfile() {
return this.jpaProject.getConnectionProfile();
}
/**
* this can return null;
* called at start-up and when the selected connection profile changes
*/
private Schema getDefaultSchema() {
Schema schema = null;
// If a schema is specified for the JPA project, set schema to the schema of the JPA project
if (this.getJpaProjectConnectionProfile() != null) {
// schema could be null when the default schema does not exist
schema = jpaProject.getDefaultDbSchema();
}
// selected connection profile could be null when no connection specified
if (schema == null && this.selectedConnectionProfile != null) {
// check if the selected connection profile is active
if (this.selectedConnectionProfile.isActive()) {
// if active, get the default schema of the selected connection profile
schema = this.selectedConnectionProfile.getDatabase().getDefaultSchema();
// if the default schema does not exist, get the first available schema
if (schema == null) {
Iterable<Schema> schemata = this.selectedConnectionProfile.getDatabase().getSchemata();
if (schemata.iterator().hasNext()) {
schema = schemata.iterator().next();
}
}
}
}
return schema;
}
/**
* the connection combo-box is updated at start-up and when the user
* adds a connection profile
*/
private void updateConnectionComboBox() {
this.connectionComboBox.removeAll();
for(String cpName : this.buildSortedConnectionProfileNames()) {
this.connectionComboBox.add(cpName);
}
if(this.selectedConnectionProfile != null) {
this.connectionComboBox.select(this.connectionComboBox.indexOf(this.selectedConnectionProfile.getName()));
}
}
private SortedSet<String> buildSortedConnectionProfileNames() {
return CollectionTools.treeSet(this.getConnectionProfileNames());
}
private Iterable<String> getConnectionProfileNames() {
ConnectionProfileFactory factory = this.getConnectionProfileFactory();
return (factory != null) ? factory.getConnectionProfileNames() : IterableTools.<String>emptyIterable();
}
/**
* called at start-up and when the selected connection profile changes
*/
private void updateReconnectLink() {
this.reconnectButton.setEnabled(this.reconnectLinkCanBeEnabled());
}
private boolean reconnectLinkCanBeEnabled() {
return (this.selectedConnectionProfile != null) && !(this.selectedConnectionProfile.isActive());
}
/**
* the schema combo-box is updated at start-up and
* when the selected connection profile changes
*/
private void updateSchemaComboBox() {
this.schemaComboBox.removeAll();
for(String name : this.getSchemaNames()) {
this.schemaComboBox.add(name);
}
// the current schema *should* be in the current connection profile
if(this.selectedSchema != null) {
this.schemaComboBox.select(this.schemaComboBox.indexOf(this.selectedSchema.getName()));
}
}
private Iterable<String> getSchemaNames() {
SchemaContainer sc = this.jpaProject.getDefaultDbSchemaContainer();
// use schema *names* since the combo-box is read-only
return (sc != null) ? sc.getSortedSchemaNames() : EmptyIterable.<String>instance();
}
/**
* If the specified name matches the name of the JPA project's
* connection profile, return it; otherwise, build a new connection
* profile.
*/
private ConnectionProfile checkJpaProjectConnectionProfile(String cpName) {
ConnectionProfile cp = this.getJpaProjectConnectionProfile();
if((cp != null) && cp.getName().equals(cpName)) {
return cp;
}
return this.buildConnectionProfile(cpName);
}
private ConnectionProfile buildConnectionProfile(String name) {
ConnectionProfileFactory factory = this.getConnectionProfileFactory();
return (factory == null) ? null : factory.buildConnectionProfile(name);
}
private ConnectionProfileFactory getConnectionProfileFactory() {
return this.jpaProject.getJpaProjectManager().getJpaWorkspace().getConnectionProfileFactory();
}
// ********** listener callbacks **********
private void selectedConnectionChanged() {
String text = this.connectionComboBox.getText();
if(text.length() == 0) {
if(this.selectedConnectionProfile == null) {
return; // no change
}
this.selectedConnectionProfile.removeConnectionListener(this.connectionListener);
this.selectedConnectionProfile = null;
}
else {
if(this.selectedConnectionProfile == null) {
this.selectedConnectionProfile = this.checkJpaProjectConnectionProfile(text);
}
else {
if(text.equals(this.selectedConnectionProfile.getName())) {
return; // no change
}
this.selectedConnectionProfile.removeConnectionListener(this.connectionListener);
this.selectedConnectionProfile = this.checkJpaProjectConnectionProfile(text);
}
this.selectedConnectionProfile.addConnectionListener(this.connectionListener);
}
this.fireConnectionProfileChanged(this.selectedConnectionProfile);
this.connectionChanged();
}
private void selectedSchemaChanged() {
Schema old = this.selectedSchema;
this.selectedSchema = this.jpaProject.getDefaultDbSchemaContainer().getSchemaNamed(this.schemaComboBox.getText());
if(this.selectedSchema != old) {
fireSchemaChanged(this.selectedSchema);
}
}
/**
* Open the DTP New Connection Profile wizard.
* If the user creates a new connection profile, start using it and
* connect it
*/
private void addConnection() {
String addedProfileName = DTPUiTools.createNewConnectionProfile();
if(addedProfileName == null) {
return; // user pressed "Cancel"
}
if(this.selectedConnectionProfile != null) {
this.selectedConnectionProfile.removeConnectionListener(this.connectionListener);
}
this.selectedConnectionProfile = this.buildConnectionProfile(addedProfileName);
this.selectedConnectionProfile.addConnectionListener(this.connectionListener);
this.updateConnectionComboBox();
this.selectedConnectionProfile.connect();
// everything else should be synchronized when we get the resulting open event
this.fireConnectionProfileChanged(this.selectedConnectionProfile);
this.updateSchemaComboBox();
}
private void reconnect() {
try {
this.wizardContainer.run(true, true, new IRunnableWithProgress() {
public void run(final IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException
{
monitor.beginTask(JptJpaUiWizardsEntityGenMessages.CONNECTING_TO_DATABASE, 10);
final SynchronizedBoolean finished = new SynchronizedBoolean(false);
Thread t = new Thread() {
@Override
public void run() {
try {
DatabaseGroup.this.selectedConnectionProfile.connect();
}
catch (Exception ex) {
JptJpaUiPlugin.instance().logError(ex);
}
finally {
finished.setTrue();
}
}
};
t.start();
// Updating the monitor caused TablesSelectorWizardPage buttons to be disable
// while(finished.isFalse()) {
// Thread.sleep(1000);
// monitor.worked(1);
// }
// everything should be synchronized when we get the resulting open event
monitor.done();
}
});
}
catch (Exception e) {
JptJpaUiPlugin.instance().logError(e);
}
this.wizardContainer.updateButtons();
}
/**
* called when
* - the user selects a new connection
* - the connection was opened
* - the connection was closed (never happens?)
* we need to update the schema stuff and the reconnect link
*/
/* CU private */ void connectionChanged() {
Schema old = this.selectedSchema;
this.selectedSchema = this.getDefaultSchema();
if(this.selectedSchema != old) {
this.fireSchemaChanged(this.selectedSchema);
}
this.updateSchemaComboBox();
this.updateReconnectLink();
}
// ********** listeners **********
private SelectionListener buildConnectionComboBoxSelectionListener() {
return new SelectionListener() {
public void widgetDefaultSelected(SelectionEvent event) {
// nothing special for "default" (double-click?)
this.widgetSelected(event);
}
public void widgetSelected(SelectionEvent event) {
DatabaseGroup.this.selectedConnectionChanged();
}
@Override
public String toString() {
return "DatabaseConnectionWizardPage connection combo-box selection listener"; //$NON-NLS-1$
}
};
}
private SelectionListener buildSchemaComboBoxSelectionListener() {
return new SelectionListener() {
public void widgetDefaultSelected(SelectionEvent event) {
// nothing special for "default" (double-click?)
this.widgetSelected(event);
}
public void widgetSelected(SelectionEvent event) {
DatabaseGroup.this.selectedSchemaChanged();
}
@Override
public String toString() {
return "DatabaseConnectionWizardPage schema combo-box selection listener"; //$NON-NLS-1$
}
};
}
private SelectionListener buildAddConnectionLinkSelectionListener() {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
DatabaseGroup.this.addConnection();
}
@Override
public String toString() {
return "DatabaseConnectionWizardPage add connection link selection listener"; //$NON-NLS-1$
}
};
}
private SelectionListener buildReconnectLinkSelectionListener() {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
DatabaseGroup.this.reconnect();
}
@Override
public String toString() {
return "DatabaseConnectionWizardPage reconnect link selection listener"; //$NON-NLS-1$
}
};
}
private ConnectionListener buildConnectionListener() {
return new LocalConnectionListener();
}
class LocalConnectionListener
extends ConnectionAdapter
{
@Override
public void opened(ConnectionProfile cp) {
this.connectionChanged();
}
@Override // this probably won't ever get called...
public void closed(ConnectionProfile cp) {
this.connectionChanged();
}
private void connectionChanged() {
org.eclipse.jpt.common.ui.internal.swt.widgets.DisplayTools.asyncExec(
new Runnable() {
public void run() {
DatabaseGroup.this.connectionChanged();
}
}
);
}
}
// ********** listeners **********
public void addListener(Listener listener) {
if( ! this.listeners.add(listener)) {
throw new IllegalArgumentException("duplicate listener: " + listener); //$NON-NLS-1$
}
}
public void removeListener(Listener listener) {
if( ! this.listeners.remove(listener)) {
throw new IllegalArgumentException("missing listener: " + listener); //$NON-NLS-1$
}
}
private Iterator<Listener> listeners() {
return IteratorTools.clone(this.listeners);
}
private void fireConnectionProfileChanged(ConnectionProfile connectionProfile) {
for(Iterator<Listener> stream = this.listeners(); stream.hasNext(); ) {
stream.next().selectedConnectionProfileChanged(connectionProfile);
}
}
private void fireSchemaChanged(Schema schema) {
for(Iterator<Listener> stream = this.listeners(); stream.hasNext(); ) {
stream.next().selectedSchemaChanged(schema);
}
}
// ********** UI components **********
private Label buildLabel(Composite parent, int span, String text) {
Label label = new Label(parent, SWT.NONE);
label.setText(text);
GridData gridData = new GridData();
gridData.horizontalSpan = span;
label.setLayoutData(gridData);
return label;
}
private Combo buildConnectionComboBox(Composite parent, int widthHint) {
Combo combo = new Combo(parent, SWT.BORDER | SWT.READ_ONLY);
GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false);
//gridData.grabExcessHorizontalSpace = true ;
gridData.widthHint = widthHint;
combo.setLayoutData(gridData);
combo.addSelectionListener(this.buildConnectionComboBoxSelectionListener());
return combo;
}
private Combo buildSchemaComboBox(Composite parent) {
Combo combo = new Combo(parent, SWT.BORDER | SWT.READ_ONLY);
GridData gridData = new GridData(SWT.BEGINNING, SWT.CENTER, true, false);
gridData.horizontalAlignment = SWT.FILL;
gridData.horizontalSpan = 1;
gridData.grabExcessHorizontalSpace = true ;
combo.setLayoutData(gridData);
combo.addSelectionListener(this.buildSchemaComboBoxSelectionListener());
return combo;
}
private Button buildButton(Composite parent, String toolTipText, Image image, SelectionListener listener) {
Button button = new Button(parent, SWT.NONE);
GridData gridData = new GridData(GridData.END, GridData.CENTER, false, false);
gridData.horizontalSpan = 1;
button.setLayoutData(gridData);
button.setImage( image );
button.setToolTipText( toolTipText);
button.addSelectionListener(listener);
return button;
}
// ********** listener interface **********
/**
* Allows clients to listen for changes to the selected connection profile
* and schema.
*/
public interface Listener extends EventListener
{
void selectedConnectionProfileChanged(ConnectionProfile connectionProfile);
void selectedSchemaChanged(Schema schema);
}
}