2018-07-31 18:03:32 +02:00
|
|
|
package world.bentobox.bentobox.database.mongodb;
|
2018-03-19 05:54:24 +01:00
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
2020-04-26 01:00:49 +02:00
|
|
|
import java.util.concurrent.CompletableFuture;
|
2018-03-19 05:54:24 +01:00
|
|
|
|
|
|
|
import org.bson.Document;
|
2018-04-01 00:17:58 +02:00
|
|
|
import org.bson.conversions.Bson;
|
2019-01-14 07:17:36 +01:00
|
|
|
import org.bukkit.Bukkit;
|
2022-01-22 22:14:57 +01:00
|
|
|
import org.eclipse.jdt.annotation.NonNull;
|
2018-03-19 05:54:24 +01:00
|
|
|
|
|
|
|
import com.google.gson.Gson;
|
2019-12-29 14:55:57 +01:00
|
|
|
import com.mongodb.MongoClientException;
|
2020-05-13 10:49:42 +02:00
|
|
|
import com.mongodb.MongoNamespace;
|
2019-12-29 14:55:57 +01:00
|
|
|
import com.mongodb.MongoTimeoutException;
|
2018-03-19 05:54:24 +01:00
|
|
|
import com.mongodb.client.MongoCollection;
|
|
|
|
import com.mongodb.client.MongoDatabase;
|
2018-04-01 00:17:58 +02:00
|
|
|
import com.mongodb.client.model.FindOneAndReplaceOptions;
|
2018-03-19 05:54:24 +01:00
|
|
|
import com.mongodb.client.model.IndexOptions;
|
|
|
|
import com.mongodb.client.model.Indexes;
|
|
|
|
import com.mongodb.util.JSON;
|
|
|
|
|
2018-07-31 18:03:32 +02:00
|
|
|
import world.bentobox.bentobox.BentoBox;
|
2018-08-06 15:32:15 +02:00
|
|
|
import world.bentobox.bentobox.database.DatabaseConnector;
|
2019-01-07 16:46:59 +01:00
|
|
|
import world.bentobox.bentobox.database.json.AbstractJSONDatabaseHandler;
|
2018-07-31 18:03:32 +02:00
|
|
|
import world.bentobox.bentobox.database.objects.DataObject;
|
2020-05-13 10:49:42 +02:00
|
|
|
import world.bentobox.bentobox.database.objects.Table;
|
2018-03-19 05:54:24 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Class that inserts a <T> into the corresponding database-table.
|
|
|
|
*
|
|
|
|
* @author tastybento
|
|
|
|
*
|
|
|
|
* @param <T>
|
|
|
|
*/
|
|
|
|
@SuppressWarnings("deprecation")
|
2018-10-28 15:28:43 +01:00
|
|
|
public class MongoDBDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
2018-03-19 05:54:24 +01:00
|
|
|
|
2018-03-31 18:24:00 +02:00
|
|
|
private static final String UNIQUEID = "uniqueId";
|
|
|
|
private static final String MONGO_ID = "_id";
|
2018-04-01 00:17:58 +02:00
|
|
|
|
2018-03-19 05:54:24 +01:00
|
|
|
private MongoCollection<Document> collection;
|
2021-08-30 03:17:21 +02:00
|
|
|
private final DatabaseConnector dbConnecter;
|
2018-03-19 05:54:24 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles the connection to the database and creation of the initial database schema (tables) for
|
|
|
|
* the class that will be stored.
|
2018-07-30 02:09:48 +02:00
|
|
|
* @param plugin - plugin object
|
2018-03-19 05:54:24 +01:00
|
|
|
* @param type - the type of class to be stored in the database. Must inherit DataObject
|
2018-04-19 04:07:20 +02:00
|
|
|
* @param dbConnecter - authentication details for the database
|
2018-03-19 05:54:24 +01:00
|
|
|
*/
|
2018-10-28 15:34:02 +01:00
|
|
|
MongoDBDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector dbConnecter) {
|
2018-04-19 04:07:20 +02:00
|
|
|
super(plugin, type, dbConnecter);
|
|
|
|
this.dbConnecter = dbConnecter;
|
2018-10-28 15:28:43 +01:00
|
|
|
|
2019-10-19 22:57:19 +02:00
|
|
|
boolean connected = true; // if it is set to false, it will consider there has been an error upon connecting.
|
|
|
|
try {
|
|
|
|
// Connection to the database
|
|
|
|
MongoDatabase database = (MongoDatabase) dbConnecter.createConnection(dataObject);
|
|
|
|
if (database == null) {
|
|
|
|
plugin.logError("Could not connect to the database. Are the credentials in the config.yml file correct?");
|
|
|
|
connected = false;
|
|
|
|
} else {
|
2020-05-13 10:49:42 +02:00
|
|
|
// Check for old collections
|
|
|
|
String oldName = plugin.getSettings().getDatabasePrefix() + type.getCanonicalName();
|
|
|
|
String newName = getName(plugin, dataObject);
|
|
|
|
if (!oldName.equals((newName)) && collectionExists(database, oldName) && !collectionExists(database, newName)){
|
|
|
|
collection = database.getCollection(oldName);
|
|
|
|
collection.renameCollection(new MongoNamespace(database.getName(), newName));
|
|
|
|
} else {
|
|
|
|
collection = database.getCollection(newName);
|
|
|
|
}
|
2019-10-19 22:57:19 +02:00
|
|
|
IndexOptions indexOptions = new IndexOptions().unique(true);
|
|
|
|
collection.createIndex(Indexes.text(UNIQUEID), indexOptions);
|
|
|
|
}
|
|
|
|
} catch (MongoTimeoutException e) {
|
|
|
|
plugin.logError("Could not connect to the database. MongoDB timed out.");
|
|
|
|
plugin.logError("Error code: " + e.getCode());
|
|
|
|
plugin.logError("Errors: " + String.join(", ", e.getErrorLabels()));
|
|
|
|
connected = false;
|
|
|
|
} catch (MongoClientException e) {
|
|
|
|
plugin.logError("Could not connect to the database. An unhandled error occurred.");
|
|
|
|
plugin.logStacktrace(e);
|
|
|
|
connected = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!connected) {
|
|
|
|
plugin.logWarning("Disabling BentoBox...");
|
2019-01-14 07:17:36 +01:00
|
|
|
Bukkit.getPluginManager().disablePlugin(plugin);
|
|
|
|
}
|
2018-03-19 05:54:24 +01:00
|
|
|
}
|
|
|
|
|
2020-05-13 10:49:42 +02:00
|
|
|
private boolean collectionExists(MongoDatabase database, final String collectionName) {
|
|
|
|
for (final String name : database.listCollectionNames()) {
|
|
|
|
if (name.equalsIgnoreCase(collectionName)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private String getName(BentoBox plugin, Class<T> type) {
|
|
|
|
return plugin.getSettings().getDatabasePrefix() +
|
|
|
|
(type.getAnnotation(Table.class) == null ?
|
|
|
|
type.getCanonicalName()
|
|
|
|
: type.getAnnotation(Table.class)
|
|
|
|
.name());
|
|
|
|
}
|
|
|
|
|
2018-03-19 05:54:24 +01:00
|
|
|
@Override
|
|
|
|
public List<T> loadObjects() {
|
|
|
|
List<T> list = new ArrayList<>();
|
2018-10-28 15:28:43 +01:00
|
|
|
Gson gson = getGson();
|
2018-06-01 03:52:05 +02:00
|
|
|
for (Document document : collection.find(new Document())) {
|
2018-03-19 05:54:24 +01:00
|
|
|
// The deprecated serialize option does not have a viable alternative without involving a huge amount of custom code
|
2018-06-01 03:52:05 +02:00
|
|
|
String json = JSON.serialize(document);
|
2018-03-31 18:24:00 +02:00
|
|
|
json = json.replaceFirst(MONGO_ID, UNIQUEID);
|
2019-02-08 04:18:46 +01:00
|
|
|
try {
|
|
|
|
list.add(gson.fromJson(json, dataObject));
|
|
|
|
} catch (Exception e) {
|
|
|
|
plugin.logError("Could not load object :" + e.getMessage());
|
|
|
|
}
|
2018-03-19 05:54:24 +01:00
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2021-09-18 17:15:15 +02:00
|
|
|
public T loadObject(@NonNull String uniqueId) {
|
2018-03-31 18:24:00 +02:00
|
|
|
Document doc = collection.find(new Document(MONGO_ID, uniqueId)).limit(1).first();
|
2018-10-28 15:28:43 +01:00
|
|
|
Gson gson = getGson();
|
2018-08-04 06:30:06 +02:00
|
|
|
String json = JSON.serialize(doc).replaceFirst(MONGO_ID, UNIQUEID);
|
|
|
|
// load single object
|
|
|
|
return gson.fromJson(json, dataObject);
|
2018-03-19 05:54:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-04-26 01:00:49 +02:00
|
|
|
public CompletableFuture<Boolean> saveObject(T instance) {
|
|
|
|
CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
|
2019-01-07 16:46:59 +01:00
|
|
|
// Null check
|
|
|
|
if (instance == null) {
|
|
|
|
plugin.logError("MongoDB database request to store a null. ");
|
2020-04-26 01:00:49 +02:00
|
|
|
completableFuture.complete(false);
|
|
|
|
return completableFuture;
|
2019-01-07 16:46:59 +01:00
|
|
|
}
|
2021-07-31 17:48:26 +02:00
|
|
|
if (!(instance instanceof DataObject dataObj)) {
|
2018-04-28 06:02:15 +02:00
|
|
|
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
2020-04-26 01:00:49 +02:00
|
|
|
completableFuture.complete(false);
|
|
|
|
return completableFuture;
|
2018-03-19 05:54:24 +01:00
|
|
|
}
|
|
|
|
try {
|
2018-10-28 15:28:43 +01:00
|
|
|
Gson gson = getGson();
|
2018-03-19 05:54:24 +01:00
|
|
|
String toStore = gson.toJson(instance);
|
|
|
|
// Change uniqueId to _id
|
2018-03-31 18:24:00 +02:00
|
|
|
toStore = toStore.replaceFirst(UNIQUEID, MONGO_ID);
|
|
|
|
// This parses JSON to a Mongo Document
|
2018-03-19 05:54:24 +01:00
|
|
|
Document document = Document.parse(toStore);
|
2018-04-01 00:17:58 +02:00
|
|
|
// Filter based on the id
|
|
|
|
Bson filter = new Document(MONGO_ID, dataObj.getUniqueId());
|
|
|
|
// Set the options to upsert (update or insert if doc is not there)
|
|
|
|
FindOneAndReplaceOptions options = new FindOneAndReplaceOptions().upsert(true);
|
|
|
|
// Do the deed
|
2020-05-02 01:04:25 +02:00
|
|
|
collection.findOneAndReplace(filter, document, options);
|
|
|
|
completableFuture.complete(true);
|
2018-03-19 05:54:24 +01:00
|
|
|
} catch (Exception e) {
|
2018-04-28 06:02:15 +02:00
|
|
|
plugin.logError("Could not save object " + instance.getClass().getName() + " " + e.getMessage());
|
2020-04-26 01:00:49 +02:00
|
|
|
completableFuture.complete(false);
|
2018-03-19 05:54:24 +01:00
|
|
|
}
|
2020-04-26 01:00:49 +02:00
|
|
|
return completableFuture;
|
2018-03-19 05:54:24 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 02:12:30 +01:00
|
|
|
@Override
|
2019-03-11 08:41:41 +01:00
|
|
|
public void deleteID(String uniqueId) {
|
2019-01-13 02:12:30 +01:00
|
|
|
try {
|
2019-03-11 08:41:41 +01:00
|
|
|
collection.findOneAndDelete(new Document(MONGO_ID, uniqueId));
|
2019-01-13 02:12:30 +01:00
|
|
|
} catch (Exception e) {
|
2020-05-13 10:49:42 +02:00
|
|
|
plugin.logError("Could not delete object " + getName(plugin, dataObject) + " " + uniqueId + " " + e.getMessage());
|
2019-01-13 02:12:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-19 05:54:24 +01:00
|
|
|
@Override
|
|
|
|
public void deleteObject(T instance) {
|
2019-01-07 16:46:59 +01:00
|
|
|
// Null check
|
|
|
|
if (instance == null) {
|
|
|
|
plugin.logError("MondDB database request to delete a null. ");
|
|
|
|
return;
|
|
|
|
}
|
2018-03-19 05:54:24 +01:00
|
|
|
if (!(instance instanceof DataObject)) {
|
2018-04-28 06:02:15 +02:00
|
|
|
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
2018-03-19 05:54:24 +01:00
|
|
|
return;
|
|
|
|
}
|
2019-01-13 02:12:30 +01:00
|
|
|
deleteID(((DataObject)instance).getUniqueId());
|
2018-03-19 05:54:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2018-06-10 01:40:38 +02:00
|
|
|
public boolean objectExists(String uniqueId) {
|
|
|
|
return collection.find(new Document(MONGO_ID, uniqueId)).first() != null;
|
2018-03-19 05:54:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void close() {
|
2019-07-06 05:11:06 +02:00
|
|
|
dbConnecter.closeConnection(dataObject);
|
2018-03-19 05:54:24 +01:00
|
|
|
}
|
|
|
|
}
|