Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHenrik Lindberg2009-08-26 21:15:09 +0000
committerHenrik Lindberg2009-08-26 21:15:09 +0000
commit600e890188a495ca44180163f1d93616fb88ecb0 (patch)
tree1fea25dde2fdb725816635c13ae10e05e5535234 /bundles/org.eclipse.equinox.p2.repository
parent3857e9aa964cc8fe33a211c97ac3649d8d6fb981 (diff)
downloadrt.equinox.p2-600e890188a495ca44180163f1d93616fb88ecb0.tar.gz
rt.equinox.p2-600e890188a495ca44180163f1d93616fb88ecb0.tar.xz
rt.equinox.p2-600e890188a495ca44180163f1d93616fb88ecb0.zip
Bug 287558 - Adopt ECF support on NTLM2 (part 1)
Added support for the new ECF status code 477 - after this checkin a new version of ECF that generates 477 status code can be used - work remains to actually switch http client when this occurs.
Diffstat (limited to 'bundles/org.eclipse.equinox.p2.repository')
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF1
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Activator.java6
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Credentials.java428
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileInfoReader.java15
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileReader.java13
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/JREHttpClientRequiredException.java20
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatusHelper.java16
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTransport.java23
-rw-r--r--bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/DebugHelper.java125
9 files changed, 568 insertions, 79 deletions
diff --git a/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF
index d55cba84f..4cf5a1439 100644
--- a/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF
@@ -20,6 +20,7 @@ Import-Package: javax.xml.parsers,
org.eclipse.equinox.internal.p2.repository.helpers,
org.eclipse.equinox.internal.provisional.p2.core,
org.eclipse.equinox.security.storage,
+ org.eclipse.osgi.service.debug,
org.eclipse.osgi.util;version="1.1.0",
org.osgi.framework;version="1.4.0",
org.osgi.service.packageadmin;version="1.2.0",
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Activator.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Activator.java
index fe8774eb8..1837a17c7 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Activator.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Activator.java
@@ -62,6 +62,12 @@ public class Activator implements BundleActivator {
return (IRetrieveFileTransferFactory) getFileTransferServiceTracker().getService();
}
+ public synchronized void useJREHttpClient() {
+ // TODO: Check of JREHttpClient is already in use - then do nothing, else switch to JRE HTTP Client
+ //
+ // TODO - Log that the http client was switched.
+ }
+
/**
* Gets the singleton ServiceTracker for the IRetrieveFileTransferFactory and starts the bundles
* "org.eclipse.ecf" and
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Credentials.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Credentials.java
index 7fcceb6a6..0ff55aac9 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Credentials.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/Credentials.java
@@ -19,6 +19,7 @@ import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.ecf.filetransfer.UserCancelledException;
import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
+import org.eclipse.equinox.internal.p2.repository.helpers.DebugHelper;
import org.eclipse.equinox.internal.provisional.p2.core.IServiceUI;
import org.eclipse.equinox.internal.provisional.p2.core.IServiceUI.AuthenticationInfo;
import org.eclipse.equinox.internal.provisional.p2.repository.IRepository;
@@ -35,9 +36,23 @@ public class Credentials {
}
+ /**
+ * Cache of auth information that is not persisted, and modified auth info.
+ */
private static final Map savedAuthInfo = Collections.synchronizedMap(new HashMap());
/**
+ * Information about retry counts, and prompts canceled by user. The SoftReference is
+ * a Map if not null. The keys are also used as serialization per host.
+ */
+ private static Map remembered;
+
+ /**
+ * Serializes pop up of login/password prompt
+ */
+ private static final Object promptLock = new Object();
+
+ /**
* Returns the AuthenticationInfo for the given URI. This may prompt the
* user for user name and password as required.
*
@@ -76,23 +91,7 @@ public class Credentials {
* @throws CoreException if there is an error
*/
public static AuthenticationInfo forLocation(URI location, boolean prompt, AuthenticationInfo lastUsed) throws LoginCanceledException, CoreException {
- ISecurePreferences securePreferences = SecurePreferencesFactory.getDefault();
-
- // if URI is not opaque, just getting the host may be enough
- String host = location.getHost();
- if (host == null) {
- String scheme = location.getScheme();
- if (URIUtil.isFileURI(location) || scheme == null)
- // If the URI references a file, a password could possibly be needed for the directory
- // (it could be a protected zip file representing a compressed directory) - in this
- // case the key is the path without the last segment.
- // Using "Path" this way may result in an empty string - which later will result in
- // an invalid key.
- host = new Path(location.toString()).removeLastSegments(1).toString();
- else
- // it is an opaque URI - details are unknown - can only use entire string.
- host = location.toString();
- }
+ String host = uriToHost(location);
String nodeKey;
try {
nodeKey = URLEncoder.encode(host, "UTF-8"); //$NON-NLS-1$
@@ -109,69 +108,214 @@ public class Credentials {
throw RepositoryStatusHelper.internalError(e);
}
}
- String nodeName = IRepository.PREFERENCE_NODE + '/' + nodeKey;
- ISecurePreferences prefNode = null;
- try {
- if (securePreferences.nodeExists(nodeName))
- prefNode = securePreferences.node(nodeName);
- } catch (IllegalArgumentException e) {
- // if the node name is illegal/malformed (should not happen).
- throw RepositoryStatusHelper.internalError(e);
- } catch (IllegalStateException e) {
- // thrown if preference store has been tampered with
- throw RepositoryStatusHelper.internalError(e);
+ if (DebugHelper.DEBUG_REPOSITORY_CREDENTIALS) {
+ DebugHelper.debug("Credentials", "forLocation:ENTER", // //$NON-NLS-1$ //$NON-NLS-2$
+ new Object[] {"host", location, "prompt", Boolean.toString(prompt)}); //$NON-NLS-1$ //$NON-NLS-2$
}
- if (!prompt) {
- try {
- if (prefNode != null) {
- String username = prefNode.get(IRepository.PROP_USERNAME, null);
- String password = prefNode.get(IRepository.PROP_PASSWORD, null);
- // if we don't have stored connection data just return a null auth info
- if (username != null && password != null)
- return new IServiceUI.AuthenticationInfo(username, password, true);
- }
- return restoreFromMemory(nodeName);
- } catch (StorageException e) {
- throw RepositoryStatusHelper.internalError(e);
+
+ // Must serialize getting stored permissions per host as the location may
+ // be prompted right now
+ // Start by getting a key to lock on
+ HostEntry hostLock = null;
+ synchronized (Credentials.class) {
+ Map r = getRemembered();
+ hostLock = (HostEntry) r.get(host);
+ if (hostLock == null) {
+ hostLock = new HostEntry(0);
+ r.put(host, hostLock);
}
}
- //need to prompt user for user name and password
- IServiceUI adminUIService = (IServiceUI) ServiceHelper.getService(Activator.getContext(), IServiceUI.class.getName());
AuthenticationInfo loginDetails = null;
- if (adminUIService != null)
- loginDetails = lastUsed != null ? adminUIService.getUsernamePassword(host, lastUsed) : adminUIService.getUsernamePassword(host);
- //null result means user canceled password dialog
- if (loginDetails == null)
- throw new LoginCanceledException();
- //save user name and password if requested by user
- if (loginDetails.saveResult()) {
- if (prefNode == null)
- prefNode = securePreferences.node(nodeName);
+ ISecurePreferences securePreferences = null;
+ // synchronize getting secure store with prompting user, as it may prompt.
+ synchronized (promptLock) {
+ securePreferences = SecurePreferencesFactory.getDefault();
+ }
+
+ // serialize the prompting per host
+ synchronized (hostLock) {
try {
- prefNode.put(IRepository.PROP_USERNAME, loginDetails.getUserName(), true);
- prefNode.put(IRepository.PROP_PASSWORD, loginDetails.getPassword(), true);
- prefNode.flush();
- } catch (StorageException e1) {
- throw RepositoryStatusHelper.internalError(e1);
- } catch (IOException e) {
- throw RepositoryStatusHelper.internalError(e);
- }
- } else {
- // if persisted earlier - the preference should be removed
- if (securePreferences.nodeExists(nodeName)) {
- prefNode = securePreferences.node(nodeName);
- prefNode.removeNode();
+ if (DebugHelper.DEBUG_REPOSITORY_CREDENTIALS) {
+ DebugHelper.debug("Credentials", "forLocation:HOSTLOCK OBTAINED", // //$NON-NLS-1$ //$NON-NLS-2$
+ new Object[] {"host", location, "prompt", Boolean.toString(prompt)}); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ String nodeName = IRepository.PREFERENCE_NODE + '/' + nodeKey;
+ ISecurePreferences prefNode = null;
try {
- prefNode.flush();
- } catch (IOException e) {
+ if (securePreferences.nodeExists(nodeName))
+ prefNode = securePreferences.node(nodeName);
+ } catch (IllegalArgumentException e) {
+ // if the node name is illegal/malformed (should not happen).
+ throw RepositoryStatusHelper.internalError(e);
+ } catch (IllegalStateException e) {
+ // thrown if preference store has been tampered with
throw RepositoryStatusHelper.internalError(e);
}
+ if (!prompt) {
+ try {
+ if (prefNode != null) {
+ String username = prefNode.get(IRepository.PROP_USERNAME, null);
+ String password = prefNode.get(IRepository.PROP_PASSWORD, null);
+ if (DebugHelper.DEBUG_REPOSITORY_CREDENTIALS) {
+ if (username != null && password != null) {
+ DebugHelper.debug("Credentials", "forLocation:PREFNODE FOUND - USING STORED INFO", // //$NON-NLS-1$ //$NON-NLS-2$
+ new Object[] {"host", location, "prompt", Boolean.toString(prompt)}); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ // if we don't have stored connection data just return a null auth info
+ if (username != null && password != null)
+ return new IServiceUI.AuthenticationInfo(username, password, true);
+ }
+ if (DebugHelper.DEBUG_REPOSITORY_CREDENTIALS) {
+ DebugHelper.debug("Credentials", "forLocation:PREFNODE NOT FOUND - RETURN FROM MEMORY", // //$NON-NLS-1$ //$NON-NLS-2$
+ new Object[] {"host", location, "prompt", Boolean.toString(prompt)}); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ return restoreFromMemory(nodeName);
+ } catch (StorageException e) {
+ throw RepositoryStatusHelper.internalError(e);
+ }
+ }
+ // need to prompt user for user name and password
+ // first check (throw exception) if having a remembered cancel
+ checkRememberedCancel(host);
+
+ // check if another thread has modified the credentials since last attempt
+ // made by current thread - if so, try with latest without prompting
+ if (DebugHelper.DEBUG_REPOSITORY_CREDENTIALS) {
+ AuthenticationInfo latest = restoreFromMemory(nodeName);
+ boolean useLatest = false;
+ if (latest != null && lastUsed != null)
+ if (!(latest.getUserName().equals(lastUsed.getUserName()) && latest.getPassword().equals(lastUsed.getPassword())))
+ useLatest = true;
+ if (useLatest)
+ DebugHelper.debug("Credentials", "forLocation:LATER INFO AVAILABLE - RETURNING IT", // //$NON-NLS-1$ //$NON-NLS-2$
+ new Object[] {"host", location, "prompt", Boolean.toString(prompt)}); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ AuthenticationInfo latest = restoreFromMemory(nodeName);
+ if (latest != null && lastUsed != null)
+ if (!(latest.getUserName().equals(lastUsed.getUserName()) && latest.getPassword().equals(lastUsed.getPassword())))
+ return latest;
+
+ // check if number of prompts have been exceeded for the host - if so
+ // do a synthetic Login canceled by user
+ // (The alternative is to return "latest" until retry login gives up with
+ // authentication failed - but that would waste time).
+ if (DebugHelper.DEBUG_REPOSITORY_CREDENTIALS) {
+ if (getPromptCount(host) >= RepositoryPreferences.getLoginRetryCount()) {
+ if (lastUsed == null && latest == null)
+ DebugHelper.debug("Credentials", "forLocation:NO INFO - SYNTHETIC CANCEL", // //$NON-NLS-1$ //$NON-NLS-2$
+ new Object[] {"host", location}); //$NON-NLS-1$
+ return latest == null ? lastUsed : latest; // keep client failing on the latest known
+ }
+ DebugHelper.debug("Credentials", "forLocation:LATER INFO AVAILABLE - RETURNING IT", // //$NON-NLS-1$ //$NON-NLS-2$
+ new Object[] {"host", location, "prompt", Boolean.toString(prompt)}); //$NON-NLS-1$ //$NON-NLS-2$
+
+ }
+ if (getPromptCount(host) >= RepositoryPreferences.getLoginRetryCount()) {
+ if (lastUsed == null && latest == null)
+ throw new LoginCanceledException();
+ return latest == null ? lastUsed : latest; // keep client failing on the latest known
+ }
+ IServiceUI adminUIService = (IServiceUI) ServiceHelper.getService(Activator.getContext(), IServiceUI.class.getName());
+ if (adminUIService != null)
+ synchronized (promptLock) {
+ try {
+ if (DebugHelper.DEBUG_REPOSITORY_CREDENTIALS) {
+ DebugHelper.debug("Credentials", "forLocation:PROMPTLOCK OBTAINED", // //$NON-NLS-1$ //$NON-NLS-2$
+ new Object[] {"host", location}); //$NON-NLS-1$
+ }
+
+ // serialize the popping of the dialog itself
+ loginDetails = lastUsed != null ? adminUIService.getUsernamePassword(host, lastUsed) : adminUIService.getUsernamePassword(host);
+ //null result means user canceled password dialog
+ if (DebugHelper.DEBUG_REPOSITORY_CREDENTIALS) {
+ if (loginDetails == null)
+ DebugHelper.debug("Credentials", "forLocation:PROMPTED - USER CANCELED (PROMPT LOCK RELEASED)", // //$NON-NLS-1$ //$NON-NLS-2$
+ new Object[] {"host", location}); //$NON-NLS-1$
+ }
+ if (loginDetails == null) {
+ rememberCancel(host);
+ throw new LoginCanceledException();
+ }
+ //save user name and password if requested by user
+ if (DebugHelper.DEBUG_REPOSITORY_CREDENTIALS) {
+ if (loginDetails.saveResult())
+ DebugHelper.debug("Credentials", "forLocation:SAVING RESULT", // //$NON-NLS-1$ //$NON-NLS-2$
+ new Object[] {"host", location}); //$NON-NLS-1$
+ }
+
+ if (loginDetails.saveResult()) {
+ if (prefNode == null)
+ prefNode = securePreferences.node(nodeName);
+ try {
+ prefNode.put(IRepository.PROP_USERNAME, loginDetails.getUserName(), true);
+ prefNode.put(IRepository.PROP_PASSWORD, loginDetails.getPassword(), true);
+ prefNode.flush();
+ } catch (StorageException e1) {
+ throw RepositoryStatusHelper.internalError(e1);
+ } catch (IOException e) {
+ throw RepositoryStatusHelper.internalError(e);
+ }
+ } else {
+ // if persisted earlier - the preference should be removed
+ if (securePreferences.nodeExists(nodeName)) {
+ if (DebugHelper.DEBUG_REPOSITORY_CREDENTIALS) {
+ DebugHelper.debug("Credentials", "forLocation:REMOVING PREVIOUSLY SAVED INFO", // //$NON-NLS-1$ //$NON-NLS-2$
+ new Object[] {"host", location}); //$NON-NLS-1$
+ }
+
+ prefNode = securePreferences.node(nodeName);
+ prefNode.removeNode();
+ try {
+ prefNode.flush();
+ } catch (IOException e) {
+ throw RepositoryStatusHelper.internalError(e);
+ }
+ }
+ }
+ saveInMemory(nodeName, loginDetails);
+ } finally {
+ if (DebugHelper.DEBUG_REPOSITORY_CREDENTIALS) {
+ DebugHelper.debug("Credentials", "forLocation:PROMPTLOCK RELEASED", // //$NON-NLS-1$ //$NON-NLS-2$
+ new Object[] {"host", location}); //$NON-NLS-1$
+ }
+ }
+ }
+ incrementPromptCount(host);
+ } finally {
+ if (DebugHelper.DEBUG_REPOSITORY_CREDENTIALS) {
+ DebugHelper.debug("Credentials", "forLocation:HOSTLOCK RELEASED", // //$NON-NLS-1$ //$NON-NLS-2$
+ new Object[] {"host", location}); //$NON-NLS-1$
+ }
}
- saveInMemory(nodeName, loginDetails);
+
}
+
return loginDetails;
}
+ private static String uriToHost(URI location) {
+ // if URI is not opaque, just getting the host may be enough
+ String host = location.getHost();
+ if (host == null) {
+ String scheme = location.getScheme();
+ if (URIUtil.isFileURI(location) || scheme == null)
+ // If the URI references a file, a password could possibly be needed for the directory
+ // (it could be a protected zip file representing a compressed directory) - in this
+ // case the key is the path without the last segment.
+ // Using "Path" this way may result in an empty string - which later will result in
+ // an invalid key.
+ host = new Path(location.toString()).removeLastSegments(1).toString();
+ else
+ // it is an opaque URI - details are unknown - can only use entire string.
+ host = location.toString();
+ }
+ return host;
+ }
+
/**
* Returns authentication details stored in memory for the given node name,
* or <code>null</code> if no information is stored.
@@ -181,10 +325,158 @@ public class Credentials {
}
/**
- * Saves authentication details in memory so user is only prompted once per session
+ * Saves authentication details in memory so user is only prompted once per (SDK) session
*/
private static void saveInMemory(String nodeName, AuthenticationInfo loginDetails) {
savedAuthInfo.put(nodeName, loginDetails);
}
+ /**
+ * Remember the fact that the host was canceled.
+ * @param host
+ */
+ private static void rememberCancel(String host) {
+ Map r = getRemembered();
+ if (r != null)
+ r.put(host, new HostEntry(-1));
+ }
+
+ /**
+ * Throws LoginCancledException if the host was previously canceled, and the information
+ * is not stale.
+ * @param host
+ * @throws LoginCanceledException
+ */
+ private static void checkRememberedCancel(String host) throws LoginCanceledException {
+ Map r = getRemembered();
+ if (r != null) {
+ Object x = r.get(host);
+ if (x != null && x instanceof HostEntry)
+ if (((HostEntry) x).isCanceled()) {
+ if (DebugHelper.DEBUG_REPOSITORY_CREDENTIALS) {
+ DebugHelper.debug("Credentials", "checkRememberCancel:PREVIOUSLY CANCELED", // //$NON-NLS-1$ //$NON-NLS-2$
+ new Object[] {"host", host}); //$NON-NLS-1$
+ }
+
+ throw new LoginCanceledException();
+ }
+ }
+
+ }
+
+ /**
+ * Increments the prompt count for host. If information is stale, the count is restarted
+ * at 1.
+ * @param host
+ */
+ private static void incrementPromptCount(String host) {
+ Map r = getRemembered();
+ if (r != null) {
+ HostEntry value = (HostEntry) r.get(host);
+ if (value == null)
+ r.put(host, value = new HostEntry(1));
+ else {
+ if (value.isStale())
+ value.reset();
+ value.increment();
+ }
+ }
+ }
+
+ /**
+ * Returns prompt count for host, except if information is stale in which case 0 is returned.
+ * @param host
+ * @return number of time prompt has been performed for a host (or 0 if information is stale)
+ */
+ private static int getPromptCount(String host) {
+ Map r = getRemembered();
+ if (r != null) {
+ HostEntry value = (HostEntry) r.get(host);
+ if (value != null && !value.isStale())
+ return value.getCount();
+ }
+ return 0;
+
+ }
+
+ /**
+ * Clears the cached information about prompts for all login/password and
+ * canceled logins.
+ */
+ public static synchronized void clearPromptCache() {
+ if (remembered == null)
+ return;
+ Map r = remembered;
+ if (r == null || r.isEmpty())
+ return;
+ // reset entries rather than creating a new empty map since the entries
+ // are also used as locks
+ Iterator itor = r.entrySet().iterator();
+ while (itor.hasNext())
+ ((HostEntry) itor.next()).reset();
+ }
+
+ /**
+ * Clears the cached information for location about prompts for login/password and
+ * canceled logins.
+ * @param location the repository location
+ */
+ public static synchronized void clearPromptCache(URI location) {
+ clearPromptCache(uriToHost(location));
+ }
+
+ /**
+ * Clears the cached information for host about prompts for login/password and
+ * canceled logins.
+ * @param host a host as returned from uriToHost for a location
+ */
+ public static synchronized void clearPromptCache(String host) {
+ if (remembered == null)
+ return;
+ Map r = remembered;
+ if (r == null)
+ return;
+ HostEntry value = (HostEntry) r.get(host);
+ if (value != null)
+ value.reset();
+ }
+
+ private static synchronized Map getRemembered() {
+ if (remembered == null)
+ remembered = Collections.synchronizedMap(new HashMap());
+ return remembered;
+ }
+
+ private static class HostEntry {
+ long timestamp;
+ int count;
+
+ public HostEntry(int count) {
+ this.count = count;
+ this.timestamp = System.currentTimeMillis();
+ }
+
+ public boolean isCanceled() {
+ return count == -1 && !isStale();
+ }
+
+ public boolean isStale() {
+ // a record is stale if older than 3 minutes
+ return System.currentTimeMillis() - timestamp > 1000 * 60 * 3;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public void increment() {
+ if (count != -1)
+ count++;
+ }
+
+ public void reset() {
+ count = 0;
+ timestamp = System.currentTimeMillis();
+ }
+ }
}
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileInfoReader.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileInfoReader.java
index 20236bc6d..c84d4e2a6 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileInfoReader.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileInfoReader.java
@@ -98,8 +98,9 @@ public class FileInfoReader extends Job implements IRemoteFileSystemListener {
* @throws CoreException
* @throws FileNotFoundException
* @throws AuthenticationFailedException
+ * @throws JREHttpClientRequiredException
*/
- public IRemoteFile[] getRemoteFiles(URI location, IProgressMonitor monitor) throws AuthenticationFailedException, FileNotFoundException, CoreException {
+ public IRemoteFile[] getRemoteFiles(URI location, IProgressMonitor monitor) throws AuthenticationFailedException, FileNotFoundException, CoreException, JREHttpClientRequiredException {
if (monitor != null)
monitor.beginTask(location.toString(), 1);
try {
@@ -117,13 +118,13 @@ public class FileInfoReader extends Job implements IRemoteFileSystemListener {
}
- public IRemoteFile getRemoteFile(URI location, IProgressMonitor monitor) throws AuthenticationFailedException, FileNotFoundException, CoreException {
+ public IRemoteFile getRemoteFile(URI location, IProgressMonitor monitor) throws AuthenticationFailedException, FileNotFoundException, CoreException, JREHttpClientRequiredException {
getRemoteFiles(location, monitor);
return remoteFiles != null && remoteFiles.length > 0 ? remoteFiles[0] : null;
}
- public long getLastModified(URI location, IProgressMonitor monitor) throws AuthenticationFailedException, FileNotFoundException, CoreException {
+ public long getLastModified(URI location, IProgressMonitor monitor) throws AuthenticationFailedException, FileNotFoundException, CoreException, JREHttpClientRequiredException {
IRemoteFile file = getRemoteFile(location, monitor);
if (file == null)
throw new FileNotFoundException(location.toString());
@@ -154,7 +155,7 @@ public class FileInfoReader extends Job implements IRemoteFileSystemListener {
}
}
- protected void sendBrowseRequest(URI uri, IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException {
+ protected void sendBrowseRequest(URI uri, IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException, JREHttpClientRequiredException {
IContainer container;
try {
container = ContainerFactory.getDefault().createContainer();
@@ -202,10 +203,14 @@ public class FileInfoReader extends Job implements IRemoteFileSystemListener {
* @throws CoreException
* @throws FileNotFoundException
* @throws AuthenticationFailedException
+ * @throws JREHttpClientRequiredException
*/
- private boolean checkException(URI uri, int attemptCounter) throws CoreException, FileNotFoundException, AuthenticationFailedException {
+ private boolean checkException(URI uri, int attemptCounter) throws CoreException, FileNotFoundException, AuthenticationFailedException, JREHttpClientRequiredException {
// note that 'exception' could have been captured in a callback
if (exception != null) {
+ // check if HTTP client needs to be changed
+ RepositoryStatusHelper.checkJREHttpClientRequired(exception);
+
// if this is a authentication failure - it is not meaningful to continue
RepositoryStatusHelper.checkPermissionDenied(exception);
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileReader.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileReader.java
index 81846dd2f..5f3394b14 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileReader.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/FileReader.java
@@ -173,7 +173,7 @@ public final class FileReader extends FileTransferJob implements IFileTransferLi
}
}
- public InputStream read(URI url, final IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException {
+ public InputStream read(URI url, final IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException, JREHttpClientRequiredException {
final PipedInputStream input = new PipedInputStream();
PipedOutputStream output;
try {
@@ -247,7 +247,7 @@ public final class FileReader extends FileTransferJob implements IFileTransferLi
}
public void readInto(URI uri, OutputStream anOutputStream, IProgressMonitor monitor) //
- throws CoreException, FileNotFoundException, AuthenticationFailedException {
+ throws CoreException, FileNotFoundException, AuthenticationFailedException, JREHttpClientRequiredException {
readInto(uri, anOutputStream, -1, monitor);
}
@@ -256,7 +256,7 @@ public final class FileReader extends FileTransferJob implements IFileTransferLi
}
public void readInto(URI uri, OutputStream anOutputStream, long startPos, IProgressMonitor monitor) //
- throws CoreException, FileNotFoundException, AuthenticationFailedException {
+ throws CoreException, FileNotFoundException, AuthenticationFailedException, JREHttpClientRequiredException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
@@ -284,7 +284,7 @@ public final class FileReader extends FileTransferJob implements IFileTransferLi
}
protected void sendRetrieveRequest(URI uri, OutputStream outputStream, DownloadRange range, boolean closeStreamOnFinish, //
- IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException {
+ IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException, JREHttpClientRequiredException {
IRetrieveFileTransferFactory factory = Activator.getDefault().getRetrieveFileTransferFactory();
if (factory == null) {
@@ -339,9 +339,12 @@ public final class FileReader extends FileTransferJob implements IFileTransferLi
* @throws FileNotFoundException
* @throws AuthenticationFailedException
*/
- private boolean checkException(URI uri, int attemptCounter) throws CoreException, FileNotFoundException, AuthenticationFailedException {
+ private boolean checkException(URI uri, int attemptCounter) throws CoreException, FileNotFoundException, AuthenticationFailedException, JREHttpClientRequiredException {
// note that 'exception' could have been captured in a callback
if (exception != null) {
+ // check if HTTP client needs to be changed
+ RepositoryStatusHelper.checkJREHttpClientRequired(exception);
+
// if this is an 'authentication failure' - it is not meaningful to continue
RepositoryStatusHelper.checkPermissionDenied(exception);
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/JREHttpClientRequiredException.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/JREHttpClientRequiredException.java
new file mode 100644
index 000000000..5d722aead
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/JREHttpClientRequiredException.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2009, Cloudsmith Inc.
+ * The code, documentation and other materials contained herein have been
+ * licensed under the Eclipse Public License - v 1.0 by the copyright holder
+ * listed above, as the Initial Contributor under such license. The text of
+ * such license is available at www.eclipse.org.
+ ******************************************************************************/
+
+package org.eclipse.equinox.internal.p2.repository;
+
+import java.net.ProtocolException;
+
+/**
+ * Exception signaling that the JRE Http Client is required to handle the request.
+ */
+public class JREHttpClientRequiredException extends ProtocolException {
+
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatusHelper.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatusHelper.java
index 7f87da896..601a2da4d 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatusHelper.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryStatusHelper.java
@@ -227,6 +227,22 @@ public abstract class RepositoryStatusHelper {
}
/**
+ * Check if the given exception represents that a switch to the JRE HTTP Client
+ * is required. ECF sets the HTTP status code 477 to indicate this.
+ * If the JRE HTTP client is required a JREHttpClientRequiredException is thrown.
+ */
+ public static void checkJREHttpClientRequired(Throwable t) throws JREHttpClientRequiredException {
+ if (t instanceof IncomingFileTransferException) {
+ if (((IncomingFileTransferException) t).getErrorCode() == 477)
+ throw new JREHttpClientRequiredException();
+ } else if (t instanceof BrowseFileTransferException) {
+ if (((BrowseFileTransferException) t).getErrorCode() == 477)
+ throw new JREHttpClientRequiredException();
+ }
+
+ }
+
+ /**
* Check if the given exception represents a permission failure (401 for HTTP),
* and throw a AuthenticationFailedException if a permission failure was encountered.
*/
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTransport.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTransport.java
index c8bef36b8..02d7027b3 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTransport.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/RepositoryTransport.java
@@ -58,6 +58,7 @@ public class RepositoryTransport extends Transport {
public IStatus download(URI toDownload, OutputStream target, long startPos, IProgressMonitor monitor) {
boolean promptUser = false;
+ boolean useJREHttp = false;
AuthenticationInfo loginDetails = null;
for (int i = RepositoryPreferences.getLoginRetryCount(); i > 0; i--) {
FileReader reader = null;
@@ -97,7 +98,12 @@ public class RepositoryTransport extends Transport {
DownloadStatus status = new DownloadStatus(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_AUTHENTICATION, //
NLS.bind(Messages.UnableToRead_0_UserCanceled, toDownload), null);
return statusOn(target, status, null);
-
+ } catch (JREHttpClientRequiredException e) {
+ if (!useJREHttp) {
+ useJREHttp = true; // only do this once
+ i++; // need an extra retry
+ Activator.getDefault().useJREHttpClient();
+ }
}
}
// reached maximum number of retries without success
@@ -135,6 +141,7 @@ public class RepositoryTransport extends Transport {
public InputStream stream(URI toDownload, IProgressMonitor monitor) throws FileNotFoundException, CoreException, AuthenticationFailedException {
boolean promptUser = false;
+ boolean useJREHttp = false;
AuthenticationInfo loginDetails = null;
for (int i = RepositoryPreferences.getLoginRetryCount(); i > 0; i--) {
FileReader reader = null;
@@ -157,6 +164,12 @@ public class RepositoryTransport extends Transport {
} catch (LoginCanceledException e) {
// i.e. same behavior when user cancels as when failing n attempts.
throw new AuthenticationFailedException();
+ } catch (JREHttpClientRequiredException e) {
+ if (!useJREHttp) {
+ useJREHttp = true; // only do this once
+ i++; // need an extra retry
+ Activator.getDefault().useJREHttpClient();
+ }
}
}
throw new AuthenticationFailedException();
@@ -195,6 +208,7 @@ public class RepositoryTransport extends Transport {
*/
public long getLastModified(URI toDownload, IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException {
boolean promptUser = false;
+ boolean useJREHttp = false;
AuthenticationInfo loginDetails = null;
for (int i = RepositoryPreferences.getLoginRetryCount(); i > 0; i--) {
try {
@@ -215,7 +229,14 @@ public class RepositoryTransport extends Transport {
} catch (LoginCanceledException e) {
// same behavior as if user failed n attempts.
throw new AuthenticationFailedException();
+ } catch (JREHttpClientRequiredException e) {
+ if (!useJREHttp) {
+ useJREHttp = true; // only do this once
+ i++; // need an extra retry
+ Activator.getDefault().useJREHttpClient();
+ }
}
+
}
// reached maximum number of authentication retries without success
throw new AuthenticationFailedException();
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/DebugHelper.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/DebugHelper.java
new file mode 100644
index 000000000..f230f47c5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/DebugHelper.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.repository.helpers;
+
+import java.util.*;
+import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
+import org.eclipse.equinox.internal.p2.repository.Activator;
+import org.eclipse.osgi.service.debug.DebugOptions;
+
+public class DebugHelper {
+ public static final String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$
+
+ public static final boolean DEBUG_REPOSITORY_CREDENTIALS;
+ public static final boolean DEBUG_REPOSITORY_TRANSPORT;
+
+ static {
+ DebugOptions options = (DebugOptions) ServiceHelper.getService(Activator.getContext(), DebugOptions.class.getName());
+ if (options != null) {
+ DEBUG_REPOSITORY_CREDENTIALS = options.getBooleanOption(Activator.ID + "/credentials/debug", false); //$NON-NLS-1$
+ DEBUG_REPOSITORY_TRANSPORT = options.getBooleanOption(Activator.ID + "/transport/debug", false); //$NON-NLS-1$
+ } else {
+ DEBUG_REPOSITORY_CREDENTIALS = false;
+ DEBUG_REPOSITORY_TRANSPORT = false;
+ }
+ }
+
+ public static void debug(String name, String message) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("["); //$NON-NLS-1$
+ buffer.append(Activator.ID + "-" + name); //$NON-NLS-1$
+ buffer.append("] "); //$NON-NLS-1$
+ buffer.append(new Date(System.currentTimeMillis()));
+ buffer.append(" - ["); //$NON-NLS-1$
+ buffer.append(Thread.currentThread().getName());
+ buffer.append("] " + LINE_SEPARATOR); //$NON-NLS-1$
+ buffer.append(message);
+ System.out.println(buffer.toString());
+ }
+
+ public static void debug(String name, String message, Object[] keyValueArray) {
+ if (keyValueArray == null || keyValueArray.length == 0)
+ debug(name, message);
+ else {
+ Map params = new LinkedHashMap(keyValueArray.length / 2);
+ for (int i = 0; i < keyValueArray.length; i += 2)
+ params.put(keyValueArray[i], keyValueArray[i + 1]);
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(message);
+ buffer.append(formatMap(params, true, true));
+ debug(name, buffer.toString());
+ }
+
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("["); //$NON-NLS-1$
+ buffer.append(Activator.ID + "-" + name); //$NON-NLS-1$
+ buffer.append("] "); //$NON-NLS-1$
+ buffer.append(new Date(System.currentTimeMillis()));
+ buffer.append(" - ["); //$NON-NLS-1$
+ buffer.append(Thread.currentThread().getName());
+ buffer.append("] " + LINE_SEPARATOR); //$NON-NLS-1$
+ buffer.append(message);
+ System.out.println(buffer.toString());
+ }
+
+ public static String formatArray(Object[] array, boolean toString, boolean newLines) {
+ if (array == null || array.length == 0)
+ return "[]"; //$NON-NLS-1$
+
+ StringBuffer buffer = new StringBuffer();
+ buffer.append('[');
+ int i = 0;
+ for (;;) {
+ if (toString)
+ buffer.append(array[i].toString());
+ else
+ buffer.append(array[i].getClass().getName());
+ i++;
+ if (i == array.length)
+ break;
+ buffer.append(',');
+ if (newLines)
+ buffer.append(DebugHelper.LINE_SEPARATOR);
+ else
+ buffer.append(' ');
+ }
+ buffer.append(']');
+ return buffer.toString();
+ }
+
+ public static String formatMap(Map map, boolean toString, boolean newLines) {
+ if (map == null || map.size() == 0)
+ return "[]"; //$NON-NLS-1$
+
+ StringBuffer buffer = new StringBuffer();
+ buffer.append('[');
+ Iterator itor = map.entrySet().iterator();
+ while (itor.hasNext()) {
+ Map.Entry e = (Map.Entry) itor.next();
+ buffer.append(e.getKey());
+ buffer.append('=');
+ if (toString)
+ buffer.append(e.getValue().toString());
+ else
+ buffer.append(e.getValue().getClass().getName());
+
+ buffer.append(',');
+ if (newLines) {
+ buffer.append(DebugHelper.LINE_SEPARATOR);
+ buffer.append(" "); //$NON-NLS-1$
+ } else
+ buffer.append(' ');
+ }
+ buffer.append(']');
+ return buffer.toString();
+ }
+
+}

Back to the top