Java tutorial
/** * Copyright (c) 2015 Doru Chiulan. 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 ro.sandd.qsync; import com.couchbase.lite.*; import com.couchbase.lite.replicator.Replication; import com.google.gson.Gson; import com.google.gson.internal.LinkedTreeMap; import java.io.IOException; import java.lang.reflect.Type; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.Vector; /** * The ro.sandd.QSync Service. It makes use of the Couchbase Lite local database to store the properties you want to sync or the * ones received from the server. Also it uses Couchbase Lite Replicators to sync with other instances of ro.sandd.QSync using * Couchbase Sync Gateway together with Couchbase Server */ public class QSync { private static Database db; private static Replication pull; private static Replication push; private static ChangesOptions changesOptions; private static Vector<QSyncChangeListener> listeners; /** * ro.sandd.QSync doesn't allow instances */ private QSync() { } /** * Initialises the ro.sandd.QSync service * @param remoteURL The URL to the Sync Gateway database (e.g. http://localhost:4984/db) * @throws CouchbaseLiteException * @throws IOException */ public static void init(String remoteURL) throws CouchbaseLiteException, IOException { ManagerOptions options = new ManagerOptions(); Manager manager = new Manager(new JavaContext(), options); db = manager.getDatabase("database"); pull = db.createPullReplication(new URL(remoteURL)); push = db.createPushReplication(new URL(remoteURL)); listeners = new Vector<QSyncChangeListener>(); changesOptions = new ChangesOptions(); pull.setContinuous(true); push.setContinuous(true); pull.addChangeListener(changeListener); pull.start(); push.start(); } /** * Saves a key-value pair for syncing * @param key A string representing the key for identifying the stored data * @param value The stored data * @throws CouchbaseLiteException */ public static void putValue(String key, Object value) throws CouchbaseLiteException { Gson gson = new Gson(); HashMap<String, Object> hm = new HashMap<String, Object>(); hm.put("data", value); String json = gson.toJson(hm); final LinkedTreeMap map = gson.fromJson(json, LinkedTreeMap.class); map.put("timestamp", System.currentTimeMillis()); db.getDocument(key).update(new Document.DocumentUpdater() { public boolean update(UnsavedRevision unsavedRevision) { unsavedRevision.setUserProperties(map); return true; } }); Map<String, Object> doc = new HashMap<String, Object>(); doc.put("sequence", db.getLastSequenceNumber()); db.putLocalDocument("sequence", doc); } /** * Gets the value for a given key * @param key A string representing the key for which you want to get the data * @param type The type that represents the data in this property. This is needed because ro.sandd.QSync has to know how to de-serialize * the data back to the desired class. (e.g. Integer.class) * @return An Object which you can cast to the original class */ public static Object getValue(String key, Type type) { Map<String, Object> properties = db.getDocument(key).getProperties(); Gson gs = new Gson(); String json = gs.toJson(properties.get("data")); return gs.fromJson(json, type); } /** * Adds a listener which will receive events when sync changes have been pulled from the server. * @param l A class implementing the ro.sandd.QSyncChangeListener interface */ public static void addListener(QSyncChangeListener l) { listeners.add(l); } /** * Removes the ro.sandd.QSyncChangeListener * @param l The ro.sandd.QSyncChangeListener to remove. */ public static void removeListener(QSyncChangeListener l) { listeners.remove(l); } private static Replication.ChangeListener changeListener = new Replication.ChangeListener() { public void changed(Replication.ChangeEvent changeEvent) { // First check whether replication is currently active: boolean active = pull.getStatus() == Replication.ReplicationStatus.REPLICATION_ACTIVE; if (!active) { // Changed from ACTIVE to IDLE, probably we have to notify Map<String, Object> doc = new HashMap<String, Object>(); // Notify QSyncChangeListeners of any keys that were updated int currentSeq = (Integer) db.getExistingLocalDocument("sequence").get("sequence"); if (currentSeq < db.getLastSequenceNumber()) { // Notify all listeners RevisionList revisionInternals = db.changesSince(currentSeq, changesOptions, null, null); if (revisionInternals.getAllDocIds().size() > 0) { for (QSyncChangeListener l : listeners) { l.changed(revisionInternals.getAllDocIds()); } } // Update the last sequence number in the db doc.put("sequence", db.getLastSequenceNumber()); try { db.putLocalDocument("sequence", doc); } catch (CouchbaseLiteException e) { e.printStackTrace(); } } } } }; }