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