Skip to content

Commit 9963960

Browse files
author
Hideki Itakura
authored
Merge pull request #1388 from couchbase/feature/post_replicate_issues
Fixed #1386 and andriod #939 : POST /_replicate to start multiple replicators and threading issues
2 parents 9c35f35 + 7e4f9cf commit 9963960

5 files changed

Lines changed: 142 additions & 198 deletions

File tree

src/main/java/com/couchbase/lite/Database.java

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2415,40 +2415,27 @@ private boolean postChangeNotifications() {
24152415

24162416
// Database+Replication
24172417

2418-
protected Replication getActiveReplicator(URL remote, boolean push) {
2418+
protected Replication findActiveReplicator(Replication replicator) {
24192419
synchronized (activeReplicators) {
2420-
try {
2421-
java.net.URI remoteUri = remote.toURI();
2422-
for (Replication replicator : activeReplicators) {
2423-
if (replicator.getRemoteUrl().toURI().equals(remoteUri) &&
2424-
replicator.isPull() == !push && replicator.isRunning())
2425-
return replicator;
2426-
}
2427-
} catch (java.net.URISyntaxException uriException) {
2428-
// Not possible since it would not be an active replicator.
2429-
// However, until we refactor everything to use java.net,
2430-
// I'm not sure we have a choice but to swallow this.
2431-
Log.e(Log.TAG_DATABASE, "Active replicator found with invalid URI", uriException);
2420+
String remoteCheckpointDocID = replicator.remoteCheckpointDocID();
2421+
if (remoteCheckpointDocID == null)
2422+
return null;
2423+
2424+
for (Replication r : activeReplicators) {
2425+
if (remoteCheckpointDocID.equals(r.remoteCheckpointDocID()) && r.isRunning())
2426+
return r;
24322427
}
24332428
}
24342429
return null;
24352430
}
24362431

2437-
protected Replication getReplicator(URL remote,
2438-
HttpClientFactory httpClientFactory,
2439-
boolean push,
2440-
boolean continuous) {
2441-
Replication result = getActiveReplicator(remote, push);
2442-
if (result != null) {
2443-
return result;
2444-
}
2445-
if (push) {
2446-
result = new Replication(this, remote, Replication.Direction.PUSH, httpClientFactory);
2447-
} else {
2448-
result = new Replication(this, remote, Replication.Direction.PULL, httpClientFactory);
2449-
}
2450-
result.setContinuous(continuous);
2451-
return result;
2432+
protected Replication createReplicator(URL remote, boolean push, HttpClientFactory factory) {
2433+
Replication replicator;
2434+
if (push)
2435+
replicator = new Replication(this, remote, Replication.Direction.PUSH, factory);
2436+
else
2437+
replicator = new Replication(this, remote, Replication.Direction.PULL, factory);
2438+
return replicator;
24522439
}
24532440

24542441
// Database+LocalDocs

src/main/java/com/couchbase/lite/Manager.java

Lines changed: 39 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -574,14 +574,6 @@ public synchronized Database getDatabase(String name, boolean mustExist) {
574574
*/
575575
@InterfaceAudience.Private
576576
public Replication getReplicator(Map<String, Object> properties) throws CouchbaseLiteException {
577-
578-
// TODO: in the iOS equivalent of this code, there is: {@"doc_ids", _documentIDs}) - write unit test that detects this bug
579-
// TODO: ditto for "headers"
580-
581-
Authorizer authorizer = null;
582-
Replication repl = null;
583-
URL remote = null;
584-
585577
Map<String, Object> remoteMap;
586578

587579
Map<String, Object> sourceMap = parseSourceOrTarget(properties, "source");
@@ -601,7 +593,7 @@ public Replication getReplicator(Map<String, Object> properties) throws Couchbas
601593

602594
// Map the 'source' and 'target' JSON params to a local database and remote URL:
603595
if (source == null || target == null) {
604-
throw new CouchbaseLiteException("source and target are both null", new Status(Status.BAD_REQUEST));
596+
throw new CouchbaseLiteException("Source and target are both null", new Status(Status.BAD_REQUEST));
605597
}
606598

607599
boolean push = false;
@@ -623,7 +615,7 @@ public Replication getReplicator(Map<String, Object> properties) throws Couchbas
623615
db = getExistingDatabase(target);
624616
}
625617
if (db == null) {
626-
throw new CouchbaseLiteException("database is null", new Status(Status.NOT_FOUND));
618+
throw new CouchbaseLiteException("Database is null", new Status(Status.NOT_FOUND));
627619
}
628620
remoteMap = sourceMap;
629621
}
@@ -633,20 +625,21 @@ public Replication getReplicator(Map<String, Object> properties) throws Couchbas
633625
throw new CouchbaseLiteException("Can't specify both a filter and doc IDs",
634626
new Status(Status.BAD_REQUEST));
635627

628+
URL remote = null;
636629
try {
637630
remote = new URL(remoteStr);
638631
} catch (MalformedURLException e) {
639-
throw new CouchbaseLiteException("malformed remote url: " + remoteStr,
632+
throw new CouchbaseLiteException("Malformed remote url: " + remoteStr,
640633
new Status(Status.BAD_REQUEST));
641634
}
642635
if (remote == null) {
643-
throw new CouchbaseLiteException("remote URL is null: " + remoteStr,
636+
throw new CouchbaseLiteException("Remote URL is null: " + remoteStr,
644637
new Status(Status.BAD_REQUEST));
645638
}
646639

640+
Authorizer authorizer = null;
647641
Map<String, Object> authMap = (Map<String, Object>) remoteMap.get("auth");
648642
if (authMap != null) {
649-
650643
Map<String, Object> persona = (Map<String, Object>) authMap.get("persona");
651644
if (persona != null) {
652645
String email = (String) persona.get("email");
@@ -661,63 +654,51 @@ public Replication getReplicator(Map<String, Object> properties) throws Couchbas
661654
authorizer.setLocalUUID(db.publicUUID());
662655
}
663656

657+
Replication repl = db.createReplicator(remote, push, getDefaultHttpClientFactory());
664658

659+
repl.setContinuous(continuous);
665660

666-
if (!cancel) {
667-
repl = db.getReplicator(remote, getDefaultHttpClientFactory(), push, continuous);
668-
if (repl == null) {
669-
throw new CouchbaseLiteException("unable to create replicator with remote: " + remote,
670-
new Status(Status.INTERNAL_SERVER_ERROR));
671-
}
672-
673-
if (authorizer != null) {
674-
repl.setAuthenticator(authorizer);
675-
}
661+
if (authorizer != null) {
662+
repl.setAuthenticator(authorizer);
663+
}
676664

677-
Map<String, Object> headers = null;
678-
if (remoteMap != null) {
679-
headers = (Map) remoteMap.get("headers");
680-
}
665+
Map<String, Object> headers = null;
666+
if (remoteMap != null) {
667+
headers = (Map) remoteMap.get("headers");
668+
}
681669

682-
if (headers != null && !headers.isEmpty()) {
683-
repl.setHeaders(headers);
684-
}
670+
if (headers != null && !headers.isEmpty()) {
671+
repl.setHeaders(headers);
672+
}
685673

686-
String filterName = (String) properties.get("filter");
687-
if (filterName != null) {
688-
repl.setFilter(filterName);
689-
Map<String, Object> filterParams = (Map<String, Object>) properties.get("query_params");
690-
if (filterParams != null) {
691-
repl.setFilterParams(filterParams);
692-
}
674+
String filterName = (String) properties.get("filter");
675+
if (filterName != null) {
676+
repl.setFilter(filterName);
677+
Map<String, Object> filterParams = (Map<String, Object>) properties.get("query_params");
678+
if (filterParams != null) {
679+
repl.setFilterParams(filterParams);
693680
}
681+
}
694682

695-
// docIDs
696-
if(properties.get("doc_ids") != null) {
697-
if(properties.get("doc_ids") instanceof List){
698-
List<String> docIds = (List<String>)properties.get("doc_ids");
699-
repl.setDocIds(docIds);
700-
}
683+
// docIDs
684+
if(properties.get("doc_ids") != null) {
685+
if(properties.get("doc_ids") instanceof List){
686+
List<String> docIds = (List<String>)properties.get("doc_ids");
687+
repl.setDocIds(docIds);
701688
}
689+
}
702690

703-
String remoteUUID = (String) properties.get("remoteUUID");
704-
if (remoteUUID != null) {
705-
repl.setRemoteUUID(remoteUUID);
706-
}
691+
String remoteUUID = (String) properties.get("remoteUUID");
692+
if (remoteUUID != null) {
693+
repl.setRemoteUUID(remoteUUID);
694+
}
707695

708-
if (push) {
709-
repl.setCreateTarget(createTarget);
710-
}
711-
} else {
712-
// Cancel replication:
713-
repl = db.getActiveReplicator(remote, push);
714-
if (repl == null) {
715-
throw new CouchbaseLiteException("unable to lookup replicator with remote: " + remote,
716-
new Status(Status.NOT_FOUND));
717-
}
696+
if (push) {
697+
repl.setCreateTarget(createTarget);
718698
}
719699

720-
return repl;
700+
Replication activeReplicator = db.findActiveReplicator(repl);
701+
return activeReplicator != null ? activeReplicator : repl;
721702
}
722703

723704
/**

src/main/java/com/couchbase/lite/replicator/PullerInternal.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,10 +1047,10 @@ protected void pauseOrResume() {
10471047

10481048
@Override
10491049
public String toString() {
1050-
if (str == null) {
1050+
if (str == null || str.contains("unknown")) {
10511051
String maskedRemote = "unknown";
10521052
if (remote != null)
1053-
remote.toExternalForm();
1053+
maskedRemote = remote.toExternalForm();
10541054
maskedRemote = maskedRemote.replaceAll("://.*:.*@", "://---:---@");
10551055
String type = isPull() ? "pull" : "push";
10561056
String replicationIdentifier = Utils.shortenString(remoteCheckpointDocID(), 5);

src/main/java/com/couchbase/lite/replicator/Replication.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,8 @@ protected void setLastError(Throwable lastError) {
464464
/**
465465
* Following two methods for temporary methods instead of CBL_ReplicatorSettings implementation.
466466
*/
467-
protected String remoteCheckpointDocID() {
467+
@InterfaceAudience.Private
468+
public String remoteCheckpointDocID() {
468469
return replicationInternal.remoteCheckpointDocID();
469470
}
470471

0 commit comments

Comments
 (0)