org.kaaproject.kaa.examples.powerplant.ViewConsumer.java Source code

Java tutorial

Introduction

Here is the source code for org.kaaproject.kaa.examples.powerplant.ViewConsumer.java

Source

/**
 *  Copyright 2014-2016 CyberVision, Inc.
 *
 *  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.kaaproject.kaa.examples.powerplant;

import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.CouchbaseCluster;
import com.couchbase.client.java.PersistTo;
import com.couchbase.client.java.document.JsonDocument;
import com.couchbase.client.java.document.json.JsonArray;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.view.Stale;
import com.couchbase.client.java.view.ViewQuery;
import com.couchbase.client.java.view.ViewResult;
import com.couchbase.client.java.view.ViewRow;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ViewConsumer {
    private static final String DEFAULT_DESIGN = "dev_total_zone_view";
    private static final String DEFAULT_VIEW = "total_zone_view_dev";
    private static final String DEFAULT_CLUSTER_IP = "127.0.0.1";
    private static final String DEFAULT_DB_NAME = "powerplant";
    private static final long UPDATE_CHECK_TIME = 300L;
    private static final int MAX_CACHE_SIZE = 4000;
    private static final int INITIAL_CAPACITY = 3600;
    private static final int TIMESTAMP_MAX = 2000000000; // year 2033
    private static final int TIMESTAMP_MIN = 1433259210; // 1 Jan 1970
    private static final long TTL_SECONDS = 300;
    private static Cache<String, JsonDocument> documentCache;

    private static String design = null;
    private static String view = null;

    private static Bucket bucket;

    public static void main(String[] args) {
        System.out.println("Usage: java -jar JarName.jar clusterIP DBName design view");

        String clusterIP = null;
        String dbName = null;

        if (args.length != 4) {
            System.out.println("Using default options:");
            clusterIP = DEFAULT_CLUSTER_IP;
            dbName = DEFAULT_DB_NAME;
            design = DEFAULT_DESIGN;
            view = DEFAULT_VIEW;
        } else {
            clusterIP = args[0];
            dbName = args[1];
            design = args[2];
            view = args[3];
        }

        System.out.println("Cluster IP: " + DEFAULT_CLUSTER_IP);
        System.out.println("DB name: " + DEFAULT_DB_NAME);
        System.out.println("Design: " + DEFAULT_DESIGN);
        System.out.println("View: " + DEFAULT_VIEW);

        // Create a cluster reference
        CouchbaseCluster cluster = CouchbaseCluster.create(clusterIP);
        documentCache = CacheBuilder.newBuilder().initialCapacity(INITIAL_CAPACITY).maximumSize(MAX_CACHE_SIZE)
                .expireAfterWrite(TTL_SECONDS, TimeUnit.SECONDS).removalListener(new DocumentRemovalListener())
                .build();

        bucket = cluster.openBucket(dbName);

        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(new Worker());

        try {
            System.out.println("Press Enter to exit");
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }

        executor.shutdownNow();
        bucket.close();
        cluster.disconnect();
    }

    private static class Worker implements Runnable {
        private volatile int latestTs = 0;

        @Override
        public void run() {
            try {
                while (!Thread.currentThread().isInterrupted()) {
                    Thread.sleep(UPDATE_CHECK_TIME);
                    // get the latest data
                    ViewResult latestData = bucket.query(ViewQuery.from(DEFAULT_DESIGN, DEFAULT_VIEW)
                            .stale(Stale.FALSE).groupLevel(2).descending().limit(1));

                    Iterator<ViewRow> iterator = latestData.iterator();
                    if (!iterator.hasNext()) { // no data was added yet
                        continue;
                    }

                    JsonArray latestRow = (JsonArray) iterator.next().key();
                    int curLatestTs = latestRow.getInt(0);
                    if (curLatestTs > TIMESTAMP_MAX || curLatestTs < TIMESTAMP_MIN) {
                        System.out.println("Error in data, incorrect timestamp for row: " + latestRow);
                        throw new RuntimeException("Incorrect timestamp for row: " + latestRow);
                    } else if (curLatestTs >= latestTs) {
                        latestTs = curLatestTs;
                        uploadData();
                    }
                }
            } catch (InterruptedException e) {
                // thread was interrupted
                e.printStackTrace();
            }
        }

        private void uploadData() {
            JsonArray startKey = JsonArray.create().add(latestTs);

            ViewResult result = bucket
                    .query(ViewQuery.from(design, view).stale(Stale.FALSE).groupLevel(2).startKey(startKey));

            Map<Integer, JsonObject> updatesMap = new HashMap<>();

            for (ViewRow row : result) {
                JsonArray key = (JsonArray) row.key();
                JsonObject value = (JsonObject) row.value();

                int tsInSeconds = key.getInt(0);

                JsonObject update = updatesMap.get(tsInSeconds);

                if (update == null) {
                    update = JsonObject.empty();
                    update.put("zones", JsonArray.empty());
                    update.put("ts", tsInSeconds);
                    updatesMap.put(tsInSeconds, update);
                }

                JsonObject zoneUpdate = JsonObject.empty();
                zoneUpdate.put("zoneId", key.getInt(1));
                zoneUpdate.put("sum", value.getDouble("sum"));
                zoneUpdate.put("count", value.getInt("count"));
                update.getArray("zones").add(zoneUpdate);
            }

            for (Map.Entry<Integer, JsonObject> entry : updatesMap.entrySet()) {
                JsonDocument doc = JsonDocument.create("totals_" + entry.getKey(), entry.getValue());
                System.out.println("Updating for totals_" + entry.getKey());
                bucket.upsert(doc);
                if (documentCache.getIfPresent(doc.id()) == null) {
                    System.out.println("Putting into cache: " + doc.id());
                    documentCache.put(doc.id(), doc);
                }
            }
        }
    }

    private static class DocumentRemovalListener implements RemovalListener<String, JsonDocument> {
        @Override
        public void onRemoval(RemovalNotification<String, JsonDocument> removalNotification) {
            System.out.println("Removing document with id: " + removalNotification.getValue().id());
            bucket.remove(removalNotification.getValue().id(), PersistTo.MASTER);
        }
    }
}