ro.sandd.qsync.QSync.java Source code

Java tutorial

Introduction

Here is the source code for ro.sandd.qsync.QSync.java

Source

/**
* 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();
                    }
                }
            }
        }
    };
}