com.softinstigate.restheart.db.DBDAO.java Source code

Java tutorial

Introduction

Here is the source code for com.softinstigate.restheart.db.DBDAO.java

Source

/*
 * RESTHeart - the data REST API server
 * Copyright (C) 2014 SoftInstigate Srl
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.softinstigate.restheart.db;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.softinstigate.restheart.utils.HttpStatus;
import com.softinstigate.restheart.handlers.IllegalQueryParamenterException;
import com.softinstigate.restheart.handlers.RequestContext;
import com.softinstigate.restheart.handlers.injectors.LocalCachesSingleton;
import com.softinstigate.restheart.utils.ResponseHelper;
import io.undertow.server.HttpServerExchange;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author Andrea Di Cesare
 */
public class DBDAO {

    private static final MongoClient client = MongoDBClientSingleton.getInstance().getClient();

    private static final Logger logger = LoggerFactory.getLogger(DBDAO.class);

    /**
     *
     */
    public static final BasicDBObject METADATA_QUERY = new BasicDBObject("_id", "_properties");

    private static final BasicDBObject fieldsToReturn;

    static {
        fieldsToReturn = new BasicDBObject();
        fieldsToReturn.put("_id", 1);
        fieldsToReturn.put("_created_on", 1);
    }

    private static final BasicDBObject fieldsToReturnIndexes;

    static {
        fieldsToReturnIndexes = new BasicDBObject();
        fieldsToReturnIndexes.put("key", 1);
        fieldsToReturnIndexes.put("name", 1);
    }

    /**
     *
     * WARNING: slow method.
     *
     * @param exchange
     * @param dbName
     * @return
     * @deprecated
     *
     */
    public static boolean checkDbExists(HttpServerExchange exchange, String dbName) {
        if (!doesDbExists(dbName)) {
            ResponseHelper.endExchange(exchange, HttpStatus.SC_NOT_FOUND);
            return false;
        }

        return true;
    }

    /**
     * WARNING: slow method.
     *
     * @param dbName
     * @return
     *
     */
    public static boolean doesDbExists(String dbName) {

        return client.getDatabaseNames().contains(dbName);
    }

    /**
     *
     * @param dbName
     * @return
     */
    public static DB getDB(String dbName) {
        return client.getDB(dbName);
    }

    /**
     *
     * @param db
     * @return
     */
    public static List<String> getDbCollections(DB db) {
        List<String> _colls = new ArrayList(db.getCollectionNames());

        Collections.sort(_colls);

        return _colls;
    }

    /**
     * @param colls the collections list got from getDbCollections()
     * @return the number of collections in this db
     *
     */
    public static long getDBSize(List<String> colls) {
        // filter out reserved resources
        List<String> _colls = colls.stream().filter(coll -> !RequestContext.isReservedResourceCollection(coll))
                .collect(Collectors.toList());

        return _colls.size();
    }

    /**
     * @param dbName
     * @return the db props
     *
     */
    public static DBObject getDbProps(String dbName) {
        if (!DBDAO.doesDbExists(dbName)) // this check is important, otherwise the db would get created if not existing after the query
        {
            return null;
        }

        DBCollection propscoll = CollectionDAO.getCollection(dbName, "_properties");

        DBObject row = propscoll.findOne(METADATA_QUERY);

        if (row != null) {
            row.put("_id", dbName);

            Object etag = row.get("_etag");

            if (etag != null && ObjectId.isValid("" + etag)) {
                ObjectId oid = new ObjectId("" + etag);

                row.put("_lastupdated_on", Instant.ofEpochSecond(oid.getTimestamp()).toString());
            }
        }

        return row;
    }

    /**
     * @param dbName
     * @param colls the collections list as got from getDbCollections()
     * @param page
     * @param pagesize
     * @return the db data
     * @throws
     * com.softinstigate.restheart.handlers.IllegalQueryParamenterException
     *
     */
    public static List<DBObject> getData(String dbName, List<String> colls, int page, int pagesize)
            throws IllegalQueryParamenterException {
        // filter out reserved resources
        List<String> _colls = colls.stream().filter(coll -> !RequestContext.isReservedResourceCollection(coll))
                .collect(Collectors.toList());

        int size = _colls.size();

        // *** arguments check
        long total_pages;

        if (size > 0) {
            float _size = size + 0f;
            float _pagesize = pagesize + 0f;

            total_pages = Math.max(1, Math.round(Math.ceil(_size / _pagesize)));

            if (page > total_pages) {
                throw new IllegalQueryParamenterException(
                        "illegal query paramenter, page is bigger that total pages which is " + total_pages);
            }
        }

        // apply page and pagesize
        _colls = _colls.subList((page - 1) * pagesize,
                (page - 1) * pagesize + pagesize > _colls.size() ? _colls.size()
                        : (page - 1) * pagesize + pagesize);

        List<DBObject> data = new ArrayList<>();

        _colls.stream().map((collName) -> {
            BasicDBObject properties = new BasicDBObject();

            properties.put("_id", collName);

            DBObject collProperties;

            if (LocalCachesSingleton.isEnabled()) {
                collProperties = LocalCachesSingleton.getInstance().getCollectionProps(dbName, collName);
            } else {
                collProperties = CollectionDAO.getCollectionProps(dbName, collName);
            }

            if (collProperties != null) {
                properties.putAll(collProperties);
            }

            return properties;
        }).forEach((item) -> {
            data.add(item);
        });

        return data;
    }

    /**
     *
     * @param dbName
     * @param content
     * @param etag
     * @param patching
     * @return
     */
    public static int upsertDB(String dbName, DBObject content, ObjectId etag, boolean patching) {
        DB db = client.getDB(dbName);

        boolean existing = db.getCollectionNames().size() > 0;

        if (patching && !existing) {
            return HttpStatus.SC_NOT_FOUND;
        }

        DBCollection coll = db.getCollection("_properties");

        // check the etag
        if (db.collectionExists("_properties")) {
            if (etag == null) {
                return HttpStatus.SC_CONFLICT;
            }

            BasicDBObject idAndEtagQuery = new BasicDBObject("_id", "_properties");
            idAndEtagQuery.append("_etag", etag);

            if (coll.count(idAndEtagQuery) < 1) {
                return HttpStatus.SC_PRECONDITION_FAILED;
            }
        }

        // apply new values
        ObjectId timestamp = new ObjectId();
        Instant now = Instant.ofEpochSecond(timestamp.getTimestamp());

        if (content == null) {
            content = new BasicDBObject();
        }

        content.put("_etag", timestamp);
        content.removeField("_created_on"); // make sure we don't change this field
        content.removeField("_id"); // make sure we don't change this field

        if (patching) {
            coll.update(METADATA_QUERY, new BasicDBObject("$set", content), true, false);

            return HttpStatus.SC_OK;
        } else {
            // we use findAndModify to get the @created_on field value from the existing document
            // we need to put this field back using a second update 
            // it is not possible in a single update even using $setOnInsert update operator
            // in this case we need to provide the other data using $set operator and this makes it a partial update (patch semantic) 
            DBObject old = coll.findAndModify(METADATA_QUERY, fieldsToReturn, null, false, content, false, true);

            if (old != null) {
                Object oldTimestamp = old.get("_created_on");

                if (oldTimestamp == null) {
                    oldTimestamp = now.toString();
                    logger.warn("properties of collection {} had no @created_on field. set to now",
                            coll.getFullName());
                }

                // need to readd the @created_on field 
                BasicDBObject createdContet = new BasicDBObject("_created_on", "" + oldTimestamp);
                createdContet.markAsPartialObject();
                coll.update(METADATA_QUERY, new BasicDBObject("$set", createdContet), true, false);

                return HttpStatus.SC_OK;
            } else {
                // need to readd the @created_on field 
                BasicDBObject createdContet = new BasicDBObject("_created_on", now.toString());
                createdContet.markAsPartialObject();
                coll.update(METADATA_QUERY, new BasicDBObject("$set", createdContet), true, false);

                return HttpStatus.SC_CREATED;
            }
        }
    }

    /**
     *
     * @param dbName
     * @param requestEtag
     * @return
     */
    public static int deleteDB(String dbName, ObjectId requestEtag) {
        DB db = DBDAO.getDB(dbName);

        DBCollection coll = db.getCollection("_properties");

        BasicDBObject checkEtag = new BasicDBObject("_id", "_properties");
        checkEtag.append("_etag", requestEtag);

        DBObject exists = coll.findOne(checkEtag, fieldsToReturn);

        if (exists == null) {
            return HttpStatus.SC_PRECONDITION_FAILED;
        } else {
            db.dropDatabase();
            return HttpStatus.SC_NO_CONTENT;
        }
    }
}