Mercurial > hg4j
diff src/org/tmatesoft/hg/repo/HgLookup.java @ 698:822f3a83ff57
in, out and clone tests pass for ssh repositories. Infrastructure to decouple HgRemoteRepository from specific Connector implementation
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Tue, 06 Aug 2013 21:18:33 +0200 |
parents | 6526d8adbc0f |
children | a483b2b68a2e |
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/repo/HgLookup.java Tue Aug 06 13:34:34 2013 +0200 +++ b/src/org/tmatesoft/hg/repo/HgLookup.java Tue Aug 06 21:18:33 2013 +0200 @@ -19,8 +19,12 @@ import static org.tmatesoft.hg.util.LogFacility.Severity.Warn; import java.io.File; -import java.net.MalformedURLException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; import org.tmatesoft.hg.core.HgBadArgumentException; import org.tmatesoft.hg.core.HgIOException; @@ -29,7 +33,7 @@ import org.tmatesoft.hg.internal.BasicSessionContext; import org.tmatesoft.hg.internal.ConfigFile; import org.tmatesoft.hg.internal.DataAccessProvider; -import org.tmatesoft.hg.internal.Internals; +import org.tmatesoft.hg.internal.Experimental; import org.tmatesoft.hg.internal.RequiresFile; import org.tmatesoft.hg.repo.HgRepoConfig.PathsSection; @@ -112,16 +116,16 @@ * @throws HgBadArgumentException if anything is wrong with the remote server's URL */ public HgRemoteRepository detectRemote(String key, HgRepository hgRepo) throws HgBadArgumentException { - URL url; + URI uri; Exception toReport; try { - url = new URL(key); + uri = new URI(key); toReport = null; - } catch (MalformedURLException ex) { - url = null; + } catch (URISyntaxException ex) { + uri = null; toReport = ex; } - if (url == null) { + if (uri == null) { String server = null; if (hgRepo != null && !hgRepo.isInvalid()) { PathsSection ps = hgRepo.getConfiguration().getPaths(); @@ -136,22 +140,55 @@ throw new HgBadArgumentException(String.format("Can't find server %s specification in the config", key), toReport); } try { - url = new URL(server); - } catch (MalformedURLException ex) { + uri = new URI(server); + } catch (URISyntaxException ex) { throw new HgBadArgumentException(String.format("Found %s server spec in the config, but failed to initialize with it", key), ex); } } - return new HgRemoteRepository(getSessionContext(), url); + return detectRemote(uri); } + /** + * Detect remote repository + * <p>Use of this method is discouraged, please use {@link #detectRemote(URI)} instead} + * + * @param url location of remote repository + * @return instance to interact with remote repository + * @throws HgBadArgumentException if location format is not a valid {@link URI} + * @throws IllegalArgumentException if url is <code>null</code> + */ public HgRemoteRepository detect(URL url) throws HgBadArgumentException { if (url == null) { throw new IllegalArgumentException(); } - if (Boolean.FALSE.booleanValue()) { - throw Internals.notImplemented(); + try { + return detectRemote(url.toURI()); + } catch (URISyntaxException ex) { + throw new HgBadArgumentException(String.format("Bad remote repository location: %s", url), ex); } - return new HgRemoteRepository(getSessionContext(), url); + } + + /** + * Resolves location of remote repository. + * + * <p>Choice between {@link URI URIs} and {@link URL URLs} done in favor of former because they are purely syntactical, + * while latter have semantics of {@link URL#openConnection() connection} establishing, which is not always possible. + * E.g. one can't instantiate <code>new URL("ssh://localhost/")</code> as long as there's no local {@link URLStreamHandler} + * for the protocol, which is the case for certain JREs out there. The way {@link URLStreamHandlerFactory URLStreamHandlerFactories} + * are installed (static field/method) is quite fragile, and definitely not the one to rely on from a library's code (apps can carefully + * do this, but library can't install own nor expect anyone outside there would do). Use of fake {@link URLStreamHandler} (fails to open + * every connection) is possible, of course, although it seems to be less appropriate when there is alternative with {@link URI URIs}. + * + * @param uriRemote remote repository location + * @return instance to interact with remote repository + * @throws HgBadArgumentException + */ + public HgRemoteRepository detectRemote(URI uriRemote) throws HgBadArgumentException { + RemoteDescriptor rd = getSessionContext().getRemoteDescriptor(uriRemote); + if (rd == null) { + throw new HgBadArgumentException(String.format("Unsupported remote repository location:%s", uriRemote), null); + } + return new HgRemoteRepository(getSessionContext(), rd); } private ConfigFile getGlobalConfig() { @@ -173,4 +210,41 @@ } return sessionContext; } + + + /** + * Session context ({@link SessionContext#getRemoteDescriptor(URI)} gives descriptor of remote when asked. + */ + @Experimental(reason="Work in progress") + public interface RemoteDescriptor { + URI getURI(); + Authenticator getAuth(); + } + + @Experimental(reason="Work in progress") + public interface Authenticator { + public void authenticate(RemoteDescriptor remote, AuthMethod authMethod); + } + + /** + * Clients do not implement this interface, instead, they invoke appropriate authentication method + * once they got user input + */ + @Experimental(reason="Work in progress") + public interface AuthMethod { + public void noCredentials() throws AuthFailedException; + public boolean supportsPassword(); + public void withPassword(String username, byte[] password) throws AuthFailedException; + public boolean supportsPublicKey(); + public void withPublicKey(String username, InputStream publicKey, String passphrase) throws AuthFailedException; + public boolean supportsCertificate(); + public void withCertificate() throws AuthFailedException; + } + + @SuppressWarnings("serial") + public class AuthFailedException extends Exception /*XXX HgRemoteException?*/ { + public AuthFailedException(String message, Throwable cause) { + super(message, cause); + } + } }