/* * Copyright (C) 2011 Ahmed Yehia (ahmed.yehia.m@gmail.com) * * 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 org.lightcouch; import static org.lightcouch.CouchDbUtil.*; import static org.lightcouch.URIBuilder.builder; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.URI; import java.util.ArrayList; import java.util.List; import org.lightcouch.ReplicatorDocument.UserCtx; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonParser; /** * <p>This class allows construction and sending of replication requests targeting a replicator database. * <p>The Replicator database, by default is called <tt>_replicator</tt> was introduced in CouchDB version 1.1.0 * <p>The API supports triggering replication requests by adding a document to the replicator database, * and cancelling a replication by removing the document that triggered the replication. * * <h3>Usage Example:</h3> * <pre> * Response response = dbClient.replicator() * .source("source-db") * .target("target-db") * .continuous(true) * .createTarget(true) * .replicatorDB("replicator-db-name") // optionally specify database name, defaults to _replicator * .replicatorDocId("doc-id") // optionally specify document id, defaults to a new UUID being assigned * .save(); * * ReplicatorDocument document = dbClient.replicator() * .replicatorDocId("doc-id") * .replicatorDocRev("doc-rev") // optional * .find(); * * {@code * List<ReplicatorDocument> docs = dbClient.replicator().findAll(); * } * * Response response = dbClient.replicator() * .replicatorDocId("doc-id") * .replicatorDocRev("doc-rev") * .remove(); * </pre> * * @see Replication * @author Ahmed Yehia * */ public class Replicator { private String source; private String target; private Boolean continuous; private String filter; private String queryParams; private String[] docIds; private String proxy; private Boolean createTarget; private String replicatorDB; private String replicatorDocId; private String replicatorDocRev; private String userCtxName; // for delegated requests private String[] userCtxRoles; private CouchDbClient dbc; public Replicator(CouchDbClient dbc) { this.dbc = dbc; replicatorDB = "_replicator"; // default replicator db userCtxRoles = new String[0]; // default roles } // ------------------------------------------------------------- Field setters public Replicator source(String source) { this.source = source; return this; } public Replicator target(String target) { this.target = target; return this; } public Replicator continuous(Boolean continuous) { this.continuous = continuous; return this; } public Replicator filter(String filter) { this.filter = filter; return this; } public Replicator queryParams(String queryParams) { this.source = queryParams; return this; } public Replicator docIds(String... docIds) { this.docIds = docIds; return this; } public Replicator proxy(String proxy) { this.proxy = proxy; return this; } public Replicator createTarget(Boolean createTarget) { this.createTarget = createTarget; return this; } public Replicator replicatorDB(String replicatorDB) { this.replicatorDB = replicatorDB; return this; } public Replicator replicatorDocId(String replicatorDocId) { this.replicatorDocId = replicatorDocId; return this; } public Replicator replicatorDocRev(String replicatorDocRev) { this.replicatorDocRev = replicatorDocRev; return this; } public Replicator userCtxName(String userCtxName) { this.userCtxName = userCtxName; return this; } public Replicator userCtxRoles(String... userCtxRoles) { this.userCtxRoles = userCtxRoles; return this; } // --------------------------------------------------------------- Requests /** * Adds a new document to the replicator database. * @return {@link Response} */ public Response save() { assertNotEmpty(source, "Source database"); assertNotEmpty(target, "Target database"); ReplicatorDocument rd = new ReplicatorDocument(); rd.setId(replicatorDocId); rd.setSource(source); rd.setTarget(target); rd.setContinuous(continuous); rd.setFilter(filter); rd.setQueryParams(queryParams); rd.setDocIds(docIds); rd.setProxy(proxy); rd.setCreateTarget(createTarget); if(userCtxName != null) { UserCtx ctx = rd.new UserCtx(); ctx.setName(userCtxName); ctx.setRoles(userCtxRoles); rd.setUserCtx(ctx); } URI uri = builder(dbc.getBaseUri()).path(replicatorDB).path("/").build(); return dbc.put(uri, rd, true); } /** * Finds a document in the replicator database. * @return {@link ReplicatorDocument} */ public ReplicatorDocument find() { assertNotEmpty(replicatorDocId, "Document Id"); URI uri = builder(dbc.getBaseUri()).path(replicatorDB).path("/").path(replicatorDocId).query("rev", replicatorDocRev).build(); return dbc.get(uri, ReplicatorDocument.class); } /** * Finds all documents in the replicator database. */ public List<ReplicatorDocument> findAll() { InputStream instream = null; try { URI uri = builder(dbc.getBaseUri()).path(replicatorDB).path("/").path("_all_docs").query("include_docs", "true").build(); Reader reader = new InputStreamReader(instream = dbc.get(uri)); JsonArray jsonArray = new JsonParser().parse(reader) .getAsJsonObject().getAsJsonArray("rows"); List<ReplicatorDocument> list = new ArrayList<ReplicatorDocument>(); for (JsonElement jsonElem : jsonArray) { JsonElement elem = jsonElem.getAsJsonObject().get("doc"); if(!getElement(elem.getAsJsonObject(), "_id").startsWith("_design")) { // skip design docs ReplicatorDocument rd = dbc.getGson().fromJson(elem, ReplicatorDocument.class); list.add(rd); } } return list; } finally { close(instream); } } /** * Removes a document from the replicator database. * @return {@link Response} */ public Response remove() { assertNotEmpty(replicatorDocId, "Replicator Document Id"); assertNotEmpty(replicatorDocRev, "Replicator Document revision"); URI uri = builder(dbc.getBaseUri()).path(replicatorDB).path("/").path(replicatorDocId).query("rev", replicatorDocRev).build(); return dbc.delete(uri); } }