com.centurylink.mdw.mongo.MongoDocumentDb.java Source code

Java tutorial

Introduction

Here is the source code for com.centurylink.mdw.mongo.MongoDocumentDb.java

Source

/*
 * Copyright (C) 2018 CenturyLink, 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 com.centurylink.mdw.mongo;

import static com.mongodb.client.model.Filters.eq;
import static com.mongodb.client.model.Projections.excludeId;
import static com.mongodb.client.model.Projections.fields;
import static com.mongodb.client.model.Projections.include;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.bson.json.JsonWriterSettings;

import com.centurylink.mdw.dataaccess.db.DocumentDb;
import com.centurylink.mdw.util.log.LoggerUtil;
import com.centurylink.mdw.util.timer.CodeTimer;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;

public class MongoDocumentDb implements DocumentDb {

    private String dbHost;

    public String getDbHost() {
        return dbHost;
    }

    public void setDbHost(String host) {
        this.dbHost = host;
    }

    private int dbPort;

    public int getDbPort() {
        return dbPort;
    }

    public void setDbPort(int port) {
        this.dbPort = port;
    }

    private int maxConnections;

    public int getMaxConnections() {
        return maxConnections;
    }

    public void setMaxConnections(int maxConns) {
        this.maxConnections = maxConns;
    }

    private static MongoClient mongoClient;
    private static Map<String, Boolean> collectionDocIdIndexed = new ConcurrentHashMap<>();

    public static MongoDatabase getMongoDb() {
        return getMongoDb("mdw"); // Default database within Mongo instance
    }

    public static MongoDatabase getMongoDb(String databaseName) {
        return mongoClient == null ? null : mongoClient.getDatabase(databaseName);
    }

    @Override
    public boolean isEnabled() {
        return dbHost != null;
    }

    @Override
    public void initializeDbClient() {
        if (mongoClient == null) {
            MongoClientOptions.Builder options = MongoClientOptions.builder();
            if (maxConnections > 100) // MongoClient default is 100 max connections per host
                options.connectionsPerHost(maxConnections);

            mongoClient = new MongoClient(new ServerAddress(dbHost, dbPort), options.build());

            for (String name : mongoClient.getDatabase("mdw").listCollectionNames()) {
                createMongoDocIdIndex(name);
            }
            LoggerUtil.getStandardLogger().info(mongoClient.getMongoClientOptions().toString());
        }
    }

    @Override
    public String getDocumentContent(String ownerType, Long documentId) {
        CodeTimer timer = new CodeTimer("Load from documentDb", true);
        try {
            MongoCollection<org.bson.Document> mongoCollection = getMongoDb().getCollection(ownerType);
            org.bson.Document mongoQuery = new org.bson.Document("document_id", documentId);
            org.bson.Document c = mongoCollection.find(mongoQuery).limit(1)
                    .projection(fields(include("CONTENT", "isJSON"), excludeId())).first();
            if (c == null) {
                return null;
            } else if (c.getBoolean("isJSON", false)) {
                return decodeMongoDoc(c.get("CONTENT", org.bson.Document.class))
                        .toJson(JsonWriterSettings.builder().indent(true).build());
            } else {
                return c.getString("CONTENT");
            }
        } finally {
            timer.stopAndLogTiming(null);
        }
    }

    @Override
    public void createDocument(String ownerType, Long documentId, String content) {
        MongoCollection<org.bson.Document> collection = getMongoDb().getCollection(ownerType);
        org.bson.Document myDoc = null;
        if (content.startsWith("{")) {
            try {
                // Parse JSON to create BSON CONTENT Document
                org.bson.Document myJsonDoc = org.bson.Document.parse(content);
                if (!myJsonDoc.isEmpty()) {
                    if (content.contains(".") || content.contains("$"))
                        myJsonDoc = encodeMongoDoc(myJsonDoc);
                    // Plus append _id and isJSON:true field
                    myDoc = new org.bson.Document("CONTENT", myJsonDoc).append("document_id", documentId)
                            .append("isJSON", true);
                }
            } catch (Throwable ex) {
                myDoc = null;
            } // Assume not JSON then
        }
        if (myDoc == null) {
            // Create BSON document with Raw content if it wasn't JSON, plus append _id and isJSON:false
            myDoc = new org.bson.Document("CONTENT", content).append("document_id", documentId).append("isJSON",
                    false);
        }

        collection.insertOne(myDoc);
        if (!checkForDocIdIndex(ownerType))
            createMongoDocIdIndex(ownerType);
    }

    @Override
    public boolean updateDocument(String ownerType, Long documentId, String content) {
        MongoCollection<org.bson.Document> collection = getMongoDb().getCollection(ownerType);
        org.bson.Document myDoc = null;
        if (content.startsWith("{")) {
            try {
                // Parse JSON to create BSON CONTENT Document
                org.bson.Document myJsonDoc = org.bson.Document.parse(content);
                if (!myJsonDoc.isEmpty()) {
                    if (content.contains(".") || content.contains("$"))
                        myJsonDoc = encodeMongoDoc(myJsonDoc);
                    // Plus append isJSON:true field
                    myDoc = new org.bson.Document("CONTENT", myJsonDoc).append("document_id", documentId)
                            .append("isJSON", true);
                }
            } catch (Throwable ex) {
                myDoc = null;
            } // Assume not JSON then
        }
        if (myDoc == null) // Create BSON document with Raw content if it wasn't JSON plus append isJSON:false
            myDoc = new org.bson.Document("CONTENT", content).append("document_id", documentId).append("isJSON",
                    false);
        if (collection.findOneAndReplace(eq("document_id", documentId), myDoc) != null)
            return true;
        else
            return false;
    }

    @Override
    public boolean deleteDocument(String ownerType, Long documentId) {
        MongoCollection<org.bson.Document> collection = getMongoDb().getCollection(ownerType);
        return collection.findOneAndDelete(eq("document_id", documentId)) != null;
    }

    public static void createMongoDocIdIndex(String collectionName) {
        IndexOptions indexOptions = new IndexOptions().unique(true).background(true);
        MongoCollection<org.bson.Document> collection = mongoClient.getDatabase("mdw")
                .getCollection(collectionName);
        String indexName = collection.createIndex(Indexes.ascending("document_id"), indexOptions);
        LoggerUtil.getStandardLogger()
                .mdwDebug("Created Index : " + indexName + " on collection : " + collectionName);
        collectionDocIdIndexed.putIfAbsent(collectionName, Boolean.valueOf(true));
    }

    /**
     * MongoDB doesn't allow keys to have dots (.) or to start with $.  This method encodes such keys if found
     * and returns a new BSON document
     */
    @SuppressWarnings("unchecked")
    public static org.bson.Document encodeMongoDoc(org.bson.Document doc) {
        org.bson.Document newDoc = new org.bson.Document();
        for (String key : doc.keySet()) {
            Object value = doc.get(key);
            if (value instanceof org.bson.Document)
                value = encodeMongoDoc(doc.get(key, org.bson.Document.class));
            else if (value instanceof List<?>) {
                for (int i = 0; i < ((List<?>) value).size(); i++) {
                    Object obj = ((List<?>) value).get(i);
                    if (obj instanceof org.bson.Document)
                        ((List<org.bson.Document>) value).set(i, encodeMongoDoc((org.bson.Document) obj));
                }
            }

            String newKey = key;
            if (key.startsWith("$"))
                newKey = "\\uff04" + key.substring(1);
            if (key.contains(".")) {
                newKey = newKey.replace(".", "\\uff0e");
            }
            newDoc.put(newKey, value);
        }
        return newDoc;
    }

    /**
     * MongoDB doesn't allow keys to have dots (.) or to start with $.  This method decodes such keys back to dots and $ if found
     * and returns a new BSON document
     */
    @SuppressWarnings("unchecked")
    public static org.bson.Document decodeMongoDoc(org.bson.Document doc) {
        org.bson.Document newDoc = new org.bson.Document();
        for (String key : doc.keySet()) {
            Object value = doc.get(key);
            if (value instanceof org.bson.Document)
                value = decodeMongoDoc(doc.get(key, org.bson.Document.class));
            else if (value instanceof List<?>) {
                for (int i = 0; i < ((List<?>) value).size(); i++) {
                    Object obj = ((List<?>) value).get(i);
                    if (obj instanceof org.bson.Document)
                        ((List<org.bson.Document>) value).set(i, decodeMongoDoc((org.bson.Document) obj));
                }
            }

            String newKey = key;
            if (key.startsWith("\\uff04"))
                newKey = "$" + key.substring(6);
            if (key.contains("\\uff0e")) {
                newKey = newKey.replace("\\uff0e", ".");
            }
            newDoc.put(newKey, value);
        }
        return newDoc;
    }

    public static boolean checkForDocIdIndex(String collectionName) {
        if (collectionDocIdIndexed.get(collectionName) != null)
            return ((Boolean) collectionDocIdIndexed.get(collectionName)).booleanValue();
        return false;
    }

    public String toString() {
        return dbHost + ":" + dbPort;
    }

}