org.restheart.db.DbsDAO.java Source code

Java tutorial

Introduction

Here is the source code for org.restheart.db.DbsDAO.java

Source

/*
 * RESTHeart - the Web API for MongoDB
 * Copyright (C) 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 org.restheart.db;

import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import static com.mongodb.client.model.Filters.eq;
import org.restheart.utils.HttpStatus;
import org.restheart.handlers.IllegalQueryParamenterException;
import org.restheart.handlers.RequestContext;
import org.restheart.handlers.injectors.LocalCachesSingleton;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.bson.BsonDocument;
import org.bson.BsonObjectId;
import org.bson.BsonString;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;

/**
 *
 * @author Andrea Di Cesare {@literal <andrea@softinstigate.com>}
 */
public class DbsDAO implements Database {
    public static final Bson PROPS_QUERY = eq("_id", "_properties");

    private static final Document FIELDS_TO_RETURN;

    static {
        FIELDS_TO_RETURN = new Document();
        FIELDS_TO_RETURN.put("_id", 1);
        FIELDS_TO_RETURN.put("_etag", 1);
    }

    /**
     * delegated object for collection operations
     */
    private final CollectionDAO collectionDAO;
    private final IndexDAO indexDAO;

    private final MongoClient client;

    public DbsDAO() {
        client = MongoDBClientSingleton.getInstance().getClient();
        this.collectionDAO = new CollectionDAO(client);
        this.indexDAO = new IndexDAO(client);
    }

    /**
     *
     * @param dbName
     * @return
     *
     */
    @Override
    public boolean doesDbExist(String dbName) {
        // at least one collection exists for an existing db
        return client.getDatabase(dbName).listCollectionNames().first() != null;
    }

    /**
     * Returns true if the collection exists
     *
     * @param dbName the database name of the collection
     * @param collName the collection name
     * @return true if the collection exists
     */
    @Override
    public boolean doesCollectionExist(String dbName, String collName) {
        return collectionDAO.doesCollectionExist(dbName, collName);
    }

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

    /**
     *
     * @param dbName
     * @return the MongoDatabase
     */
    @Override
    public MongoDatabase getDatabase(String dbName) {
        return client.getDatabase(dbName);
    }

    /**
     *
     * @return A ordered List of collection names
     */
    @Override
    public List<String> getCollectionNames(String dbName) {
        MongoDatabase db = getDatabase(dbName);

        List<String> _colls = new ArrayList<>();

        db.listCollectionNames().into(_colls);

        // filter out reserved dbs
        return _colls.stream().filter(coll -> !RequestContext.isReservedResourceCollection(coll)).sorted()
                .collect(Collectors.toList());
    }

    /**
     * @param colls the collections list got from getCollectionNames()
     * @return the number of collections in this db
     *
     */
    @Override
    public long getDBSize(final 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
     *
     */
    @Override
    public BsonDocument getDatabaseProperties(final String dbName) {
        MongoCollection<BsonDocument> propsColl = collectionDAO.getCollection(dbName, "_properties");

        BsonDocument props = propsColl.find(PROPS_QUERY).limit(1).first();

        if (props != null) {
            props.append("_id", new BsonString(dbName));
        } else if (doesDbExist(dbName)) {
            return new BsonDocument("_id", new BsonString(dbName));
        }

        return props;
    }

    /**
     * @param dbName
     * @param colls the collections list as got from getCollectionNames()
     * @param page
     * @param pagesize
     * @return the db data
     * @throws org.restheart.handlers.IllegalQueryParamenterException
     *
     */
    @Override
    public List<BsonDocument> getDatabaseData(final String dbName, final List<String> colls, final int page,
            final 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<BsonDocument> data = new ArrayList<>();

        _colls.stream().map((collName) -> {
            BsonDocument properties = new BsonDocument("_id", new BsonString(collName));

            BsonDocument collProperties;

            if (LocalCachesSingleton.isEnabled()) {
                collProperties = LocalCachesSingleton.getInstance().getCollectionProperties(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 newContent
     * @param requestEtag
     * @param patching
     * @return
     */
    @Override
    @SuppressWarnings("unchecked")
    public OperationResult upsertDB(final String dbName, final BsonDocument newContent, final String requestEtag,
            final boolean updating, final boolean patching, final boolean checkEtag) {

        if (patching && !updating) {
            return new OperationResult(HttpStatus.SC_NOT_FOUND);
        }

        ObjectId newEtag = new ObjectId();

        final BsonDocument content = DAOUtils.validContent(newContent);

        content.put("_etag", new BsonObjectId(newEtag));
        content.remove("_id"); // make sure we don't change this field

        MongoDatabase mdb = client.getDatabase(dbName);
        MongoCollection<BsonDocument> mcoll = mdb.getCollection("_properties", BsonDocument.class);

        if (checkEtag && updating) {
            BsonDocument oldProperties = mcoll.find(eq("_id", "_properties")).projection(FIELDS_TO_RETURN).first();

            if (oldProperties != null) {
                BsonValue oldEtag = oldProperties.get("_etag");

                if (oldEtag != null && requestEtag == null) {
                    return new OperationResult(HttpStatus.SC_CONFLICT, oldEtag);
                }

                BsonValue _requestEtag;

                if (ObjectId.isValid(requestEtag)) {
                    _requestEtag = new BsonObjectId(new ObjectId(requestEtag));
                } else {
                    // restheart generates ObjectId etags, but here we support
                    // strings as well
                    _requestEtag = new BsonString(requestEtag);
                }

                if (Objects.equals(_requestEtag, oldEtag)) {
                    return doDbPropsUpdate(patching, updating, mcoll, content, newEtag);
                } else {
                    return new OperationResult(HttpStatus.SC_PRECONDITION_FAILED, oldEtag);
                }
            } else {
                // this is the case when the db does not have properties
                // e.g. it has not been created by restheart
                return doDbPropsUpdate(patching, updating, mcoll, content, newEtag);
            }
        } else {
            return doDbPropsUpdate(patching, updating, mcoll, content, newEtag);
        }
    }

    private OperationResult doDbPropsUpdate(boolean patching, boolean updating, MongoCollection<BsonDocument> mcoll,
            BsonDocument dcontent, ObjectId newEtag) {
        if (patching) {
            DAOUtils.updateDocument(mcoll, "_properties", null, dcontent, false);
            return new OperationResult(HttpStatus.SC_OK, newEtag);
        } else if (updating) {
            DAOUtils.updateDocument(mcoll, "_properties", null, dcontent, true);
            return new OperationResult(HttpStatus.SC_OK, newEtag);
        } else {
            DAOUtils.updateDocument(mcoll, "_properties", null, dcontent, false);
            return new OperationResult(HttpStatus.SC_CREATED, newEtag);
        }
    }

    /**
     *
     * @param dbName
     * @param requestEtag
     * @return
     */
    @Override
    public OperationResult deleteDatabase(final String dbName, final String requestEtag, final boolean checkEtag) {
        MongoDatabase mdb = client.getDatabase(dbName);
        MongoCollection<Document> mcoll = mdb.getCollection("_properties");

        if (checkEtag) {
            Document properties = mcoll.find(eq("_id", "_properties")).projection(FIELDS_TO_RETURN).first();

            if (properties != null) {
                Object oldEtag = properties.get("_etag");

                if (oldEtag != null) {
                    if (requestEtag == null) {
                        return new OperationResult(HttpStatus.SC_CONFLICT, oldEtag);
                    } else if (!Objects.equals(oldEtag.toString(), requestEtag)) {
                        return new OperationResult(HttpStatus.SC_PRECONDITION_FAILED, oldEtag);
                    }
                }
            }
        }

        mdb.drop();
        return new OperationResult(HttpStatus.SC_NO_CONTENT);
    }

    @Override
    public BsonDocument getCollectionProperties(String dbName, String collName) {
        return collectionDAO.getCollectionProps(dbName, collName);
    }

    @Override
    public DBCollection getCollectionLegacy(String dbName, String collName) {
        return collectionDAO.getCollectionLegacy(dbName, collName);
    }

    @Override
    public MongoCollection<BsonDocument> getCollection(String dbName, String collName) {
        MongoDatabase mdb = client.getDatabase(dbName);
        MongoCollection<BsonDocument> mcoll = mdb.getCollection(collName, BsonDocument.class);
        return mcoll;
    }

    @Override
    public OperationResult upsertCollection(String dbName, String collName, BsonDocument content,
            String requestEtag, boolean updating, boolean patching, final boolean checkEtag) {
        return collectionDAO.upsertCollection(dbName, collName, content, requestEtag, updating, patching,
                checkEtag);
    }

    @Override
    public OperationResult deleteCollection(String dbName, String collectionName, String requestEtag,
            final boolean checkEtag) {
        return collectionDAO.deleteCollection(dbName, collectionName, requestEtag, checkEtag);
    }

    @Override
    public long getCollectionSize(final MongoCollection<BsonDocument> coll, BsonDocument filters) {
        return collectionDAO.getCollectionSize(coll, filters);
    }

    @Override
    public ArrayList<BsonDocument> getCollectionData(MongoCollection<BsonDocument> coll, int page, int pagesize,
            BsonDocument sortBy, BsonDocument filter, BsonDocument keys,
            CursorPool.EAGER_CURSOR_ALLOCATION_POLICY cursorAllocationPolicy) {
        return collectionDAO.getCollectionData(coll, page, pagesize, sortBy, filter, keys, cursorAllocationPolicy);
    }

    @Override
    public List<String> getDatabaseNames() {
        ArrayList<String> dbNames = new ArrayList<>();

        client.listDatabaseNames().into(dbNames);

        return dbNames;
    }

    @Override
    public int deleteIndex(String dbName, String collection, String indexId) {
        return indexDAO.deleteIndex(dbName, collection, indexId);
    }

    @Override
    public List<BsonDocument> getCollectionIndexes(String dbName, String collectionName) {
        return indexDAO.getCollectionIndexes(dbName, collectionName);
    }

    @Override
    public FindIterable<BsonDocument> getFindIterable(MongoCollection<BsonDocument> collection, BsonDocument sortBy,
            BsonDocument filters, BsonDocument keys) {
        return collectionDAO.getFindIterable(collection, sortBy, filters, keys);
    }

    @Override
    public void createIndex(String dbName, String collection, BsonDocument keys, BsonDocument options) {
        indexDAO.createIndex(dbName, collection, keys, options);
    }
}