/* * ToroDB * Copyright © 2014 8Kdata Technology (www.8kdata.com) * * 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.torodb.mongodb.commands.signatures.diagnostic; import com.eightkdata.mongowp.bson.BsonArray; import com.eightkdata.mongowp.bson.BsonDocument; import com.eightkdata.mongowp.bson.BsonType; import com.eightkdata.mongowp.bson.BsonValue; import com.eightkdata.mongowp.bson.annotations.NotMutable; import com.eightkdata.mongowp.exceptions.BadValueException; import com.eightkdata.mongowp.exceptions.NoSuchKeyException; import com.eightkdata.mongowp.exceptions.TypesMismatchException; import com.eightkdata.mongowp.fields.ArrayField; import com.eightkdata.mongowp.fields.BooleanField; import com.eightkdata.mongowp.fields.LongField; import com.eightkdata.mongowp.fields.StringField; import com.eightkdata.mongowp.server.api.impl.AbstractNotAliasableCommand; import com.eightkdata.mongowp.server.api.tools.Empty; import com.eightkdata.mongowp.utils.BsonArrayBuilder; import com.eightkdata.mongowp.utils.BsonDocumentBuilder; import com.eightkdata.mongowp.utils.BsonReaderTool; import com.google.common.collect.Lists; import com.torodb.mongodb.commands.signatures.diagnostic.ListDatabasesCommand.ListDatabasesReply; import com.torodb.mongodb.commands.tools.EmptyCommandArgumentMarshaller; import org.apache.logging.log4j.LogManager; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class ListDatabasesCommand extends AbstractNotAliasableCommand<Empty, ListDatabasesReply> { private static final org.apache.logging.log4j.Logger LOGGER = LogManager.getLogger(ListDatabasesCommand.class); public static final ListDatabasesCommand INSTANCE = new ListDatabasesCommand(); private static final String COMMAND_NAME = "listDatabases"; public ListDatabasesCommand() { super(COMMAND_NAME); } @Override public boolean isAdminOnly() { return true; } @Override public Class<? extends Empty> getArgClass() { return Empty.class; } @Override public Empty unmarshallArg(BsonDocument requestDoc) { return Empty.getInstance(); } @Override public BsonDocument marshallArg(Empty request) { return EmptyCommandArgumentMarshaller.marshallEmptyArgument(this); } @Override public Class<? extends ListDatabasesReply> getResultClass() { return ListDatabasesReply.class; } @Override public BsonDocument marshallResult(ListDatabasesReply reply) { return reply.marshall(); } @Override public ListDatabasesReply unmarshallResult(BsonDocument replyDoc) throws TypesMismatchException, NoSuchKeyException, BadValueException { return ListDatabasesReply.unmarshall(replyDoc); } public static class ListDatabasesReply { private static final ArrayField DATABASES_FIELD = new ArrayField("databases"); private static final LongField TOTAL_SIZE_FIELD = new LongField("totalSize"); private static final LongField TOTAL_SIZE_MB_FIELD = new LongField("totalSizeMb"); private final List<DatabaseEntry> databases; private final long totalSize; public ListDatabasesReply(@NotMutable List<DatabaseEntry> databases, long sizeOnDisk) { this.databases = Collections.unmodifiableList(databases); this.totalSize = sizeOnDisk; long temp = 0; for (DatabaseEntry database : databases) { temp += database.getSizeOnDisk(); } if (temp != sizeOnDisk) { LOGGER.warn("Inconsistent data provided to " + getClass() + " constructor. Recived a total size of {} but {} was " + "calculated. Using provided value", sizeOnDisk, temp); } } public List<DatabaseEntry> getDatabases() { return databases; } /** * * @return the total size of all databases file on disk in bytes */ public long getTotalSize() { return totalSize; } private static ListDatabasesReply unmarshall( BsonDocument replyDoc) throws TypesMismatchException, NoSuchKeyException { BsonArray arr = BsonReaderTool.getArray(replyDoc, DATABASES_FIELD); int i = 0; ArrayList<DatabaseEntry> databases = Lists.newArrayListWithCapacity(arr.size()); for (BsonValue element : arr) { if (!element.isDocument()) { String field = DATABASES_FIELD.getFieldName() + "." + i; throw new TypesMismatchException( field, BsonType.DOCUMENT, element.getType(), "Element " + field + " is not a document as it was " + "expected but a " + element.getType()); } databases.add(new DatabaseEntry(element.asDocument())); i++; } long sizeOnDisk = BsonReaderTool.getNumeric(replyDoc, TOTAL_SIZE_FIELD).longValue(); return new ListDatabasesReply(databases, sizeOnDisk); } private BsonDocument marshall() { BsonArrayBuilder arr = new BsonArrayBuilder(); for (DatabaseEntry database : databases) { arr.add(database.marshall()); } return new BsonDocumentBuilder() .append(DATABASES_FIELD, arr.build()) .append(TOTAL_SIZE_FIELD, totalSize) .append(TOTAL_SIZE_MB_FIELD, totalSize / (1000 * 1000)) .build(); } public static class DatabaseEntry { private static final StringField NAME_FIELD = new StringField("name"); private static final LongField SIZE_ON_DISK_FIELD = new LongField("sizeOnDisk"); private static final BooleanField EMPTY_FIELD = new BooleanField("empty"); private final String name; private final long sizeOnDisk; private final boolean empty; public DatabaseEntry(String name, long sizeOnDisk, boolean empty) { this.name = name; this.sizeOnDisk = sizeOnDisk; this.empty = empty; } private DatabaseEntry(BsonDocument doc) throws TypesMismatchException, NoSuchKeyException { name = BsonReaderTool.getString(doc, NAME_FIELD); sizeOnDisk = BsonReaderTool.getNumeric(doc, SIZE_ON_DISK_FIELD).longValue(); empty = BsonReaderTool.getBoolean(doc, EMPTY_FIELD); } /** * * @return the name of the database */ public String getName() { return name; } /** * * @return the total size of the database file on disk in bytes */ public long getSizeOnDisk() { return sizeOnDisk; } /** * * @return whether the databse has data */ public boolean isEmpty() { return empty; } private BsonDocument marshall() { return new BsonDocumentBuilder() .append(NAME_FIELD, name) .append(SIZE_ON_DISK_FIELD, sizeOnDisk) .append(EMPTY_FIELD, empty) .build(); } } } }