Mercurial > hg4j
comparison src/org/tmatesoft/hg/repo/HgRemoteRepository.java @ 177:e10225daface
Use POST for long between queries. Batch between queries (pass multiple pairs to a server) to minimize number thereof
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Sat, 02 Apr 2011 23:05:28 +0200 |
parents | a8df7162ec75 |
children | 62665d8f0686 |
comparison
equal
deleted
inserted
replaced
176:a8df7162ec75 | 177:e10225daface |
---|---|
16 */ | 16 */ |
17 package org.tmatesoft.hg.repo; | 17 package org.tmatesoft.hg.repo; |
18 | 18 |
19 import java.io.File; | 19 import java.io.File; |
20 import java.io.IOException; | 20 import java.io.IOException; |
21 import java.io.InputStream; | 21 import java.io.InputStreamReader; |
22 import java.io.OutputStream; | |
22 import java.io.StreamTokenizer; | 23 import java.io.StreamTokenizer; |
24 import java.net.HttpURLConnection; | |
23 import java.net.MalformedURLException; | 25 import java.net.MalformedURLException; |
24 import java.net.URL; | 26 import java.net.URL; |
25 import java.net.URLConnection; | 27 import java.net.URLConnection; |
26 import java.security.cert.CertificateException; | 28 import java.security.cert.CertificateException; |
27 import java.security.cert.X509Certificate; | 29 import java.security.cert.X509Certificate; |
28 import java.util.Collection; | 30 import java.util.Collection; |
29 import java.util.Collections; | 31 import java.util.Collections; |
32 import java.util.Iterator; | |
30 import java.util.LinkedHashMap; | 33 import java.util.LinkedHashMap; |
31 import java.util.LinkedList; | 34 import java.util.LinkedList; |
32 import java.util.List; | 35 import java.util.List; |
33 import java.util.Map; | 36 import java.util.Map; |
34 import java.util.prefs.BackingStoreException; | 37 import java.util.prefs.BackingStoreException; |
37 import javax.net.ssl.HttpsURLConnection; | 40 import javax.net.ssl.HttpsURLConnection; |
38 import javax.net.ssl.SSLContext; | 41 import javax.net.ssl.SSLContext; |
39 import javax.net.ssl.TrustManager; | 42 import javax.net.ssl.TrustManager; |
40 import javax.net.ssl.X509TrustManager; | 43 import javax.net.ssl.X509TrustManager; |
41 | 44 |
45 import org.tmatesoft.hg.core.HgBadStateException; | |
42 import org.tmatesoft.hg.core.HgException; | 46 import org.tmatesoft.hg.core.HgException; |
43 import org.tmatesoft.hg.core.Nodeid; | 47 import org.tmatesoft.hg.core.Nodeid; |
44 | 48 |
45 /** | 49 /** |
46 * WORK IN PROGRESS, DO NOT USE | 50 * WORK IN PROGRESS, DO NOT USE |
104 return Collections.singletonList(Nodeid.fromAscii("71ddbf8603e8e09d54ac9c5fe4bb5ae824589f1d")); | 108 return Collections.singletonList(Nodeid.fromAscii("71ddbf8603e8e09d54ac9c5fe4bb5ae824589f1d")); |
105 // return Collections.emptyList(); | 109 // return Collections.emptyList(); |
106 } | 110 } |
107 | 111 |
108 public List<Nodeid> between(Nodeid tip, Nodeid base) throws HgException { | 112 public List<Nodeid> between(Nodeid tip, Nodeid base) throws HgException { |
113 Range r = new Range(base, tip); | |
114 // XXX shall handle errors like no range key in the returned map, not sure how. | |
115 return between(Collections.singletonList(r)).get(r); | |
116 } | |
117 | |
118 /** | |
119 * @param ranges | |
120 * @return map, where keys are input instances, values are corresponding server reply | |
121 * @throws HgException | |
122 */ | |
123 public Map<Range, List<Nodeid>> between(Collection<Range> ranges) throws HgException { | |
124 if (ranges.isEmpty()) { | |
125 return Collections.emptyMap(); | |
126 } | |
127 // if fact, shall do other way round, this method shall send | |
128 LinkedHashMap<Range, List<Nodeid>> rv = new LinkedHashMap<HgRemoteRepository.Range, List<Nodeid>>(ranges.size() * 4 / 3); | |
129 StringBuilder sb = new StringBuilder(20 + ranges.size() * 82); | |
130 sb.append("pairs="); | |
131 for (Range r : ranges) { | |
132 sb.append(r.end.toString()); | |
133 sb.append('-'); | |
134 sb.append(r.start.toString()); | |
135 sb.append('+'); | |
136 } | |
137 if (sb.charAt(sb.length() - 1) == '+') { | |
138 // strip last space | |
139 sb.setLength(sb.length() - 1); | |
140 } | |
109 try { | 141 try { |
110 LinkedList<Nodeid> rv = new LinkedList<Nodeid>(); | 142 boolean usePOST = ranges.size() > 3; |
111 URL u = new URL(url, url.getPath() + "?cmd=between&pairs=" + tip.toString() + '-' + base.toString()); | 143 URL u = new URL(url, url.getPath() + "?cmd=between" + (usePOST ? "" : '&' + sb.toString())); |
112 URLConnection c = setupConnection(u.openConnection()); | 144 HttpURLConnection c = setupConnection(u.openConnection()); |
113 c.connect(); | 145 if (usePOST) { |
114 System.out.println("Query:" + u.getQuery()); | 146 c.setRequestMethod("POST"); |
147 c.setRequestProperty("Content-Length", String.valueOf(sb.length()/*nodeids are ASCII, bytes == characters */)); | |
148 c.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); | |
149 c.setDoOutput(true); | |
150 c.connect(); | |
151 OutputStream os = c.getOutputStream(); | |
152 os.write(sb.toString().getBytes()); | |
153 os.flush(); | |
154 os.close(); | |
155 } else { | |
156 c.connect(); | |
157 } | |
158 System.out.printf("%d ranges, method:%s \n", ranges.size(), c.getRequestMethod()); | |
159 System.out.printf("Query (%d bytes):%s\n", u.getQuery().length(), u.getQuery()); | |
115 System.out.println("Response headers:"); | 160 System.out.println("Response headers:"); |
116 final Map<String, List<String>> headerFields = c.getHeaderFields(); | 161 final Map<String, List<String>> headerFields = c.getHeaderFields(); |
117 for (String s : headerFields.keySet()) { | 162 for (String s : headerFields.keySet()) { |
118 System.out.printf("%s: %s\n", s, c.getHeaderField(s)); | 163 System.out.printf("%s: %s\n", s, c.getHeaderField(s)); |
119 } | 164 } |
120 InputStream is = c.getInputStream(); | 165 InputStreamReader is = new InputStreamReader(c.getInputStream(), "US-ASCII"); |
121 StreamTokenizer st = new StreamTokenizer(is); | 166 StreamTokenizer st = new StreamTokenizer(is); |
122 st.ordinaryChars('0', '9'); | 167 st.ordinaryChars('0', '9'); |
123 st.wordChars('0', '9'); | 168 st.wordChars('0', '9'); |
169 st.eolIsSignificant(true); | |
170 Iterator<Range> rangeItr = ranges.iterator(); | |
171 LinkedList<Nodeid> currRangeList = null; | |
172 Range currRange = null; | |
173 boolean possiblyEmptyNextLine = true; | |
124 while (st.nextToken() != StreamTokenizer.TT_EOF) { | 174 while (st.nextToken() != StreamTokenizer.TT_EOF) { |
125 System.out.println(st.sval); | 175 if (st.ttype == StreamTokenizer.TT_EOL) { |
126 Nodeid nid = Nodeid.fromAscii(st.sval); | 176 if (possiblyEmptyNextLine) { |
127 rv.addLast(nid); | 177 // newline follows newline; |
178 assert currRange == null; | |
179 assert currRangeList == null; | |
180 if (!rangeItr.hasNext()) { | |
181 throw new HgBadStateException(); | |
182 } | |
183 rv.put(rangeItr.next(), Collections.<Nodeid>emptyList()); | |
184 } else { | |
185 if (currRange == null || currRangeList == null) { | |
186 throw new HgBadStateException(); | |
187 } | |
188 // indicate next range value is needed | |
189 currRange = null; | |
190 currRangeList = null; | |
191 possiblyEmptyNextLine = true; | |
192 } | |
193 } else { | |
194 possiblyEmptyNextLine = false; | |
195 if (currRange == null) { | |
196 if (!rangeItr.hasNext()) { | |
197 throw new HgBadStateException(); | |
198 } | |
199 currRange = rangeItr.next(); | |
200 currRangeList = new LinkedList<Nodeid>(); | |
201 rv.put(currRange, currRangeList); | |
202 } | |
203 Nodeid nid = Nodeid.fromAscii(st.sval); | |
204 currRangeList.addLast(nid); | |
205 } | |
128 } | 206 } |
129 is.close(); | 207 is.close(); |
130 return rv; | 208 return rv; |
131 } catch (MalformedURLException ex) { | 209 } catch (MalformedURLException ex) { |
132 throw new HgException(ex); | 210 throw new HgException(ex); |
133 } catch (IOException ex) { | 211 } catch (IOException ex) { |
134 throw new HgException(ex); | 212 throw new HgException(ex); |
135 } | 213 } |
136 } | 214 } |
137 | 215 |
138 /** | |
139 * @param ranges | |
140 * @return map, where keys are input instances, values are corresponding server reply | |
141 * @throws HgException | |
142 */ | |
143 public Map<Range, List<Nodeid>> between(Collection<Range> ranges) throws HgException { | |
144 // if fact, shall do other way round, this method shall send | |
145 LinkedHashMap<Range, List<Nodeid>> rv = new LinkedHashMap<HgRemoteRepository.Range, List<Nodeid>>(ranges.size() * 4 / 3); | |
146 for (Range r : ranges) { | |
147 List<Nodeid> between = between(r.end, r.start); | |
148 rv.put(r, between); | |
149 } | |
150 return rv; | |
151 } | |
152 | |
153 public List<RemoteBranch> branches(List<Nodeid> nodes) { | 216 public List<RemoteBranch> branches(List<Nodeid> nodes) { |
154 return Collections.emptyList(); | 217 return Collections.emptyList(); |
155 } | 218 } |
156 | 219 |
157 // WireProtocol wiki: roots = a list of the latest nodes on every service side changeset branch that both the client and server know about. | 220 // WireProtocol wiki: roots = a list of the latest nodes on every service side changeset branch that both the client and server know about. |
158 public HgBundle getChanges(List<Nodeid> roots) throws HgException { | 221 public HgBundle getChanges(List<Nodeid> roots) throws HgException { |
159 return new HgLookup().loadBundle(new File("/temp/hg/hg-bundle-000000000000-gz.tmp")); | 222 return new HgLookup().loadBundle(new File("/temp/hg/hg-bundle-000000000000-gz.tmp")); |
160 } | 223 } |
161 | 224 |
162 private URLConnection setupConnection(URLConnection urlConnection) { | 225 private HttpURLConnection setupConnection(URLConnection urlConnection) { |
163 urlConnection.addRequestProperty("User-Agent", "hg4j/0.5.0"); | 226 urlConnection.setRequestProperty("User-Agent", "hg4j/0.5.0"); |
164 urlConnection.addRequestProperty("Accept", "application/mercurial-0.1"); | 227 urlConnection.addRequestProperty("Accept", "application/mercurial-0.1"); |
165 if (authInfo != null) { | 228 if (authInfo != null) { |
166 urlConnection.addRequestProperty("Authorization", "Basic " + authInfo); | 229 urlConnection.addRequestProperty("Authorization", "Basic " + authInfo); |
167 } | 230 } |
168 if (sslContext != null) { | 231 if (sslContext != null) { |
169 ((HttpsURLConnection) urlConnection).setSSLSocketFactory(sslContext.getSocketFactory()); | 232 ((HttpsURLConnection) urlConnection).setSSLSocketFactory(sslContext.getSocketFactory()); |
170 } | 233 } |
171 return urlConnection; | 234 return (HttpURLConnection) urlConnection; |
172 } | 235 } |
173 | 236 |
174 public static final class Range { | 237 public static final class Range { |
175 /** | 238 /** |
176 * Root of the range, earlier revision | 239 * Root of the range, earlier revision |