30
30
import com .cloudant .sync .internal .documentstore .MultipartAttachmentWriter ;
31
31
import com .cloudant .sync .internal .util .JSONUtils ;
32
32
import com .cloudant .sync .internal .util .Misc ;
33
+ import com .cloudant .sync .replication .PullFilter ;
33
34
import com .fasterxml .jackson .core .type .TypeReference ;
34
35
35
36
import org .apache .commons .io .IOUtils ;
51
52
import java .util .logging .Level ;
52
53
import java .util .logging .Logger ;
53
54
54
- public class CouchClient {
55
+ public class CouchClient {
55
56
56
57
private CouchURIHelper uriHelper ;
57
58
private List <HttpConnectionRequestInterceptor > requestInterceptors ;
@@ -65,11 +66,11 @@ public CouchClient(URI rootUri,
65
66
this .requestInterceptors = new ArrayList <HttpConnectionRequestInterceptor >();
66
67
this .responseInterceptors = new ArrayList <HttpConnectionResponseInterceptor >();
67
68
68
- if (requestInterceptors != null ) {
69
+ if (requestInterceptors != null ) {
69
70
this .requestInterceptors .addAll (requestInterceptors );
70
71
}
71
72
72
- if (responseInterceptors != null ) {
73
+ if (responseInterceptors != null ) {
73
74
this .responseInterceptors .addAll (responseInterceptors );
74
75
}
75
76
}
@@ -82,16 +83,14 @@ public URI getRootUri() {
82
83
// - stream non-null and exception null: the call was successful, result in stream
83
84
// - stream null and exception non-null: the call was unsuccessful, details in exception
84
85
// - fatal: set to true when exception non-null, indicates call should not be retried
85
- private static class ExecuteResult
86
- {
86
+ private static class ExecuteResult {
87
87
private ExecuteResult (InputStream stream ,
88
88
InputStream errorStream ,
89
89
int responseCode ,
90
90
String responseMessage ,
91
- Throwable cause )
92
- {
91
+ Throwable cause ) {
93
92
boolean needsCouchException = false ;
94
- switch (responseCode / 100 ) {
93
+ switch (responseCode / 100 ) {
95
94
case 1 :
96
95
case 2 :
97
96
// 1xx and 2xx are OK
@@ -103,7 +102,8 @@ private ExecuteResult(InputStream stream,
103
102
break ;
104
103
case 3 :
105
104
// 3xx redirection
106
- throw new CouchException ("Unexpected redirection (3xx) code encountered" , responseCode );
105
+ throw new CouchException ("Unexpected redirection (3xx) code encountered" ,
106
+ responseCode );
107
107
case 4 :
108
108
// 4xx errors normally mean we are not authenticated so we shouldn't retry
109
109
this .fatal = true ;
@@ -135,7 +135,8 @@ private ExecuteResult(InputStream stream,
135
135
ce .setReason (json .get ("reason" ));
136
136
this .exception = ce ;
137
137
} catch (Exception e ) {
138
- CouchException ce = new CouchException ("Error deserializing server response" , cause ,
138
+ CouchException ce = new CouchException ("Error deserializing server response" ,
139
+ cause ,
139
140
responseCode );
140
141
this .exception = ce ;
141
142
}
@@ -152,7 +153,8 @@ private ExecuteResult(InputStream stream,
152
153
// - if there's a couch error returned as json, un-marshall and throw
153
154
// - anything else, just throw the IOException back, use the cause part of the exception?
154
155
155
- // it needs to catch eg FileNotFoundException and rethrow to emulate the previous exception handling behaviour
156
+ // it needs to catch eg FileNotFoundException and rethrow to emulate the previous exception
157
+ // handling behaviour
156
158
private ExecuteResult execute (HttpConnection connection ) {
157
159
158
160
InputStream inputStream = null ; // input stream - response from server on success
@@ -248,7 +250,7 @@ private <T> T executeToJsonObjectWithRetry(final HttpConnection connection,
248
250
}
249
251
250
252
private <T > T executeWithRetry (final HttpConnection connection ,
251
- InputStreamProcessor <T > processor ) throws
253
+ InputStreamProcessor <T > processor ) throws
252
254
CouchException {
253
255
// all CouchClient requests want to receive application/json responses
254
256
connection .requestProperties .put ("Accept" , "application/json" );
@@ -284,33 +286,53 @@ private Map<String, Object> getDefaultChangeFeedOptions() {
284
286
return options ;
285
287
}
286
288
287
- public ChangesResult changes (Object since , Integer limit ) {
288
- return this .changes (null , null , since , limit );
289
- }
290
-
291
- public ChangesResult changes (String filterName , Map <String , String > filterParameters , Object since , Integer limit ) {
289
+ private Map <String , Object > getParametrizedChangeFeedOptions (Object since , Integer limit ) {
292
290
Map <String , Object > options = getDefaultChangeFeedOptions ();
293
- if (filterName != null ) {
294
- options .put ("filter" , filterName );
295
- if (filterParameters != null ) {
296
- options .putAll (filterParameters );
297
- }
298
- }
299
- if (since != null ) {
291
+ if (since != null ) {
300
292
options .put ("since" , since );
301
293
}
302
294
if (limit != null ) {
303
295
options .put ("limit" , limit );
304
296
}
305
297
// seq_interval: improve performance and reduce load on the remote database
306
- if (limit != null ) {
298
+ if (limit != null ) {
307
299
options .put ("seq_interval" , limit );
308
300
} else {
309
301
options .put ("seq_interval" , 1000 );
310
302
}
303
+ return options ;
304
+ }
305
+
306
+ public ChangesResult changes (Object since , Integer limit ) {
307
+ Map <String , Object > options = getParametrizedChangeFeedOptions (since , limit );
311
308
return this .changes (options );
312
309
}
313
310
311
+ public ChangesResult changes (PullFilter filter , Object since , Integer limit ) {
312
+ Map <String , Object > options = getParametrizedChangeFeedOptions (since , limit );
313
+ if (filter != null ) {
314
+ String filterName = filter .getName ();
315
+ Map filterParameters = filter .getParameters ();
316
+ if (filterName != null ) {
317
+ options .put ("filter" , filterName );
318
+ if (filterParameters != null ) {
319
+ options .putAll (filterParameters );
320
+ }
321
+ }
322
+ }
323
+ return this .changes (options );
324
+ }
325
+
326
+ public ChangesResult changes (String selector , Object since , Integer limit ) {
327
+ Misc .checkNotNullOrEmpty (selector ,null );
328
+ Map <String , Object > options = getParametrizedChangeFeedOptions (since , limit );
329
+ options .put ("filter" , "_selector" );
330
+ URI changesFeedUri = uriHelper .changesUri (options );
331
+ HttpConnection connection = Http .POST (changesFeedUri , "application/json" );
332
+ connection .setRequestBody (selector );
333
+ return executeToJsonObjectWithRetry (connection , ChangesResult .class );
334
+ }
335
+
314
336
public ChangesResult changes (final Map <String , Object > options ) {
315
337
URI changesFeedUri = uriHelper .changesUri (options );
316
338
HttpConnection connection = Http .GET (changesFeedUri );
@@ -349,7 +371,8 @@ public Response create(Object document) {
349
371
}
350
372
}
351
373
352
- public <T > T processAttachmentStream (String id , String rev , String attachmentName , final boolean acceptGzip , InputStreamProcessor <T > processor ) {
374
+ public <T > T processAttachmentStream (String id , String rev , String attachmentName , final
375
+ boolean acceptGzip , InputStreamProcessor <T > processor ) {
353
376
Misc .checkNotNullOrEmpty (id , "id" );
354
377
Misc .checkNotNullOrEmpty (rev , "rev" );
355
378
Map <String , Object > queries = new HashMap <String , Object >();
@@ -362,7 +385,8 @@ public <T> T processAttachmentStream(String id, String rev, String attachmentNam
362
385
return executeWithRetry (connection , processor );
363
386
}
364
387
365
- public void putAttachmentStream (String id , String rev , String attachmentName , String contentType , byte [] attachmentData ) {
388
+ public void putAttachmentStream (String id , String rev , String attachmentName , String
389
+ contentType , byte [] attachmentData ) {
366
390
Misc .checkNotNullOrEmpty (id , "id" );
367
391
Misc .checkNotNullOrEmpty (rev , "rev" );
368
392
Map <String , Object > queries = new HashMap <String , Object >();
@@ -387,18 +411,18 @@ public void putAttachmentStream(String id, String rev, String attachmentName, St
387
411
* "2-65ddd7d56da84f25af544e84a3267ccf" ]
388
412
* }
389
413
*/
390
- public Map <String ,Object > getDocConflictRevs (String id ) {
414
+ public Map <String , Object > getDocConflictRevs (String id ) {
391
415
Map <String , Object > options = new HashMap <String , Object >();
392
416
options .put ("conflicts" , true );
393
417
return this .getDocument (id , options , JSONUtils .STRING_MAP_TYPE_DEF );
394
418
}
395
419
396
420
/**
397
- * Convenience method to get document with revision history for a given list of open revisions. It does that by
421
+ * Convenience method to get document with revision history for a given list of open
422
+ * revisions. It does that by
398
423
* adding "open_revs=["rev1", "rev2"]" option to the GET request.
399
424
*
400
425
* It must return a list because that is how CouchDB return its results.
401
- *
402
426
*/
403
427
public List <OpenRevision > getDocWithOpenRevisions (String id , Collection <String > revisions ,
404
428
Collection <String > attsSince ,
@@ -433,7 +457,8 @@ public List<OpenRevision> getDocWithOpenRevisions(String id, Collection<String>
433
457
* Each time the iterator is advanced, a DocumentRevsList is returned, which represents the
434
458
* leaf nodes and their ancestries for a given document ID.
435
459
* </p>
436
- * @param request A request for 1 or more (ID,rev) pairs.
460
+ *
461
+ * @param request A request for 1 or more (ID,rev) pairs.
437
462
* @param pullAttachmentsInline If true, retrieve attachments as inline base64
438
463
* @return An iterator representing the result of calling the _bulk_docs endpoint.
439
464
*/
@@ -458,7 +483,8 @@ public Iterable<DocumentRevsList> bulkReadDocsWithOpenRevisions(List<BulkGetRequ
458
483
// deserialise response
459
484
BulkGetResponse response = executeToJsonObjectWithRetry (connection , BulkGetResponse .class );
460
485
461
- Map <String ,ArrayList <DocumentRevs >> revsMap = new HashMap <String ,ArrayList <DocumentRevs >>();
486
+ Map <String , ArrayList <DocumentRevs >> revsMap = new HashMap <String ,
487
+ ArrayList <DocumentRevs >>();
462
488
463
489
// merge results back in, so there is one list of DocumentRevs per ID
464
490
for (BulkGetResponse .Result result : response .results ) {
@@ -486,7 +512,7 @@ public Map<String, Object> getDocument(String id) {
486
512
return this .getDocument (id , new HashMap <String , Object >(), JSONUtils .STRING_MAP_TYPE_DEF );
487
513
}
488
514
489
- public <T > T getDocument (String id , final Class <T > type ) {
515
+ public <T > T getDocument (String id , final Class <T > type ) {
490
516
return this .getDocument (id , new HashMap <String , Object >(),
491
517
new CouchClientTypeReference <T >(type ));
492
518
}
@@ -527,10 +553,10 @@ public <T> T getDocument(String id, String rev, final Class<T> type) {
527
553
}
528
554
529
555
/**
530
- * Get document along with its revision history, and the result is converted to a <code>DocumentRevs</code> object.
556
+ * Get document along with its revision history, and the result is converted to a
557
+ * <code>DocumentRevs</code> object.
531
558
*
532
559
* @see <code>DocumentRevs</code>
533
- *
534
560
*/
535
561
public DocumentRevs getDocRevisions (String id , String rev ) {
536
562
return getDocRevisions (id , rev ,
@@ -579,7 +605,7 @@ public String getDocumentRev(String id) {
579
605
580
606
String rev = head .getConnection ().getHeaderField ("ETag" );
581
607
// Remove enclosing "" before returning
582
- return rev .substring (1 , rev .length ()- 1 );
608
+ return rev .substring (1 , rev .length () - 1 );
583
609
}
584
610
585
611
public Response delete (String id , String rev ) {
@@ -608,7 +634,8 @@ private List<Response> bulkCreateDocs(String payload) {
608
634
URI uri = this .uriHelper .bulkDocsUri ();
609
635
HttpConnection connection = Http .POST (uri , "application/json" );
610
636
connection .setRequestBody (payload );
611
- return executeToJsonObjectWithRetry (connection , new CouchClientTypeReference <List <Response >>());
637
+ return executeToJsonObjectWithRetry (connection , new
638
+ CouchClientTypeReference <List <Response >>());
612
639
}
613
640
614
641
/**
@@ -639,8 +666,8 @@ public List<Response> bulkCreateSerializedDocs(List<String> serializedDocs) {
639
666
private String generateBulkSerializedDocsPayload (List <String > serializedDocs ) {
640
667
String newEditsVal = "\" new_edits\" : false, " ;
641
668
StringBuilder sb = new StringBuilder ("[" );
642
- for (String doc : serializedDocs ) {
643
- if (sb .length () > 1 ) {
669
+ for (String doc : serializedDocs ) {
670
+ if (sb .length () > 1 ) {
644
671
sb .append (", " );
645
672
}
646
673
sb .append (doc );
@@ -671,7 +698,9 @@ private String generateBulkSerializedDocsPayload(List<String> serializedDocs) {
671
698
* If the ID has no missing revision, it should not appear in the Map's key set. If all IDs
672
699
* do not have missing revisions, the returned Map should be empty map, but never null.
673
700
*
674
- * @see <a target="_blank" href="http://wiki.apache.org/couchdb/HttpPostRevsDiff">HttpPostRevsDiff documentation</a>
701
+ * @see
702
+ * <a target="_blank" href="http://wiki.apache.org/couchdb/HttpPostRevsDiff">HttpPostRevsDiff
703
+ * documentation</a>
675
704
*/
676
705
public Map <String , MissingRevisions > revsDiff (Map <String , Set <String >> revisions ) {
677
706
Misc .checkNotNull (revisions , "Input revisions" );
@@ -735,7 +764,8 @@ private static class CouchClientTypeReference<T> extends TypeReference<T> {
735
764
CouchClientTypeReference () {
736
765
this (null );
737
766
}
738
- CouchClientTypeReference (Class <T > type ){
767
+
768
+ CouchClientTypeReference (Class <T > type ) {
739
769
this .type = type ;
740
770
}
741
771
@@ -753,6 +783,7 @@ public Type getType() {
753
783
private static final class TypeInputStreamProcessor <T > implements InputStreamProcessor <T > {
754
784
755
785
private final TypeReference <T > typeReference ;
786
+
756
787
TypeInputStreamProcessor (TypeReference <T > typeReference ) {
757
788
this .typeReference = typeReference ;
758
789
}
@@ -774,4 +805,4 @@ public Void processStream(InputStream stream) {
774
805
return null ;
775
806
}
776
807
}
777
- }
808
+ }
0 commit comments