Mercurial > hg4j
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 } |