comparison 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
comparison
equal deleted inserted replaced
697:24f4efedc9d5 698:822f3a83ff57
17 package org.tmatesoft.hg.repo; 17 package org.tmatesoft.hg.repo;
18 18
19 import static org.tmatesoft.hg.util.LogFacility.Severity.Warn; 19 import static org.tmatesoft.hg.util.LogFacility.Severity.Warn;
20 20
21 import java.io.File; 21 import java.io.File;
22 import java.net.MalformedURLException; 22 import java.io.InputStream;
23 import java.net.URI;
24 import java.net.URISyntaxException;
23 import java.net.URL; 25 import java.net.URL;
26 import java.net.URLStreamHandler;
27 import java.net.URLStreamHandlerFactory;
24 28
25 import org.tmatesoft.hg.core.HgBadArgumentException; 29 import org.tmatesoft.hg.core.HgBadArgumentException;
26 import org.tmatesoft.hg.core.HgIOException; 30 import org.tmatesoft.hg.core.HgIOException;
27 import org.tmatesoft.hg.core.HgRepositoryNotFoundException; 31 import org.tmatesoft.hg.core.HgRepositoryNotFoundException;
28 import org.tmatesoft.hg.core.SessionContext; 32 import org.tmatesoft.hg.core.SessionContext;
29 import org.tmatesoft.hg.internal.BasicSessionContext; 33 import org.tmatesoft.hg.internal.BasicSessionContext;
30 import org.tmatesoft.hg.internal.ConfigFile; 34 import org.tmatesoft.hg.internal.ConfigFile;
31 import org.tmatesoft.hg.internal.DataAccessProvider; 35 import org.tmatesoft.hg.internal.DataAccessProvider;
32 import org.tmatesoft.hg.internal.Internals; 36 import org.tmatesoft.hg.internal.Experimental;
33 import org.tmatesoft.hg.internal.RequiresFile; 37 import org.tmatesoft.hg.internal.RequiresFile;
34 import org.tmatesoft.hg.repo.HgRepoConfig.PathsSection; 38 import org.tmatesoft.hg.repo.HgRepoConfig.PathsSection;
35 39
36 /** 40 /**
37 * Utility methods to find Mercurial repository at a given location 41 * Utility methods to find Mercurial repository at a given location
110 * @param hgRepo optional local repository to get default or otherwise configured remote location 114 * @param hgRepo optional local repository to get default or otherwise configured remote location
111 * @return an instance featuring access to remote repository, check {@link HgRemoteRepository#isInvalid()} before actually using it 115 * @return an instance featuring access to remote repository, check {@link HgRemoteRepository#isInvalid()} before actually using it
112 * @throws HgBadArgumentException if anything is wrong with the remote server's URL 116 * @throws HgBadArgumentException if anything is wrong with the remote server's URL
113 */ 117 */
114 public HgRemoteRepository detectRemote(String key, HgRepository hgRepo) throws HgBadArgumentException { 118 public HgRemoteRepository detectRemote(String key, HgRepository hgRepo) throws HgBadArgumentException {
115 URL url; 119 URI uri;
116 Exception toReport; 120 Exception toReport;
117 try { 121 try {
118 url = new URL(key); 122 uri = new URI(key);
119 toReport = null; 123 toReport = null;
120 } catch (MalformedURLException ex) { 124 } catch (URISyntaxException ex) {
121 url = null; 125 uri = null;
122 toReport = ex; 126 toReport = ex;
123 } 127 }
124 if (url == null) { 128 if (uri == null) {
125 String server = null; 129 String server = null;
126 if (hgRepo != null && !hgRepo.isInvalid()) { 130 if (hgRepo != null && !hgRepo.isInvalid()) {
127 PathsSection ps = hgRepo.getConfiguration().getPaths(); 131 PathsSection ps = hgRepo.getConfiguration().getPaths();
128 server = key == null || key.trim().length() == 0 ? ps.getDefault() : ps.getString(key, null); // XXX Java 1.5 isEmpty() 132 server = key == null || key.trim().length() == 0 ? ps.getDefault() : ps.getString(key, null); // XXX Java 1.5 isEmpty()
129 } else if (key == null || key.trim().length() == 0) { 133 } else if (key == null || key.trim().length() == 0) {
134 } 138 }
135 if (server == null) { 139 if (server == null) {
136 throw new HgBadArgumentException(String.format("Can't find server %s specification in the config", key), toReport); 140 throw new HgBadArgumentException(String.format("Can't find server %s specification in the config", key), toReport);
137 } 141 }
138 try { 142 try {
139 url = new URL(server); 143 uri = new URI(server);
140 } catch (MalformedURLException ex) { 144 } catch (URISyntaxException ex) {
141 throw new HgBadArgumentException(String.format("Found %s server spec in the config, but failed to initialize with it", key), ex); 145 throw new HgBadArgumentException(String.format("Found %s server spec in the config, but failed to initialize with it", key), ex);
142 } 146 }
143 } 147 }
144 return new HgRemoteRepository(getSessionContext(), url); 148 return detectRemote(uri);
145 } 149 }
146 150
151 /**
152 * Detect remote repository
153 * <p>Use of this method is discouraged, please use {@link #detectRemote(URI)} instead}
154 *
155 * @param url location of remote repository
156 * @return instance to interact with remote repository
157 * @throws HgBadArgumentException if location format is not a valid {@link URI}
158 * @throws IllegalArgumentException if url is <code>null</code>
159 */
147 public HgRemoteRepository detect(URL url) throws HgBadArgumentException { 160 public HgRemoteRepository detect(URL url) throws HgBadArgumentException {
148 if (url == null) { 161 if (url == null) {
149 throw new IllegalArgumentException(); 162 throw new IllegalArgumentException();
150 } 163 }
151 if (Boolean.FALSE.booleanValue()) { 164 try {
152 throw Internals.notImplemented(); 165 return detectRemote(url.toURI());
153 } 166 } catch (URISyntaxException ex) {
154 return new HgRemoteRepository(getSessionContext(), url); 167 throw new HgBadArgumentException(String.format("Bad remote repository location: %s", url), ex);
168 }
169 }
170
171 /**
172 * Resolves location of remote repository.
173 *
174 * <p>Choice between {@link URI URIs} and {@link URL URLs} done in favor of former because they are purely syntactical,
175 * while latter have semantics of {@link URL#openConnection() connection} establishing, which is not always possible.
176 * E.g. one can't instantiate <code>new URL("ssh://localhost/")</code> as long as there's no local {@link URLStreamHandler}
177 * for the protocol, which is the case for certain JREs out there. The way {@link URLStreamHandlerFactory URLStreamHandlerFactories}
178 * are installed (static field/method) is quite fragile, and definitely not the one to rely on from a library's code (apps can carefully
179 * do this, but library can't install own nor expect anyone outside there would do). Use of fake {@link URLStreamHandler} (fails to open
180 * every connection) is possible, of course, although it seems to be less appropriate when there is alternative with {@link URI URIs}.
181 *
182 * @param uriRemote remote repository location
183 * @return instance to interact with remote repository
184 * @throws HgBadArgumentException
185 */
186 public HgRemoteRepository detectRemote(URI uriRemote) throws HgBadArgumentException {
187 RemoteDescriptor rd = getSessionContext().getRemoteDescriptor(uriRemote);
188 if (rd == null) {
189 throw new HgBadArgumentException(String.format("Unsupported remote repository location:%s", uriRemote), null);
190 }
191 return new HgRemoteRepository(getSessionContext(), rd);
155 } 192 }
156 193
157 private ConfigFile getGlobalConfig() { 194 private ConfigFile getGlobalConfig() {
158 if (globalCfg == null) { 195 if (globalCfg == null) {
159 globalCfg = new ConfigFile(getSessionContext()); 196 globalCfg = new ConfigFile(getSessionContext());
171 if (sessionContext == null) { 208 if (sessionContext == null) {
172 sessionContext = new BasicSessionContext(null); 209 sessionContext = new BasicSessionContext(null);
173 } 210 }
174 return sessionContext; 211 return sessionContext;
175 } 212 }
213
214
215 /**
216 * Session context ({@link SessionContext#getRemoteDescriptor(URI)} gives descriptor of remote when asked.
217 */
218 @Experimental(reason="Work in progress")
219 public interface RemoteDescriptor {
220 URI getURI();
221 Authenticator getAuth();
222 }
223
224 @Experimental(reason="Work in progress")
225 public interface Authenticator {
226 public void authenticate(RemoteDescriptor remote, AuthMethod authMethod);
227 }
228
229 /**
230 * Clients do not implement this interface, instead, they invoke appropriate authentication method
231 * once they got user input
232 */
233 @Experimental(reason="Work in progress")
234 public interface AuthMethod {
235 public void noCredentials() throws AuthFailedException;
236 public boolean supportsPassword();
237 public void withPassword(String username, byte[] password) throws AuthFailedException;
238 public boolean supportsPublicKey();
239 public void withPublicKey(String username, InputStream publicKey, String passphrase) throws AuthFailedException;
240 public boolean supportsCertificate();
241 public void withCertificate() throws AuthFailedException;
242 }
243
244 @SuppressWarnings("serial")
245 public class AuthFailedException extends Exception /*XXX HgRemoteException?*/ {
246 public AuthFailedException(String message, Throwable cause) {
247 super(message, cause);
248 }
249 }
176 } 250 }