Added JSON (flatfile) database type

This commit is contained in:
Florian CUNY 2018-10-28 16:34:43 +01:00
parent 10000b71f6
commit db5ac2d0e5
4 changed files with 222 additions and 1 deletions

View File

@ -2,6 +2,7 @@ package world.bentobox.bentobox.database;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.database.flatfile.FlatFileDatabase;
import world.bentobox.bentobox.database.json.JSONDatabase;
import world.bentobox.bentobox.database.mongodb.MongoDBDatabase;
import world.bentobox.bentobox.database.mysql.MySQLDatabase;
@ -9,7 +10,7 @@ public interface DatabaseSetup {
/**
* Gets the type of database being used.
* Currently supported options are FLATFILE, MYSQL and MONGODB.
* Currently supported options are FLATFILE, JSON, MYSQL and MONGODB.
* Default is FLATFILE.
* @return Database type
*/
@ -24,6 +25,7 @@ public interface DatabaseSetup {
enum DatabaseType {
FLATFILE(new FlatFileDatabase()),
JSON(new JSONDatabase()),
MYSQL(new MySQLDatabase()),
MONGODB(new MongoDBDatabase());

View File

@ -0,0 +1,13 @@
package world.bentobox.bentobox.database.json;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
import world.bentobox.bentobox.database.DatabaseSetup;
public class JSONDatabase implements DatabaseSetup {
@Override
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> dataObjectClass) {
return new JSONDatabaseHandler<>(BentoBox.getInstance(), dataObjectClass, new JSONDatabaseConnector(BentoBox.getInstance()));
}
}

View File

@ -0,0 +1,53 @@
package world.bentobox.bentobox.database.json;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.database.DatabaseConnector;
import java.io.File;
import java.util.UUID;
public class JSONDatabaseConnector implements DatabaseConnector {
private static final int MAX_LOOPS = 100;
private static final String DATABASE_FOLDER_NAME = "database";
private final BentoBox plugin;
private final File dataFolder;
public JSONDatabaseConnector(BentoBox plugin) {
this.plugin = plugin;
dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME);
}
@Override
public String getUniqueId(String tableName) {
UUID uuid = UUID.randomUUID();
File file = new File(dataFolder, tableName + File.separator + uuid.toString() + ".json");
int limit = 0;
while (file.exists() && limit++ < MAX_LOOPS) {
uuid = UUID.randomUUID();
file = new File(dataFolder, tableName + File.separator + uuid.toString() + ".json");
}
return uuid.toString();
}
@Override
public boolean uniqueIdExists(String tableName, String key) {
File file = new File(dataFolder, tableName + File.separator + key + ".json");
return file.exists();
}
@Override
public Object createConnection() {
return null; // Not used
}
@Override
public String getConnectionUrl() {
return null; // Not used
}
@Override
public void closeConnection() {
// Not used
}
}

View File

@ -0,0 +1,153 @@
package world.bentobox.bentobox.database.json;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.database.DatabaseConnector;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
public class JSONDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
/**
* Constructor
*
* @param plugin
* @param type The type of the objects that should be created and filled with
* values from the database or inserted into the database
* @param databaseConnector Contains the settings to create a connection to the database
*/
JSONDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
super(plugin, type, databaseConnector);
}
@Override
public List<T> loadObjects() {
// In this case, all the objects of a specific type are being loaded.
List<T> list = new ArrayList<>();
// The path is the simple name of the class
String path = dataObject.getSimpleName();
// The database folder name is in the plugin's data folder
File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME);
// The folder for the objects (tables in database terminology) is here
File tableFolder = new File(dataFolder, path);
if (!tableFolder.exists()) {
// Nothing there...
tableFolder.mkdirs();
}
// Load each object from the file system, filtered, non-null
for (File file: Objects.requireNonNull(tableFolder.listFiles((dir, name) -> name.toLowerCase(Locale.ENGLISH).endsWith(".json")))) {
try {
list.add(getGson().fromJson(new FileReader(file), dataObject));
} catch (FileNotFoundException e) {
plugin.logError("Could not load file '" + file.getName() + "': File not found.");
}
}
return list;
}
@Override
public T loadObject(String uniqueId) {
// Objects are loaded from a folder named after the simple name of the class being stored
String path = DATABASE_FOLDER_NAME + File.separator + dataObject.getSimpleName();
String fileName = path + File.separator + uniqueId;
if (!fileName.endsWith(".json")) {
fileName = fileName + ".json";
}
T result = null;
try {
result = getGson().fromJson(new FileReader(new File(plugin.getDataFolder(), fileName)), dataObject);
} catch (FileNotFoundException e) {
plugin.logError("Could not load file '" + fileName + "': File not found.");
}
return result;
}
@Override
public void saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
String path = DATABASE_FOLDER_NAME + File.separator + dataObject.getSimpleName();
// Obtain the value of uniqueId within the instance (which must be a DataObject)
PropertyDescriptor propertyDescriptor = new PropertyDescriptor("uniqueId", dataObject);
Method method = propertyDescriptor.getReadMethod();
String fileName = (String) method.invoke(instance);
File tableFolder = new File(plugin.getDataFolder(), path);
File file = new File(tableFolder, fileName);
if (!tableFolder.exists()) {
tableFolder.mkdirs();
}
String toStore = getGson().toJson(instance);
try {
File tmpFile = new File(tableFolder, fileName + ".bak");
if (file.exists()) {
// Make a backup of file
Files.copy(file.toPath(), tmpFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
FileWriter fileWriter = new FileWriter(file);
fileWriter.write(toStore);
fileWriter.close();
Files.deleteIfExists(tmpFile.toPath());
} catch (IOException e) {
plugin.logError("Could not save json file: " + path + " " + fileName + " " + e.getMessage());
}
}
@Override
public void deleteObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
// Obtain the value of uniqueId within the instance (which must be a DataObject)
PropertyDescriptor propertyDescriptor = new PropertyDescriptor("uniqueId", dataObject);
Method method = propertyDescriptor.getReadMethod();
String fileName = (String) method.invoke(instance);
// The filename of the JSON file is the value of uniqueId field plus .json. Sometimes the .json is already appended.
if (!fileName.endsWith(".json")) {
fileName = fileName + ".json";
}
// Get the database and table folders
File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME);
File tableFolder = new File(dataFolder, dataObject.getSimpleName());
if (tableFolder.exists()) {
// Obtain the file and delete it
File file = new File(tableFolder, fileName);
try {
Files.delete(file.toPath());
} catch (IOException e) {
plugin.logError("Could not delete json database object! " + file.getName() + " - " + e.getMessage());
}
}
}
@Override
public boolean objectExists(String uniqueId) {
// Check if the uniqueId (key) exists in the file system
return databaseConnector.uniqueIdExists(dataObject.getSimpleName(), uniqueId);
}
@Override
public void close() {
// Not used
}
}