Mercurial > jhg
comparison src/org/tmatesoft/hg/repo/HgRemoteRepository.java @ 646:3b7d51ed4c65
Push: phase3 - update matching remote bookmarks
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Fri, 21 Jun 2013 18:30:35 +0200 |
parents | 14dac192aa26 |
children | e79cf9a8130b |
comparison
equal
deleted
inserted
replaced
645:14dac192aa26 | 646:3b7d51ed4c65 |
---|---|
60 import org.tmatesoft.hg.core.HgRemoteConnectionException; | 60 import org.tmatesoft.hg.core.HgRemoteConnectionException; |
61 import org.tmatesoft.hg.core.HgRepositoryNotFoundException; | 61 import org.tmatesoft.hg.core.HgRepositoryNotFoundException; |
62 import org.tmatesoft.hg.core.Nodeid; | 62 import org.tmatesoft.hg.core.Nodeid; |
63 import org.tmatesoft.hg.core.SessionContext; | 63 import org.tmatesoft.hg.core.SessionContext; |
64 import org.tmatesoft.hg.internal.DataSerializer; | 64 import org.tmatesoft.hg.internal.DataSerializer; |
65 import org.tmatesoft.hg.internal.EncodingHelper; | |
65 import org.tmatesoft.hg.internal.Internals; | 66 import org.tmatesoft.hg.internal.Internals; |
66 import org.tmatesoft.hg.internal.DataSerializer.OutputStreamSerializer; | 67 import org.tmatesoft.hg.internal.DataSerializer.OutputStreamSerializer; |
67 import org.tmatesoft.hg.internal.PropertyMarshal; | 68 import org.tmatesoft.hg.internal.PropertyMarshal; |
69 import org.tmatesoft.hg.util.Pair; | |
70 import org.tmatesoft.hg.util.LogFacility.Severity; | |
68 | 71 |
69 /** | 72 /** |
70 * WORK IN PROGRESS, DO NOT USE | 73 * WORK IN PROGRESS, DO NOT USE |
71 * | 74 * |
72 * @see http://mercurial.selenic.com/wiki/WireProtocol | 75 * @see http://mercurial.selenic.com/wiki/WireProtocol |
76 * @see http://mercurial.selenic.com/wiki/HttpCommandProtocol | |
73 * | 77 * |
74 * @author Artem Tikhomirov | 78 * @author Artem Tikhomirov |
75 * @author TMate Software Ltd. | 79 * @author TMate Software Ltd. |
76 */ | 80 */ |
77 public class HgRemoteRepository implements SessionContext.Source { | 81 public class HgRemoteRepository implements SessionContext.Source { |
160 authInfo = null; | 164 authInfo = null; |
161 } | 165 } |
162 } | 166 } |
163 | 167 |
164 public boolean isInvalid() throws HgRemoteConnectionException { | 168 public boolean isInvalid() throws HgRemoteConnectionException { |
165 if (remoteCapabilities == null) { | 169 initCapabilities(); |
166 remoteCapabilities = new HashSet<String>(); | |
167 // say hello to server, check response | |
168 try { | |
169 URL u = new URL(url, url.getPath() + "?cmd=hello"); | |
170 HttpURLConnection c = setupConnection(u.openConnection()); | |
171 c.connect(); | |
172 if (debug) { | |
173 dumpResponseHeader(u, c); | |
174 } | |
175 BufferedReader r = new BufferedReader(new InputStreamReader(c.getInputStream(), "US-ASCII")); | |
176 String line = r.readLine(); | |
177 c.disconnect(); | |
178 final String capsPrefix = "capabilities:"; | |
179 if (line == null || !line.startsWith(capsPrefix)) { | |
180 // for whatever reason, some servers do not respond to hello command (e.g. svnkit) | |
181 // but respond to 'capabilities' instead. Try it. | |
182 // TODO [post-1.0] tests needed | |
183 u = new URL(url, url.getPath() + "?cmd=capabilities"); | |
184 c = setupConnection(u.openConnection()); | |
185 c.connect(); | |
186 if (debug) { | |
187 dumpResponseHeader(u, c); | |
188 } | |
189 r = new BufferedReader(new InputStreamReader(c.getInputStream(), "US-ASCII")); | |
190 line = r.readLine(); | |
191 c.disconnect(); | |
192 if (line == null || line.trim().length() == 0) { | |
193 return true; | |
194 } | |
195 } else { | |
196 line = line.substring(capsPrefix.length()).trim(); | |
197 } | |
198 String[] caps = line.split("\\s"); | |
199 remoteCapabilities.addAll(Arrays.asList(caps)); | |
200 c.disconnect(); | |
201 } catch (MalformedURLException ex) { | |
202 throw new HgRemoteConnectionException("Bad URL", ex).setRemoteCommand("hello").setServerInfo(getLocation()); | |
203 } catch (IOException ex) { | |
204 throw new HgRemoteConnectionException("Communication failure", ex).setRemoteCommand("hello").setServerInfo(getLocation()); | |
205 } | |
206 } | |
207 return remoteCapabilities.isEmpty(); | 170 return remoteCapabilities.isEmpty(); |
208 } | 171 } |
209 | 172 |
210 /** | 173 /** |
211 * @return human-readable address of the server, without user credentials or any other security information | 174 * @return human-readable address of the server, without user credentials or any other security information |
444 c.disconnect(); | 407 c.disconnect(); |
445 } | 408 } |
446 } | 409 } |
447 } | 410 } |
448 | 411 |
449 public void unbundle(HgBundle bundle, List<Nodeid> heads) throws HgRemoteConnectionException, HgRuntimeException { | 412 public void unbundle(HgBundle bundle, List<Nodeid> remoteHeads) throws HgRemoteConnectionException, HgRuntimeException { |
450 if (heads == null) { | 413 if (remoteHeads == null) { |
451 // TODO collect heads from bundle: | 414 // TODO collect heads from bundle: |
452 // bundle.inspectChangelog(new HeadCollector(for each c : if collected has c.p1 or c.p2, remove them. Add c)) | 415 // bundle.inspectChangelog(new HeadCollector(for each c : if collected has c.p1 or c.p2, remove them. Add c)) |
416 // or get from remote server??? | |
453 throw Internals.notImplemented(); | 417 throw Internals.notImplemented(); |
454 } | 418 } |
455 StringBuilder sb = appendNodeidListArgument("heads", heads, null); | 419 StringBuilder sb = appendNodeidListArgument("heads", remoteHeads, null); |
456 | 420 |
457 HttpURLConnection c = null; | 421 HttpURLConnection c = null; |
458 DataSerializer.DataSource bundleData = bundle.new BundleSerializer(); | 422 DataSerializer.DataSource bundleData = bundle.new BundleSerializer(); |
459 try { | 423 try { |
460 URL u = new URL(url, url.getPath() + "?cmd=unbundle&" + sb.toString()); | 424 URL u = new URL(url, url.getPath() + "?cmd=unbundle&" + sb.toString()); |
470 os.close(); | 434 os.close(); |
471 if (debug) { | 435 if (debug) { |
472 dumpResponseHeader(u, c); | 436 dumpResponseHeader(u, c); |
473 dumpResponse(c); | 437 dumpResponse(c); |
474 } | 438 } |
475 if (c.getResponseCode() != 200) { | 439 checkResponseOk(c, "Push", "unbundle"); |
476 String m = c.getResponseMessage() == null ? "unknown reason" : c.getResponseMessage(); | |
477 String em = String.format("Push failed: %s (HTTP error:%d)", m, c.getResponseCode()); | |
478 throw new HgRemoteConnectionException(em).setRemoteCommand("unbundle").setServerInfo(getLocation()); | |
479 } | |
480 } catch (MalformedURLException ex) { | 440 } catch (MalformedURLException ex) { |
481 throw new HgRemoteConnectionException("Bad URL", ex).setRemoteCommand("unbundle").setServerInfo(getLocation()); | 441 throw new HgRemoteConnectionException("Bad URL", ex).setRemoteCommand("unbundle").setServerInfo(getLocation()); |
482 } catch (IOException ex) { | 442 } catch (IOException ex) { |
483 throw new HgRemoteConnectionException("Communication failure", ex).setRemoteCommand("unbundle").setServerInfo(getLocation()); | 443 throw new HgRemoteConnectionException("Communication failure", ex).setRemoteCommand("unbundle").setServerInfo(getLocation()); |
484 } catch (HgIOException ex) { | 444 } catch (HgIOException ex) { |
488 c.disconnect(); | 448 c.disconnect(); |
489 } | 449 } |
490 } | 450 } |
491 } | 451 } |
492 | 452 |
453 public List<Pair<String,Nodeid>> bookmarks() throws HgRemoteConnectionException, HgRuntimeException { | |
454 final String actionName = "Get remote bookmarks"; | |
455 final List<Pair<String, String>> values = listkeys("bookmarks", actionName); | |
456 ArrayList<Pair<String, Nodeid>> rv = new ArrayList<Pair<String, Nodeid>>(); | |
457 for (Pair<String, String> l : values) { | |
458 if (l.second().length() != Nodeid.SIZE_ASCII) { | |
459 sessionContext.getLog().dump(getClass(), Severity.Warn, "%s: bad nodeid '%s', ignored", actionName, l.second()); | |
460 continue; | |
461 } | |
462 Nodeid n = Nodeid.fromAscii(l.second()); | |
463 String bm = new String(l.first()); | |
464 rv.add(new Pair<String, Nodeid>(bm, n)); | |
465 } | |
466 return rv; | |
467 } | |
468 | |
469 public void updateBookmark(String name, Nodeid oldRev, Nodeid newRev) throws HgRemoteConnectionException, HgRuntimeException { | |
470 final String namespace = "bookmarks"; | |
471 HttpURLConnection c = null; | |
472 try { | |
473 URL u = new URL(url, String.format("%s?cmd=pushkey&namespace=%s&key=%s&old=%s&new=%s",url.getPath(), namespace, name, oldRev.toString(), newRev.toString())); | |
474 c = setupConnection(u.openConnection()); | |
475 c.connect(); | |
476 if (debug) { | |
477 dumpResponseHeader(u, c); | |
478 } | |
479 checkResponseOk(c, "Update remote bookmark", "pushkey"); | |
480 } catch (MalformedURLException ex) { | |
481 throw new HgRemoteConnectionException("Bad URL", ex).setRemoteCommand("pushkey").setServerInfo(getLocation()); | |
482 } catch (IOException ex) { | |
483 throw new HgRemoteConnectionException("Communication failure", ex).setRemoteCommand("pushkey").setServerInfo(getLocation()); | |
484 } finally { | |
485 if (c != null) { | |
486 c.disconnect(); | |
487 } | |
488 } | |
489 } | |
490 | |
491 private void phases() throws HgRemoteConnectionException, HgRuntimeException { | |
492 final List<Pair<String, String>> values = listkeys("phases", "Get remote phases"); | |
493 for (Pair<String, String> l : values) { | |
494 System.out.printf("%s : %s\n", l.first(), l.second()); | |
495 } | |
496 } | |
497 | |
498 public static void main(String[] args) throws Exception { | |
499 final HgRemoteRepository r = new HgLookup().detectRemote("http://selenic.com/hg", null); | |
500 if (r.isInvalid()) { | |
501 return; | |
502 } | |
503 System.out.println(r.remoteCapabilities); | |
504 r.phases(); | |
505 final List<Pair<String, Nodeid>> bm = r.bookmarks(); | |
506 for (Pair<String, Nodeid> pair : bm) { | |
507 System.out.println(pair); | |
508 } | |
509 } | |
510 | |
493 @Override | 511 @Override |
494 public String toString() { | 512 public String toString() { |
495 return getClass().getSimpleName() + '[' + getLocation() + ']'; | 513 return getClass().getSimpleName() + '[' + getLocation() + ']'; |
496 } | 514 } |
515 | |
516 | |
517 private void initCapabilities() throws HgRemoteConnectionException { | |
518 if (remoteCapabilities == null) { | |
519 remoteCapabilities = new HashSet<String>(); | |
520 // say hello to server, check response | |
521 try { | |
522 URL u = new URL(url, url.getPath() + "?cmd=hello"); | |
523 HttpURLConnection c = setupConnection(u.openConnection()); | |
524 c.connect(); | |
525 if (debug) { | |
526 dumpResponseHeader(u, c); | |
527 } | |
528 BufferedReader r = new BufferedReader(new InputStreamReader(c.getInputStream(), "US-ASCII")); | |
529 String line = r.readLine(); | |
530 c.disconnect(); | |
531 final String capsPrefix = "capabilities:"; | |
532 if (line == null || !line.startsWith(capsPrefix)) { | |
533 // for whatever reason, some servers do not respond to hello command (e.g. svnkit) | |
534 // but respond to 'capabilities' instead. Try it. | |
535 // TODO [post-1.0] tests needed | |
536 u = new URL(url, url.getPath() + "?cmd=capabilities"); | |
537 c = setupConnection(u.openConnection()); | |
538 c.connect(); | |
539 if (debug) { | |
540 dumpResponseHeader(u, c); | |
541 } | |
542 r = new BufferedReader(new InputStreamReader(c.getInputStream(), "US-ASCII")); | |
543 line = r.readLine(); | |
544 c.disconnect(); | |
545 if (line == null || line.trim().length() == 0) { | |
546 return; | |
547 } | |
548 } else { | |
549 line = line.substring(capsPrefix.length()).trim(); | |
550 } | |
551 String[] caps = line.split("\\s"); | |
552 remoteCapabilities.addAll(Arrays.asList(caps)); | |
553 c.disconnect(); | |
554 } catch (MalformedURLException ex) { | |
555 throw new HgRemoteConnectionException("Bad URL", ex).setRemoteCommand("hello").setServerInfo(getLocation()); | |
556 } catch (IOException ex) { | |
557 throw new HgRemoteConnectionException("Communication failure", ex).setRemoteCommand("hello").setServerInfo(getLocation()); | |
558 } | |
559 } | |
560 } | |
497 | 561 |
498 private HgLookup getLookupHelper() { | 562 private HgLookup getLookupHelper() { |
499 if (lookupHelper == null) { | 563 if (lookupHelper == null) { |
500 lookupHelper = new HgLookup(sessionContext); | 564 lookupHelper = new HgLookup(sessionContext); |
501 } | 565 } |
502 return lookupHelper; | 566 return lookupHelper; |
503 } | 567 } |
504 | 568 |
569 private List<Pair<String,String>> listkeys(String namespace, String actionName) throws HgRemoteConnectionException, HgRuntimeException { | |
570 HttpURLConnection c = null; | |
571 try { | |
572 URL u = new URL(url, url.getPath() + "?cmd=listkeys&namespace=" + namespace); | |
573 c = setupConnection(u.openConnection()); | |
574 c.connect(); | |
575 if (debug) { | |
576 dumpResponseHeader(u, c); | |
577 } | |
578 checkResponseOk(c, actionName, "listkeys"); | |
579 ArrayList<Pair<String, String>> rv = new ArrayList<Pair<String, String>>(); | |
580 BufferedReader r = new BufferedReader(new InputStreamReader(c.getInputStream(), EncodingHelper.getUTF8())); | |
581 String l; | |
582 while ((l = r.readLine()) != null) { | |
583 int sep = l.indexOf('\t'); | |
584 if (sep == -1) { | |
585 sessionContext.getLog().dump(getClass(), Severity.Warn, "%s: bad line '%s', ignored", actionName, l); | |
586 continue; | |
587 } | |
588 rv.add(new Pair<String,String>(l.substring(0, sep), l.substring(sep+1))); | |
589 } | |
590 r.close(); | |
591 return rv; | |
592 } catch (MalformedURLException ex) { | |
593 throw new HgRemoteConnectionException("Bad URL", ex).setRemoteCommand("listkeys").setServerInfo(getLocation()); | |
594 } catch (IOException ex) { | |
595 throw new HgRemoteConnectionException("Communication failure", ex).setRemoteCommand("listkeys").setServerInfo(getLocation()); | |
596 } finally { | |
597 if (c != null) { | |
598 c.disconnect(); | |
599 } | |
600 } | |
601 } | |
602 | |
603 private void checkResponseOk(HttpURLConnection c, String opName, String remoteCmd) throws HgRemoteConnectionException, IOException { | |
604 if (c.getResponseCode() != 200) { | |
605 String m = c.getResponseMessage() == null ? "unknown reason" : c.getResponseMessage(); | |
606 String em = String.format("%s failed: %s (HTTP error:%d)", opName, m, c.getResponseCode()); | |
607 throw new HgRemoteConnectionException(em).setRemoteCommand(remoteCmd).setServerInfo(getLocation()); | |
608 } | |
609 } | |
610 | |
505 private HttpURLConnection setupConnection(URLConnection urlConnection) { | 611 private HttpURLConnection setupConnection(URLConnection urlConnection) { |
506 urlConnection.setRequestProperty("User-Agent", "hg4j/1.0.0"); | 612 urlConnection.setRequestProperty("User-Agent", "hg4j/1.0.0"); |
507 urlConnection.addRequestProperty("Accept", "application/mercurial-0.1"); | 613 urlConnection.addRequestProperty("Accept", "application/mercurial-0.1"); |
508 if (authInfo != null) { | 614 if (authInfo != null) { |
509 urlConnection.addRequestProperty("Authorization", "Basic " + authInfo); | 615 urlConnection.addRequestProperty("Authorization", "Basic " + authInfo); |
547 } | 653 } |
548 } | 654 } |
549 | 655 |
550 private static File writeBundle(InputStream is, boolean decompress, String header) throws IOException { | 656 private static File writeBundle(InputStream is, boolean decompress, String header) throws IOException { |
551 InputStream zipStream = decompress ? new InflaterInputStream(is) : is; | 657 InputStream zipStream = decompress ? new InflaterInputStream(is) : is; |
552 File tf = File.createTempFile("hg-bundle-", null); | 658 File tf = File.createTempFile("hg4j-bundle-", null); |
553 FileOutputStream fos = new FileOutputStream(tf); | 659 FileOutputStream fos = new FileOutputStream(tf); |
554 fos.write(header.getBytes()); | 660 fos.write(header.getBytes()); |
555 int r; | 661 int r; |
556 byte[] buf = new byte[8*1024]; | 662 byte[] buf = new byte[8*1024]; |
557 while ((r = zipStream.read(buf)) != -1) { | 663 while ((r = zipStream.read(buf)) != -1) { |