/*
* Copyright (C) 2007 SQL Explorer Development Team
* http://sourceforge.net/projects/eclipsesql
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package net.sourceforge.sqlexplorer.dbproduct;
import java.sql.SQLException;
import net.sourceforge.sqlexplorer.Messages;
import net.sourceforge.sqlexplorer.connections.SessionEstablishedListener;
import net.sourceforge.sqlexplorer.dialogs.PasswordConnDlg;
import net.sourceforge.sqlexplorer.plugin.SQLExplorerPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchSite;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
/**
* Establishes a connection in the background and notifies a listener when complete; takes
* care of prompting the user for new username/password credentials if the login fails etc
* @author John Spackman
*/
public class ConnectionJob extends Job {
// The Alias to connect
private Alias alias;
// The User we're trying to log in
private User user;
// The callback to give the new session to
private SessionEstablishedListener listener;
// Whether to ask for the password on the first pass, instead of trying a saved one
private boolean requirePassword;
// True if the password dialog was cancelled
private boolean cancelled;
/**
* Constructor
* @param alias Alias to connect
* @param user User to establish a connection for (can be null, but if provided user.getAlias() must match alias)
* @param listener Callback listener
* @param requirePassword Whether to always prompt the user for a password, even if one is already known
*/
public ConnectionJob(Alias alias, User user, SessionEstablishedListener listener, boolean requirePassword) {
super(Messages.getString("Progress.Connection.Title") + " " + alias.getName() + '/' + ((user != null) ? user.getUserName() : "?"));
this.alias = alias;
this.user = user;
if (user != null && user.getAlias() != alias)
throw new RuntimeException("Invalid User - users alias must match the alias provided");
this.listener = listener;
this.requirePassword = user == null || requirePassword;
if (alias.hasNoUserName())
requirePassword = false;
}
/**
* Constructor; asks for a password on the first attempt only if the alias.isAutoLogin() is false
* @param alias Alias to connect
* @param user User to establish a connection for
* @param listener Callback listener
*/
public ConnectionJob(Alias alias, User user, SessionEstablishedListener listener) {
this(alias, user, listener, !alias.isAutoLogon());
}
/**
* Constructor; logs in a brand new user
* @param alias Alias to connect
* @param listener Callback listener
*/
public ConnectionJob(Alias alias, SessionEstablishedListener listener) {
this(alias, null, listener, true);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
boolean firstPass = true;
while (!monitor.isCanceled() && user != null && !cancelled) {
Session session = null;
Exception exception = null;
// Try to login
try {
if (!firstPass || !requirePassword)
session = user.createSession();
if (session != null) {
if (!user.hasAuthenticated()) {
SQLConnection connection = null;
try {
connection = session.grabConnection();
}catch(SQLException e) {
user.releaseSession(session);
session = null;
throw e;
} finally {
if (connection != null)
session.releaseConnection(connection);
}
}
if (listener != null)
listener.sessionEstablished(session);
return new Status(IStatus.OK, getClass().getName(), IStatus.OK, Messages.getString("Progress.Connection.Success"), null);
}
} catch(SQLException e) {
exception = e;
}
if (monitor.isCanceled())
break;
// Prompt for username and password
promptForPassword(exception != null ? exception.getMessage() : null);
// Always need to prompt for the password after the first pass
firstPass = false;
}
// Can't do it
if (listener != null)
listener.cannotEstablishSession(user);
return new Status(IStatus.CANCEL, getClass().getName(), IStatus.CANCEL, Messages.getString("Progress.Connection.Cancelled"), null);
}
/**
* Prompts the user for a new username/password to attempt login with; if the dialog is
* cancelled then this.user is set to null.
* @param message
*/
private void promptForPassword(final String message) {
final Shell shell = SQLExplorerPlugin.getDefault().getSite().getShell();
// Switch to the UI thread to run the password dialog, but run it synchronously so we
// wait for it to complete
shell.getDisplay().syncExec(new Runnable() {
public void run() {
if (message != null) {
String title = Messages.getString("Progress.Connection.Title") + ' ' + alias.getName();
if (user != null && !alias.hasNoUserName())
title += '/' + user.getUserName();
if (alias.hasNoUserName()) {
MessageDialog dlg = new MessageDialog(shell, title, null,
Messages.getString("Progress.Connection.ErrorMessage_Part1") + "\n\n" +
message,
MessageDialog.ERROR, new String[] { IDialogConstants.OK_LABEL }, 0);
dlg.open();
cancelled = true;
return;
} else {
MessageDialog dlg = new MessageDialog(shell, title, null,
Messages.getString("Progress.Connection.ErrorMessage_Part1") + "\n\n" +
message + "\n\n" +
Messages.getString("Progress.Connection.ErrorMessage_Part2"),
MessageDialog.ERROR, new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL }, 0);
boolean retry = dlg.open() == 0;
if (!retry) {
cancelled = true;
return;
}
}
}
Shell shell = Display.getCurrent().getActiveShell();
PasswordConnDlg dlg = new PasswordConnDlg(shell, user.getAlias(), user);
if (dlg.open() != Window.OK) {
cancelled = true;
return;
}
// Create a new user and add it to the alias
User userTmp = new User(dlg.getUserName(), dlg.getPassword());
userTmp.setAutoCommit(dlg.getAutoCommit());
userTmp.setCommitOnClose(dlg.getCommitOnClose());
user = alias.addUser(userTmp);
}
});
}
/**
* Establishes a connection in the background
* @param alias Alias to connect
* @param user User to establish a connection for
* @param listener Callback listener
*/
public static void createSession(Alias alias, User user, SessionEstablishedListener listener) {
createSession(alias, user, listener, false);
}
/**
* Establishes a connection in the background for a brand new set of user credentials
* @param alias Alias to connect
* @param user User to establish a connection for
* @param listener Callback listener
*/
public static void createSession(Alias alias, SessionEstablishedListener listener) {
createSession(alias, null, listener, false);
}
/**
* Establishes a connection in the background
* @param alias Alias to connect
* @param user User to establish a connection for
* @param listener Callback listener
* @param requirePassword Whether to always prompt the user for a password, even if one is already known
*/
public static void createSession(Alias alias, User user, SessionEstablishedListener listener, boolean requirePassword) {
final ConnectionJob bgJob = new ConnectionJob(alias, user, listener, requirePassword);
final IWorkbenchSite site = SQLExplorerPlugin.getDefault().getSite();
site.getShell().getDisplay().syncExec(new Runnable() {
public void run() {
IWorkbenchSiteProgressService siteps = (IWorkbenchSiteProgressService) site.getAdapter(IWorkbenchSiteProgressService.class);
siteps.showInDialog(site.getShell(), bgJob);
bgJob.schedule();
}
});
}
}