/*******************************************************************************
* Copyright (c) 2013 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.foundation.core.ecf.internal;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.internal.preferences.Base64;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.ecf.core.ContainerCreateException;
import org.eclipse.ecf.core.ContainerFactory;
import org.eclipse.ecf.core.IContainer;
import org.eclipse.ecf.core.security.Callback;
import org.eclipse.ecf.core.security.ConnectContextFactory;
import org.eclipse.ecf.core.security.IConnectContext;
import org.eclipse.ecf.core.security.NameCallback;
import org.eclipse.ecf.core.security.PasswordCallback;
import org.eclipse.ecf.core.security.UnsupportedCallbackException;
import org.eclipse.ecf.filetransfer.IFileTransferListener;
import org.eclipse.ecf.filetransfer.IIncomingFileTransfer;
import org.eclipse.ecf.filetransfer.IRemoteFile;
import org.eclipse.ecf.filetransfer.IRemoteFileSystemBrowserContainerAdapter;
import org.eclipse.ecf.filetransfer.IRemoteFileSystemListener;
import org.eclipse.ecf.filetransfer.IRetrieveFileTransferContainerAdapter;
import org.eclipse.ecf.filetransfer.IRetrieveFileTransferOptions;
import org.eclipse.ecf.filetransfer.IncomingFileTransferException;
import org.eclipse.ecf.filetransfer.RemoteFileSystemException;
import org.eclipse.ecf.filetransfer.UserCancelledException;
import org.eclipse.ecf.filetransfer.events.IFileTransferEvent;
import org.eclipse.ecf.filetransfer.events.IIncomingFileTransferReceiveDataEvent;
import org.eclipse.ecf.filetransfer.events.IIncomingFileTransferReceiveDoneEvent;
import org.eclipse.ecf.filetransfer.events.IIncomingFileTransferReceiveStartEvent;
import org.eclipse.ecf.filetransfer.events.IRemoteFileSystemBrowseEvent;
import org.eclipse.ecf.filetransfer.events.IRemoteFileSystemEvent;
import org.eclipse.ecf.filetransfer.identity.FileCreateException;
import org.eclipse.ecf.filetransfer.identity.FileIDFactory;
import org.eclipse.ecf.filetransfer.identity.IFileID;
import org.eclipse.ecf.filetransfer.service.IRetrieveFileTransferFactory;
import org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer;
import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.core.UIServices;
import org.eclipse.equinox.p2.core.UIServices.AuthenticationInfo;
import org.eclipse.equinox.security.storage.ISecurePreferences;
import org.eclipse.equinox.security.storage.SecurePreferencesFactory;
import org.eclipse.equinox.security.storage.StorageException;
import org.eclipse.osgi.util.NLS;
import org.jboss.tools.foundation.core.ecf.Messages;
import org.jboss.tools.foundation.core.internal.FoundationCorePlugin;
import org.jboss.tools.foundation.core.internal.Trace;
import org.jboss.tools.foundation.core.jobs.BarrierWaitJob;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Version;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.util.tracker.ServiceTracker;
public class InternalURLTransport {
private static final String BUNDLE_P2_UI_AUTHORIZATION = "org.eclipse.equinox.p2.ui.sdk";
private static final String BUNDLE_ECF = "org.eclipse.ecf"; //$NON-NLS-1$
private static final String BUNDLE_ECF_FILETRANSFER = "org.eclipse.ecf.provider.filetransfer"; //$NON-NLS-1$
/*
* The following constants are marked public but are in an INTERNAL PACKAGE
*/
public static final String PROTOCOL_FILE = "file"; //$NON-NLS-1$
public static final String PROTOCOL_PLATFORM = "platform"; //$NON-NLS-1$
public static final String PROTOCOL_BUNDLEENTRY = "bundleentry"; //$NON-NLS-1$
/**
* The number of password retry attempts allowed before failing.
*/
private static final int LOGIN_RETRIES = 3;
private static final ProtocolException ERROR_401 = new ProtocolException();
private static final String SERVER_REDIRECT = Messages.ECFExamplesTransport_Server_redirected_too_many_times;
/**
* The node identifier for repository secure preference store.
*/
public static final String PREFERENCE_NODE = "org.jboss.tools.project.examples"; //$NON-NLS-1$
/**
* The key for a string property providing the user name to an authenticated
* URL. This key is used in the secure preference store for repository data.
* @see #PREFERENCE_NODE
*/
public static final String PROP_USERNAME = "username"; //$NON-NLS-1$
/**
* The key for a string property providing the password to an authenticated
* URL. This key is used in the secure preference store for repository data.
* @see #PREFERENCE_NODE
*/
public static final String PROP_PASSWORD = "password"; //$NON-NLS-1$
private static InternalURLTransport INSTANCE;
private ServiceTracker retrievalFactoryTracker;
/**
* Protected to allow backward compatability
* from runtimes version, but avoid other instantiation
*/
protected InternalURLTransport() {
}
/**
* Returns an initialized instance of ECFExamplesTransport
*/
public static synchronized InternalURLTransport getInstance() {
if (INSTANCE == null) {
INSTANCE = new InternalURLTransport();
}
return INSTANCE;
}
/**
* Gets the last modified date for the specified file. If null values are passed, default impl will be used instead.
*
* @param location - The URL location of the file.
* @return A <code>long</code> representing the date. Returns <code>0</code> if the file is not found or an error occurred.
* @exception OperationCanceledException if the request was canceled.
*/
public long getLastModified(URL location, String user, String pass, IProgressMonitor monitor) throws CoreException {
String[] creds = ( user == null || pass == null ) ? null : new String[]{user, pass};
return getLastModified(location, creds, monitor);
}
/**
* Gets the last modified date for the specified file.
* @param location - The URL location of the file.
* @return A <code>long</code> representing the date. Returns <code>0</code> if the file is not found or an error occurred.
* @exception OperationCanceledException if the request was canceled.
*/
public long getLastModified(URL location, IProgressMonitor monitor) throws CoreException {
return getLastModified(location, null, monitor);
}
private long getLastModified(URL location, String[] credentials, IProgressMonitor monitor) throws CoreException {
Trace.trace(Trace.STRING_FINER, "Checking last-modified timestamp for " + location.toExternalForm());
String locationString = location.toExternalForm();
IConnectContext context = null;
if( credentials == null )
context = getConnectionContext(locationString, false);
else
context = getConnectionContext(credentials[0], credentials[1]);
try {
// Try 3 times ONLY if it's a credentialing issue.
// Otherwise, either return the value we get or propogate the exception
for (int i = 0; i < LOGIN_RETRIES; i++) {
if( monitor.isCanceled())
return -1;
try {
long val = doGetLastModified(locationString, context, monitor);
return val;
} catch (ProtocolException e) {
if (ERROR_401 == e)
context = getConnectionContext(locationString, true);
} catch(CoreException ce) {
throw ce;
} catch (Exception e) {
throw new CoreException(FoundationCorePlugin.statusFactory().errorStatus(Messages.ECFExamplesTransport_IO_error, e));
}
}
} catch (UserCancelledException e) {
throw new OperationCanceledException();
}
//too many retries, so report as failure
throw new CoreException(FoundationCorePlugin.statusFactory().errorStatus(Messages.ECFExamplesTransport_IO_error));
}
/**
* Perform the ECF call to get the last modified time, failing if there is any
* protocol failure such as an authentication failure.
*/
private long doGetLastModified(String location, IConnectContext context, IProgressMonitor monitor) throws CoreException, ProtocolException {
IContainer container;
try {
container = ContainerFactory.getDefault().createContainer();
} catch (ContainerCreateException e) {
throw new CoreException(FoundationCorePlugin.statusFactory().errorStatus(
NLS.bind(Messages.ECFExamplesTransport_Initialization_error, location)));
}
IRemoteFileSystemBrowserContainerAdapter adapter = (IRemoteFileSystemBrowserContainerAdapter) container.getAdapter(IRemoteFileSystemBrowserContainerAdapter.class);
if (adapter == null) {
throw new CoreException(FoundationCorePlugin.statusFactory().errorStatus(
NLS.bind(Messages.ECFExamplesTransport_Initialization_error, location)));
}
// Cannot return null, may throw CoreException or ProtocolException
IRemoteFile remoteFile = checkFile(adapter, location, context, monitor);
return remoteFile.getInfo().getLastModified();
}
private CoreException createRemoteFileIOCoreException(String location, Throwable t) {
if( t == null )
return new CoreException(FoundationCorePlugin.statusFactory().errorStatus(
NLS.bind(Messages.ECFExamplesTransport_File_Timestamp_Error, location)));
return new CoreException(FoundationCorePlugin.statusFactory().errorStatus(
NLS.bind(Messages.ECFExamplesTransport_File_Timestamp_Error, location), t));
}
/**
* Downloads the contents of the given URL to the given output stream
* using ONLY ecf. Any resources that is inaccessible via ECF may
* fail here, as ECF is the backing framework.
*
* The destination stream will be closed by this method whether it succeeds
* to download or not.
*/
public IStatus download(String name, String url, OutputStream destination, int timeout, IProgressMonitor monitor) {
return download(name, url, null, destination, timeout, monitor);
}
public IStatus download(String name, String url, String user, String pass, OutputStream destination, int timeout, IProgressMonitor monitor) {
String[] creds = ( user == null || pass == null ) ? null : new String[]{user, pass};
return download(name, url, creds, destination, timeout, monitor);
}
private IStatus download(String name, String url, String[] credentials, OutputStream destination, int timeout, IProgressMonitor monitor) {
Trace.trace(Trace.STRING_FINER, "Downloading url " + url + " to an outputstream");
IStatus status = null;
IConnectContext context = null;
try {
if( credentials == null )
context = getConnectionContext(url, false);
if( context == null && credentials != null)
context = getConnectionContext(credentials[0], credentials[1]);
for (int i = 0; i < LOGIN_RETRIES; i++) {
if( monitor.isCanceled())
return FoundationCorePlugin.statusFactory().cancelStatus(Messages.ECFTransport_Operation_canceled);
try {
status = performDownload(name,url, destination, context, timeout, monitor);
if (status.isOK()) {
return status;
} else {
Throwable exception = status.getException();
if (exception instanceof IncomingFileTransferException) {
int code = ((IncomingFileTransferException)exception).getErrorCode();
if (code == 401) {
context = getConnectionContext(url, true);
}
}
}
} catch (ProtocolException e) {
if (e == ERROR_401)
context = getConnectionContext(url, true);
}
}
} catch (UserCancelledException e) {
return FoundationCorePlugin.statusFactory().cancelStatus(Messages.ECFTransport_Operation_canceled);
} catch (CoreException e) {
return e.getStatus();
} finally {
try {
destination.close();
} catch (IOException e) {
//ignore secondary failure
}
}
//reached maximum number of retries without success
if (status != null) {
return status;
}
return FoundationCorePlugin.statusFactory().errorStatus(Messages.ECFExamplesTransport_IO_error);
}
/**
* This method downloads a file to an output stream using ECF.
* Any url which may fail on ECF (some local files) may not want to use this method.
*
* @param name
* @param toDownload
* @param target
* @param context
* @param monitor
* @return
* @throws ProtocolException
*/
public IStatus performDownload(String name,String toDownload, OutputStream target,
IConnectContext context, IProgressMonitor monitor) throws ProtocolException {
return performDownload(name, toDownload, target, context, -1, monitor);
}
/**
* This method downloads a file to an output stream using ECF.
* Any url which may fail on ECF (some local files) may not want to use this method.
*
* @param name
* @param toDownload
* @param target
* @param context
* @param timeout
* @param monitor
* @return
* @throws ProtocolException
*/
public IStatus performDownload(String name,String toDownload, OutputStream target,
IConnectContext context, int timeout, IProgressMonitor monitor) throws ProtocolException {
IRetrieveFileTransferFactory factory = (IRetrieveFileTransferFactory) getFileTransferServiceTracker().getService();
if (factory == null)
return FoundationCorePlugin.statusFactory().errorStatus(Messages.ECFExamplesTransport_IO_error);
return transfer(name,factory.newInstance(), toDownload, target, context, timeout, monitor);
}
private IStatus transfer(final String name,final IRetrieveFileTransferContainerAdapter retrievalContainer, final String toDownload,
final OutputStream target, IConnectContext context, int timeout, final IProgressMonitor monitor) throws ProtocolException {
Trace.trace(Trace.STRING_FINER, "Beginning transfer for remote file " + toDownload);
final IStatus[] result = new IStatus[1];
final IIncomingFileTransferReceiveStartEvent[] cancelable = new IIncomingFileTransferReceiveStartEvent[1];
cancelable[0] = null;
IFileTransferListener listener = getFileTransferListener(result, cancelable, target, toDownload, name, monitor);
HashMap<Object, Object> map = new HashMap<Object, Object>();
if( timeout >= 0 ) {
map.put(IRetrieveFileTransferOptions.CONNECT_TIMEOUT, new Integer(timeout));
map.put(IRetrieveFileTransferOptions.READ_TIMEOUT, new Integer(timeout));
}
if( context != null ) {
// This solution works, but it troubles me a bit.
// Ideally, the call to retrievalContainer.setConnectContextForAuthentication(etc) should work, but it doesn't.
// Passing the credentials via headers (insecure) is not a regression, since we always did it before, but,
// I would imagine passing via setConnectContextForAuthentication should work and the fact it doesn't is strange to me.
NameCallback ncb = new NameCallback("Username");
PasswordCallback pcb = new PasswordCallback("Password");
Callback[] arr = new Callback[]{ncb, pcb};
try {
context.getCallbackHandler().handle(arr);
Map<String, String> headers = new HashMap<String, String>();
String n = ncb.getName();
String p = pcb.getPassword();
if( n != null && p != null ) {
String userCredentials = ncb.getName()+ ":" + pcb.getPassword();
String basicAuth = "Basic " + new String(new Base64().encode(userCredentials.getBytes()));
headers.put("Authorization", basicAuth);
map.put(IRetrieveFileTransferOptions.REQUEST_HEADERS, headers);
}
} catch(IOException | UnsupportedCallbackException ce) {
}
}
try {
// In a slow-response server, this could block. The proper solution is to INTERRUPT this thread.
retrievalContainer.setConnectContextForAuthentication(context);
retrievalContainer.sendRetrieveRequest(FileIDFactory.getDefault().createFileID(retrievalContainer.getRetrieveNamespace(), toDownload), listener, map);
} catch (IncomingFileTransferException e) {
IStatus status = e.getStatus();
Throwable exception = status.getException();
if (exception instanceof IOException) {
if (exception.getMessage() != null && (exception.getMessage().indexOf("401") != -1 || exception.getMessage().indexOf(SERVER_REDIRECT) != -1)) //$NON-NLS-1$
throw ERROR_401;
}
return status;
} catch (FileCreateException e) {
return e.getStatus();
}
try {
waitFor(toDownload, result);
} catch(InterruptedException ie) {
if( cancelable[0] != null ) {
cancelable[0].cancel();
return FoundationCorePlugin.statusFactory().cancelStatus(Messages.ECFTransport_Operation_canceled);
}
}
return result[0];
}
private IFileTransferListener getFileTransferListener(final IStatus[] result, final IIncomingFileTransferReceiveStartEvent[] cancelable,
final OutputStream target, final String toDownload, final String name, final IProgressMonitor monitor) {
IFileTransferListener listener = new IFileTransferListener() {
private long transferStartTime;
protected int oldWorked;
// Ensure no updates to status until 250 ms due to possible mac bug with too many updates
private long throttleMilliseconds = 250;
private long lastProgressUpdate = System.currentTimeMillis();
public void handleTransferEvent(IFileTransferEvent event) {
if (event instanceof IIncomingFileTransferReceiveStartEvent) {
Trace.trace(Trace.STRING_FINER, "Transfer has begun for " + toDownload);
if( monitor.isCanceled()) {
synchronized(result) {
result[0] = FoundationCorePlugin.statusFactory().cancelStatus(Messages.ECFTransport_Operation_canceled);
result.notify();
}
}
IIncomingFileTransferReceiveStartEvent rse = (IIncomingFileTransferReceiveStartEvent) event;
cancelable[0] = rse;
try {
if (target != null) {
rse.receive(target);
transferStartTime = System.currentTimeMillis();
}
if (monitor != null) {
long fileLength = rse.getSource().getFileLength();
final long totalWork = ((fileLength == -1) ? 100 : fileLength);
int work = (totalWork > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) totalWork;
monitor.beginTask(NLS.bind(Messages.ECFExamplesTransport_Downloading, name), work);
lastProgressUpdate = System.currentTimeMillis();
oldWorked=0;
}
} catch (IOException e) {
IStatus status = convertToStatus(e);
synchronized (result) {
result[0] = status;
result.notify();
}
}
}
if (event instanceof IIncomingFileTransferReceiveDataEvent) {
IIncomingFileTransfer source = ((IIncomingFileTransferReceiveDataEvent) event).getSource();
if (monitor != null) {
if (monitor.isCanceled()) {
try {
source.cancel();
} catch (Throwable e) {
FoundationCorePlugin.pluginLog().logWarning(Messages.ECFTransport_Operation_canceled);
}
IStatus status = FoundationCorePlugin.statusFactory().cancelStatus(Messages.ECFTransport_Operation_canceled);
synchronized (result) {
result[0] = status;
result.notify();
}
return;
}
long currentTime = System.currentTimeMillis();
if( lastProgressUpdate + throttleMilliseconds < currentTime ) {
long fileLength = source.getFileLength();
final long totalWork = ((fileLength == -1) ? 100 : fileLength);
double factor = (totalWork > Integer.MAX_VALUE) ? (((double) Integer.MAX_VALUE) / ((double) totalWork)) : 1.0;
long received = source.getBytesReceived();
int worked = (int) Math.round(factor * received);
double downloadRateBytesPerSecond = (received / ((System.currentTimeMillis() + 1 - transferStartTime) / 1000.0));
String downloadRateString = AbstractRetrieveFileTransfer.toHumanReadableBytes(downloadRateBytesPerSecond);
String receivedString = AbstractRetrieveFileTransfer.toHumanReadableBytes(received);
String fileLengthString = AbstractRetrieveFileTransfer.toHumanReadableBytes(fileLength);
String str = null;
if( fileLength < 0 ) {
str = NLS.bind(
Messages.ECFExamplesTransport_ReceivedSize_At_RatePerSecond,
new String[]{receivedString, downloadRateString});
} else {
str = NLS.bind(
Messages.ECFExamplesTransport_ReceivedSize_Of_FileSize_At_RatePerSecond,
new String[]{receivedString, fileLengthString, downloadRateString});
}
Trace.trace(Trace.STRING_FINEST, "Transfer " + toDownload + " status: " + str);
monitor.subTask(str);
lastProgressUpdate = currentTime;
monitor.worked(worked-oldWorked);
oldWorked=worked;
}
}
}
if (event instanceof IIncomingFileTransferReceiveDoneEvent) {
Trace.trace(Trace.STRING_FINER, "Transfer " + toDownload + " is complete");
Exception exception = ((IIncomingFileTransferReceiveDoneEvent) event).getException();
IStatus status = convertToStatus(exception);
synchronized (result) {
result[0] = status;
result.notify();
}
}
}
};
return listener;
}
private IRemoteFile checkFile(final IRemoteFileSystemBrowserContainerAdapter retrievalContainer,
final String location, IConnectContext context, final IProgressMonitor monitor) throws CoreException, ProtocolException {
final Object[] result = new Object[2];
final Object FAIL = new Object();
IRemoteFileSystemListener listener = new IRemoteFileSystemListener() {
public void handleRemoteFileEvent(IRemoteFileSystemEvent event) {
Exception exception = event.getException();
if (exception != null) {
synchronized (result) {
result[0] = FAIL;
result[1] = exception;
result.notify();
}
} if( monitor.isCanceled() ) {
synchronized(result) {
result[0] = FoundationCorePlugin.statusFactory().cancelStatus(Messages.ECFTransport_Operation_canceled);
result.notify();
}
} else if (event instanceof IRemoteFileSystemBrowseEvent) {
IRemoteFileSystemBrowseEvent fsbe = (IRemoteFileSystemBrowseEvent) event;
IRemoteFile[] remoteFiles = fsbe.getRemoteFiles();
if (remoteFiles != null && remoteFiles.length > 0 && remoteFiles[0] != null) {
synchronized (result) {
result[0] = remoteFiles[0];
result.notify();
}
} else {
synchronized (result) {
result[0] = FAIL;
result.notify();
}
}
}
}
};
try {
retrievalContainer.setConnectContextForAuthentication(context);
IFileID id = FileIDFactory.getDefault().createFileID(retrievalContainer.getBrowseNamespace(), location);
// In a slow-response server, this could block. The proper solution is to INTERRUPT this thread.
retrievalContainer.sendBrowseRequest(id, listener);
} catch (RemoteFileSystemException e) {
throw createRemoteFileIOCoreException(location, e);
} catch (FileCreateException e) {
throw createRemoteFileIOCoreException(location, e);
}
try {
waitFor(location, result);
} catch(InterruptedException ie) {
// There is no way to clean up the remote request,
throw createRemoteFileIOCoreException(location, ie);
}
if (result[0] == FAIL && result[1] instanceof IOException) {
IOException ioException = (IOException) result[1];
//throw a special exception for authentication failure so we know to prompt for username/password
String message = ioException.getMessage();
if (message != null && (message.indexOf(" 401 ") != -1 || message.indexOf(SERVER_REDIRECT) != -1)) //$NON-NLS-1$
throw ERROR_401;
}
if (result[0] instanceof IRemoteFile)
return (IRemoteFile) result[0];
throw createRemoteFileIOCoreException(location, null);
}
// For when we already know the credentials to use
public IConnectContext getConnectionContext(String user, String pass) throws UserCancelledException, CoreException {
return ConnectContextFactory.createUsernamePasswordConnectContext(user, pass);
}
/**
* Returns the connection context for the given URL. This may prompt the
* user for user name and password as required.
*
* @param xmlLocation - the file location requiring login details
* @param prompt - use <code>true</code> to prompt the user instead of
* looking at the secure preference store for login, use <code>false</code>
* to only try the secure preference store
* @throws UserCancelledException when the user cancels the login prompt
* @throws CoreException if the password cannot be read or saved
* @return The connection context
*/
public IConnectContext getConnectionContext(String xmlLocation, boolean prompt) throws UserCancelledException, CoreException {
ISecurePreferences securePreferences = SecurePreferencesFactory.getDefault();
IPath hostLocation = new Path(xmlLocation).removeLastSegments(1);
String nodeKey;
try {
nodeKey = URLEncoder.encode(hostLocation.toString(), "UTF-8"); //$NON-NLS-1$
} catch (UnsupportedEncodingException e2) {
//fall back to default platform encoding
nodeKey = URLEncoder.encode(hostLocation.toString());
}
String nodeName = PREFERENCE_NODE + '/' + nodeKey;
ISecurePreferences prefNode = null;
if (securePreferences.nodeExists(nodeName))
prefNode = securePreferences.node(nodeName);
if (!prompt) {
if (prefNode == null)
return null;
try {
String username = prefNode.get(PROP_USERNAME, null);
String password = prefNode.get(PROP_PASSWORD, null);
//if we don't have stored connection data just return a null connection context
if (username == null || password == null)
return null;
return ConnectContextFactory.createUsernamePasswordConnectContext(username, password);
} catch (StorageException e) {
String msg = Messages.ECFExamplesTransport_Internal_Error;
throw new CoreException(FoundationCorePlugin.statusFactory().errorStatus(msg, e));
}
}
//need to prompt user for user name and password
// check if adminUIService has been started
forceStart(BUNDLE_P2_UI_AUTHORIZATION);
IProvisioningAgent agent = (IProvisioningAgent) ServiceHelper.getService(FoundationCorePlugin.getDefault().getBundleContext(), IProvisioningAgent.SERVICE_NAME);
UIServices adminUIService = (UIServices) agent.getService(UIServices.SERVICE_NAME);
AuthenticationInfo loginDetails = null;
if (adminUIService != null)
loginDetails = adminUIService.getUsernamePassword(hostLocation.toString());
//null result means user canceled password dialog
if (loginDetails == null || loginDetails == UIServices.AUTHENTICATION_PROMPT_CANCELED)
throw new UserCancelledException();
//save user name and password if requested by user
if (loginDetails.saveResult()) {
if (prefNode == null)
prefNode = securePreferences.node(nodeName);
try {
prefNode.put(PROP_USERNAME, loginDetails.getUserName(), true);
prefNode.put(PROP_PASSWORD, loginDetails.getPassword(), true);
prefNode.flush();
} catch (StorageException e1) {
String msg = Messages.ECFExamplesTransport_Internal_Error;
throw new CoreException(FoundationCorePlugin.statusFactory().errorStatus(msg, e1));
} catch (IOException e) {
String msg = Messages.ECFExamplesTransport_Internal_Error;
throw new CoreException(FoundationCorePlugin.statusFactory().errorStatus(msg, e));
}
}
return ConnectContextFactory.createUsernamePasswordConnectContext(loginDetails.getUserName(), loginDetails.getPassword());
}
private IStatus convertToStatus(Exception e) {
if (e == null)
return Status.OK_STATUS;
if (e instanceof UserCancelledException)
return FoundationCorePlugin.statusFactory().cancelStatus(e);
return FoundationCorePlugin.statusFactory().errorStatus(e);
}
/**
* Waits until the first entry in the given array is non-null.
*/
private void waitFor(String location, Object[] barrier) throws InterruptedException {
Trace.trace(Trace.STRING_FINER, "Waiting for remote file to download: " + location);
BarrierWaitJob.waitForSynchronous(Messages.ECFExamplesTransport_Loading, barrier, true);
}
private synchronized ServiceTracker getFileTransferServiceTracker() {
if (retrievalFactoryTracker == null) {
retrievalFactoryTracker = new ServiceTracker(FoundationCorePlugin.getDefault().getBundleContext(), IRetrieveFileTransferFactory.class.getName(), null);
retrievalFactoryTracker.open();
Trace.trace(Trace.STRING_FINER, "Ensuring all bundles for URLTransport are started");
requestStart(BUNDLE_ECF); //$NON-NLS-1$
requestStart(BUNDLE_ECF_FILETRANSFER); //$NON-NLS-1$
}
return retrievalFactoryTracker;
}
/*
* The following methods may one day be moved to a public
* BundleUtility class once a suitable API can be discovered.
*/
/**
* This method will request a start for the first version
* of the requested bundle id that is found.
*
* @param bundleId The id of the bundle you wish to start
* @return boolean whether the bundle has been found and is now started
*/
private boolean requestStart(String bundleId) {
return requestStart(bundleId, null);
}
/**
* Request a start for the bundle and version combination selected
* or, if version is null, the first bundle
* @param bundleId The id of the bundle you wish to start
* @param version The requested version, or null.
* @return boolean whether the bundle has been found and is now started
*/
private boolean requestStart(String bundleId, Version version) {
/*
* PackageAdmin is deprecated, but is still (on forums) the agreed
* upon way of locating and starting individual bundles. It was not
* ported over the new API.
*/
BundleContext bc = FoundationCorePlugin.getDefault().getBundleContext();
PackageAdmin packageAdmin = (PackageAdmin) ServiceHelper.getService(
bc, PackageAdmin.class.getName());
if (packageAdmin == null)
return false;
Bundle[] bundles = packageAdmin.getBundles(bundleId, null);
if (bundles != null && bundles.length > 0) {
for (int i = 0; i < bundles.length; i++) {
if( version == null || bundles[i].getVersion().equals(version)) {
try {
if ((bundles[i].getState() & Bundle.INSTALLED) == 0) {
bundles[i].start(Bundle.START_ACTIVATION_POLICY);
bundles[i].start(Bundle.START_TRANSIENT);
return true;
}
} catch (BundleException e) {
// failed, try next bundle
}
}
}
}
return false;
}
/**
* Force the bundle into the active state now.
* @param bundleId
* @return boolean whether the bundle is now active
*/
private boolean forceStart(String bundleId) {
Trace.trace(Trace.STRING_FINEST, "Forcing " + bundleId + " to start");
Bundle bundle = Platform.getBundle(bundleId); //$NON-NLS-1$
if (bundle != null && bundle.getState() != Bundle.ACTIVE) {
try {
bundle.start();
} catch (BundleException e) {
// ignore
}
}
return bundle.getState() == Bundle.ACTIVE;
}
}