/*
* Copyright (C) 2011 lightcouch.org
* Copyright (c) 2015 IBM Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
package com.cloudant.client.org.lightcouch;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.assertNotEmpty;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.close;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.getAsString;
import com.cloudant.client.internal.DatabaseURIHelper;
import com.cloudant.client.internal.URIBase;
import com.cloudant.client.org.lightcouch.ReplicatorDocument.UserCtx;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* This class provides access to the <tt>_replicator</tt> database introduced in CouchDB version
* 1.1.0
* <p>A replication is triggered by persisting a document, and cancelled by removing the document
* that triggered the replication.
* <p/>
* <P>
* Replicates source to target, the target must exist prior to replication. Use {@link
* #createTarget(Boolean)} to have it created automatically.
* </P>
* <P>Usage Example:</P>
* <pre>
* {@code
* Response response = db.replicator()
* .source("https://source.example/source-db")
* .target("https://target.example/target-db")
* .continuous(true)
* .createTarget(true)
* .replicatorDB("replicator-db-name") // optional, defaults to _replicator
* .replicatorDocId("doc-id") // optional, defaults to UUID
* .save(); // persist the document to the _replicator database
*
* //the replication is controlled by the server and will start soon after the save
*
* //get a specific replicator document
* ReplicatorDocument replicatorDoc = db.replicator()
* .replicatorDocId("doc-id")
* .replicatorDocRev("doc-rev") // optional
* .find();
*
* //get all the replicator documents
* List<ReplicatorDocument> replicatorDocs = db.replicator().findAll();
*
* //remove a replicator document to cancel replication
* Response response = db.replicator()
* .replicatorDocId("doc-id")
* .replicatorDocRev("doc-rev")
* .remove(); // cancels a replication
* }
* </pre>
*
* @author Ahmed Yehia
* @see ReplicatorDocument
* @see <a target="_blank"
* href="https://docs.cloudant.com/replication.html#the-/_replicator-database">
* Replication - _replicator
* </a>
* @since 0.0.2
*/
public class Replicator {
private String replicatorDB;
private String userCtxName;
private String[] userCtxRoles;
private CouchDbClient client;
private ReplicatorDocument replicatorDoc;
private URI dbURI;
public Replicator(CouchDbClient client) {
this.client = client;
replicatorDoc = new ReplicatorDocument();
replicatorDB = "_replicator"; // default replicator db
userCtxRoles = new String[0]; // default roles
dbURI = new URIBase(client.getBaseUri()).path(replicatorDB).build();
}
/**
* Adds a new document to the replicator database.
*
* @return {@link Response}
*/
public Response save() {
assertNotEmpty(replicatorDoc.getSource(), "Source");
assertNotEmpty(replicatorDoc.getTarget(), "Target");
if (userCtxName != null) {
UserCtx ctx = new ReplicatorDocument.UserCtx();
ctx.setName(userCtxName);
ctx.setRoles(userCtxRoles);
replicatorDoc.setUserCtx(ctx);
}
return client.put(dbURI, replicatorDoc, true);
}
/**
* Finds a document in the replicator database.
*
* @return {@link ReplicatorDocument}
*/
public ReplicatorDocument find() {
assertNotEmpty(replicatorDoc.getId(), "Doc id");
final URI uri = new DatabaseURIHelper(dbURI).documentUri(replicatorDoc.getId(),
replicatorDoc.getRevision());
return client.get(uri, ReplicatorDocument.class);
}
/**
* Finds all documents in the replicator database.
*/
public List<ReplicatorDocument> findAll() {
InputStream instream = null;
try {
final URI uri = new DatabaseURIHelper(dbURI).path("_all_docs")
.query("include_docs", "true").build();
final Reader reader = new InputStreamReader(instream = client.get(uri), "UTF-8");
final JsonArray jsonArray = new JsonParser().parse(reader)
.getAsJsonObject().getAsJsonArray("rows");
final List<ReplicatorDocument> list = new ArrayList<ReplicatorDocument>();
for (JsonElement jsonElem : jsonArray) {
JsonElement elem = jsonElem.getAsJsonObject().get("doc");
if (!getAsString(elem.getAsJsonObject(), "_id").startsWith("_design")) { // skip
// design docs
ReplicatorDocument rd = client.getGson().fromJson(elem, ReplicatorDocument
.class);
list.add(rd);
}
}
return list;
} catch (UnsupportedEncodingException e) {
// This should never happen as every implementation of the java platform is required
// to support UTF-8.
throw new RuntimeException(e);
} finally {
close(instream);
}
}
/**
* Removes a document from the replicator database.
*
* @return {@link Response}
*/
public Response remove() {
assertNotEmpty(replicatorDoc.getId(), "Doc id");
assertNotEmpty(replicatorDoc.getRevision(), "Doc rev");
final URI uri = new DatabaseURIHelper(dbURI).path(replicatorDoc.getId())
.query("rev", replicatorDoc.getRevision()).build();
return client.delete(uri);
}
// fields
public Replicator source(String source) {
replicatorDoc.setSource(source);
return this;
}
public Replicator target(String target) {
replicatorDoc.setTarget(target);
return this;
}
public Replicator continuous(boolean continuous) {
replicatorDoc.setContinuous(continuous);
return this;
}
public Replicator filter(String filter) {
replicatorDoc.setFilter(filter);
return this;
}
public Replicator queryParams(String queryParams) {
replicatorDoc.setQueryParams(client.getGson().fromJson(queryParams, JsonObject.class));
return this;
}
public Replicator queryParams(Map<String, Object> queryParams) {
replicatorDoc.setQueryParams(client.getGson().toJsonTree(queryParams).getAsJsonObject());
return this;
}
public Replicator docIds(String... docIds) {
replicatorDoc.setDocIds(docIds);
return this;
}
public Replicator proxy(String proxy) {
replicatorDoc.setProxy(proxy);
return this;
}
public Replicator createTarget(Boolean createTarget) {
replicatorDoc.setCreateTarget(createTarget);
return this;
}
public Replicator replicatorDB(String replicatorDB) {
this.replicatorDB = replicatorDB;
dbURI = new URIBase(client.getBaseUri()).path(replicatorDB).build();
return this;
}
public Replicator replicatorDocId(String replicatorDocId) {
replicatorDoc.setId(replicatorDocId);
return this;
}
public Replicator replicatorDocRev(String replicatorDocRev) {
replicatorDoc.setRevision(replicatorDocRev);
return this;
}
public Replicator workerProcesses(int workerProcesses) {
replicatorDoc.setWorkerProcesses(workerProcesses);
return this;
}
public Replicator workerBatchSize(int workerBatchSize) {
replicatorDoc.setWorkerBatchSize(workerBatchSize);
return this;
}
public Replicator httpConnections(int httpConnections) {
replicatorDoc.setHttpConnections(httpConnections);
return this;
}
public Replicator connectionTimeout(long connectionTimeout) {
replicatorDoc.setConnectionTimeout(connectionTimeout);
return this;
}
public Replicator retriesPerRequest(int retriesPerRequest) {
replicatorDoc.setRetriesPerRequest(retriesPerRequest);
return this;
}
public Replicator userCtxName(String userCtxName) {
this.userCtxName = userCtxName;
return this;
}
public Replicator userCtxRoles(String... userCtxRoles) {
this.userCtxRoles = userCtxRoles;
return this;
}
public Replicator sinceSeq(Integer sinceSeq) {
replicatorDoc.setSinceSeq(sinceSeq);
return this;
}
}