/*****************************************************************************
* Copyright (c) 2006-2008 g-Eclipse Consortium
* 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
*
* Initial development of the original code was made for the
* g-Eclipse project founded by European Union
* project number: FP6-IST-034327 http://www.geclipse.eu/
*
* Contributors:
* Mathias Stuempert - initial API and implementation
*****************************************************************************/
package eu.geclipse.ui;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.cheatsheets.CheatSheetListener;
import org.eclipse.ui.cheatsheets.ICheatSheetEvent;
import org.eclipse.ui.cheatsheets.ICheatSheetManager;
import eu.geclipse.core.ICoreProblems;
import eu.geclipse.core.auth.AuthTokenRequest;
import eu.geclipse.core.auth.AuthenticationException;
import eu.geclipse.core.auth.CoreAuthTokenProvider;
import eu.geclipse.core.auth.IAuthTokenProvider;
import eu.geclipse.core.auth.IAuthenticationToken;
import eu.geclipse.core.auth.IAuthenticationTokenDescription;
import eu.geclipse.core.reporting.ProblemException;
import eu.geclipse.ui.dialogs.ProblemDialog;
import eu.geclipse.ui.internal.Activator;
import eu.geclipse.ui.wizards.wizardselection.ExtPointWizardSelectionListPage;
/**
* The <code>UIAuthTokenProvider</code> is the main point where Plugins should request their
* authentication tokens. It should be used instead of the <code>AuthenticationTokenManager</code>
* whenever possible. It provides methods to request any token or tokens of a special type. It also
* takes responsibility for the user interactions with respect to new token wizards, question
* dialogs and error dialogs. Therefore it makes the request for a new token very easy.
*/
public class UIAuthTokenProvider extends CheatSheetListener implements IAuthTokenProvider {
/**
* Runnable implementation that is executed in the UI thread in
* order to retrieve an existing token or to create a new token.
* This has to run in the UI thread in order to allow the popup
* of error dialogs or token wizards.
*/
private class Runner implements Runnable {
/**
* The token description for which to retrieve a token.
*/
AuthTokenRequest request;
/**
* The token that was found or created.
*/
IAuthenticationToken token;
ProblemException exc;
private CoreAuthTokenProvider cProvider;
/**
* Construct a new Runner used to find a token for the specified
* token description.
*
* @param request The {@link AuthTokenRequest} for
* which to find an {@link IAuthenticationToken}.
* @param cProvider
*/
public Runner( final AuthTokenRequest request, final CoreAuthTokenProvider cProvider ) {
this.request = request;
this.cProvider = cProvider;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
// No token could be found, so create one
String title = this.request.getRequester();
if ( title == null ) {
title = Messages.getString( "UIAuthTokenProvider.req_token_title" ); //$NON-NLS-1$
}
String message = this.request.getPurpose();
if ( message == null ) {
message = Messages.getString( "UIAuthTokenProvider.new_token_question" ); //$NON-NLS-1$
}
boolean result = MessageDialog.openQuestion( UIAuthTokenProvider.this.shell, title, message );
if( result ) {
IAuthenticationTokenDescription description = this.request.getDescription();
String tokenWizardId = description.getWizardId();
if ( showNewTokenWizard( tokenWizardId, false, description ) ) {
this.token = this.cProvider.requestToken( this.request );
} else {
this.exc = new ProblemException( ICoreProblems.AUTH_TOKEN_REQUEST_CANCELED, Activator.PLUGIN_ID );
}
} else {
this.exc = new ProblemException( ICoreProblems.AUTH_TOKEN_REQUEST_CANCELED, Activator.PLUGIN_ID );
}
}
}
/**
* A manager used for cheat sheet automation.
*/
protected static ICheatSheetManager cheatSheetManager;
/**
* Key for the auth token wizard.
*/
private static final String WIZARD_PAGE_NAME = "pagename"; //$NON-NLS-1$
/**
* The <code>Shell</code> that is used to create dialogs, error dialogs...
*/
protected Shell shell;
/**
* The display used to synchronously run the token creation process.
*/
protected Display display;
/**
* Standard constructor for the <code>UIAuthTokenProvider</code>.
*/
public UIAuthTokenProvider() {
this( null );
}
/**
* Construct a new <code>UIAuthTokenProvider</code>.
*
* @param shell The shell that is used to create wizards and dialogs.
*/
public UIAuthTokenProvider( final Shell shell ) {
IWorkbench workbench = PlatformUI.getWorkbench();
this.display = workbench.getDisplay();
this.shell = shell;
if ( shell == null ) {
IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
if ( window != null ) {
this.shell = window.getShell();
}
}
}
/**
* Request any authentication token. This is the same as
* <code>requestToken(null)</code>.
*
* @return Any token that could be found.
* @throws ProblemException If the token request was canceled by the user.
* @see #requestToken(AuthTokenRequest)
*/
public IAuthenticationToken requestToken() throws ProblemException {
return requestToken( null );
}
/**
* Request an authentication token that fits the specified description. At the
* moment the token has only to fit the supported type of the description. So
* if a <code>GridProxy</code> is requested an empty
* <code>GridProxyDescription</code> should be passed to this method.
* <p>
* Internally this method queries the <code>AuthenticationTokenManager</code>.
* The first step therefore is always to look for the default token. If the
* default token is of the specified type it is returned. The second step is
* to look for all currently registered tokens. If one of these fits it is
* returned. If no token could be found up to here the new token wizard is
* launched to create a new token that fits the description. The newly created
* token is afterwards added to the token managers managed tokens.
* <p>
* If the found token is not the default token a message box will pop up to
* ask if the token should be set as default token. If the found token is not
* valid or not active it is validated and respectively activated. If
* something wents wrong during this process an error message will pop up.
*
* @param request An {@link AuthTokenRequest} that
* describes the token that is requested.
* @return A token of the type that is described by the specified description
* or null if no such token could be found or created.
* @throws ProblemException If the token request was canceled by the user.
*/
public IAuthenticationToken requestToken( final AuthTokenRequest request ) throws ProblemException {
IAuthenticationToken token = null;
Throwable t = null;
CoreAuthTokenProvider cProvider = new CoreAuthTokenProvider();
try {
token = cProvider.requestToken( request );
} catch ( Exception e ) {
t = e;
}
if ( ( token == null ) && ( t == null ) ) {
Runner runner = new Runner( request, cProvider );
runInUIThread( runner );
if ( runner.exc != null ) {
throw runner.exc;
}
token = runner.token;
}
if( token != null ) {
// Check if the token is both valid and active
try {
if( !token.isValid() ) {
validateToken( token );
}
if( !token.isActive() ) {
activateToken( token );
}
} catch( InvocationTargetException itExc ) {
t = itExc.getCause();
if( t == null ) {
t = itExc;
}
} catch( InterruptedException intExc ) {
t = intExc;
}
}
if ( t != null ) {
ProblemDialog.openProblem( UIAuthTokenProvider.this.shell,
Messages.getString("UIAuthTokenProvider.token_activation_error_title"), //$NON-NLS-1$
Messages.getString("UIAuthTokenProvider.token_activation_error_message"), //$NON-NLS-1$
t );
}
return token;
}
/**
* Show the new token wizard. If the specified description is not null the wizard will
* be started with the wizard page belonging to the specified description. Otherwise it
* will be started with the token type page as starting page where the user can choose
* the type of the he wants to create.
*
* @param tokenWizardId The ID of the token type that should be created or null.
* @param forceWizardId
* @param description Token description passed to the token specific wizard pages in
* order to allow initialisation for a predefined token type.
* @return True if the token dialog was closed with status {@link Window#OK}.
*/
public boolean showNewTokenWizard( final String tokenWizardId,
final boolean forceWizardId,
final IAuthenticationTokenDescription description ) {
URL imgUrl = Activator.getDefault().getBundle().getEntry( "icons/wizban/newtoken_wiz.gif" ); //$NON-NLS-1$
Wizard wizard = new Wizard() {
@Override
public boolean performFinish() {
return false;
}
@Override
public void addPages() {
List<String> filterList = null;
if (tokenWizardId != null) {
filterList = new LinkedList<String>();
filterList.add( tokenWizardId );
}
ExtPointWizardSelectionListPage page = new ExtPointWizardSelectionListPage(
WIZARD_PAGE_NAME,
Extensions.AUTH_TOKEN_UI_POINT,
filterList,
forceWizardId,
Messages.getString( "UIAuthTokenProvider.wizard_first_page_title" ), //$NON-NLS-1$
Messages.getString( "UIAuthTokenProvider.wizard_first_page_description" ), //$NON-NLS-1$
Messages.getString( "UIAuthTokenProvider.noTokenCreator" ) ); //$NON-NLS-1$
// page.setPreselectedId( tokenWizardId, true );
page.setInitData( description );
page.setCheatSheetManager(cheatSheetManager);
addPage( page );
}
};
wizard.setNeedsProgressMonitor( true );
wizard.setForcePreviousAndNextButtons( true );
wizard.setWindowTitle( Messages.getString("UIAuthTokenProvider.wizard_title") ); //$NON-NLS-1$
wizard.setDefaultPageImageDescriptor( ImageDescriptor.createFromURL( imgUrl ) );
WizardDialog dialog = new WizardDialog( this.shell, wizard );
return dialog.open() == Window.OK;
}
/**
* Validates the specified token. This method does the validation in a
* separate thread and provides a progress monitor for the validation process.
*
* @param token The token to be validated.
* @throws InvocationTargetException Thrown if an exception occurs in the
* validation thread.
* @throws InterruptedException Thrown if the validation thread is
* interrupted.
*/
protected void validateToken( final IAuthenticationToken token )
throws InvocationTargetException, InterruptedException
{
final Exception[] exc = new Exception[1];
Runnable runnable = new Runnable() {
public void run() {
ProgressMonitorDialog progMon = new ProgressMonitorDialog( UIAuthTokenProvider.this.shell );
try {
progMon.run( false, false, new IRunnableWithProgress() {
public void run( final IProgressMonitor monitor )
throws InvocationTargetException, InterruptedException
{
try {
token.validate( monitor );
} catch( AuthenticationException authExc ) {
throw new InvocationTargetException( authExc );
}
}
} );
} catch( InvocationTargetException exception ) {
exc[0] = exception;
} catch( InterruptedException exception ) {
exc[0] = exception;
}
}};
runInUIThread( runnable );
if( exc[0] instanceof InvocationTargetException ) {
throw (InvocationTargetException)exc[0];
} else if( exc[0] instanceof InterruptedException ) {
throw (InterruptedException)exc[0];
}
}
/**
* Activate the specified token. This method does the activation in a separate
* thread and provides a progress monitor for the activation process.
*
* @param token The token to be activated.
* @throws InvocationTargetException Thrown if an exception occurs in the
* activation thread.
* @throws InterruptedException Thrown if the activation thread is
* interrupted.
*/
protected void activateToken( final IAuthenticationToken token )
throws InvocationTargetException, InterruptedException
{
final Exception[] exc = new Exception[1];
Runnable uiRunnable = new Runnable() {
public void run() {
ProgressMonitorDialog progMon = new ProgressMonitorDialog( UIAuthTokenProvider.this.shell );
try {
progMon.run( false, false, new IRunnableWithProgress() {
public void run( final IProgressMonitor monitor ) throws InvocationTargetException
{
try {
token.setActive( true, monitor );
} catch( AuthenticationException authExc ) {
throw new InvocationTargetException( authExc );
}
}
} );
} catch( InvocationTargetException exception ) {
exc[0] = exception;
} catch( InterruptedException exception ) {
exc[0] = exception;
}
}};
runInUIThread( uiRunnable );
if( exc[0] instanceof InvocationTargetException ) {
throw (InvocationTargetException)exc[0];
} else if( exc[0] instanceof InterruptedException ) {
throw (InterruptedException)exc[0];
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.cheatsheets.CheatSheetListener#cheatSheetEvent(org.eclipse.ui.cheatsheets.ICheatSheetEvent)
*/
@Override
public void cheatSheetEvent( final ICheatSheetEvent event ) {
cheatSheetManager = event.getCheatSheetManager();
if ( cheatSheetManager.getData( "startingPageName" ) == null ) { //$NON-NLS-1$
cheatSheetManager.setData( "startingPageName", "none" ); //$NON-NLS-1$ //$NON-NLS-2$
}
}
/**
* Method calling {@link Runnable#run()} in UI thread. In the future this
* method will have deadlock detection
*
* @param runnable
*/
private void runInUIThread( final Runnable runnable ) {
this.display.syncExec( runnable );
}
}