mirror of
https://github.com/ChestShop-authors/ChestShop-3.git
synced 2024-11-23 18:45:31 +01:00
Remove logging to database!
This commit is contained in:
parent
9200928dca
commit
6466b2d6aa
@ -7,17 +7,14 @@ import com.Acrobot.ChestShop.Commands.Toggle;
|
||||
import com.Acrobot.ChestShop.Commands.Version;
|
||||
import com.Acrobot.ChestShop.Configuration.Messages;
|
||||
import com.Acrobot.ChestShop.Configuration.Properties;
|
||||
import com.Acrobot.ChestShop.DB.Generator;
|
||||
import com.Acrobot.ChestShop.DB.Queue;
|
||||
import com.Acrobot.ChestShop.DB.Transaction;
|
||||
import com.Acrobot.ChestShop.Database.Account;
|
||||
import com.Acrobot.ChestShop.Database.ConnectionManager;
|
||||
import com.Acrobot.ChestShop.Database.Migrations;
|
||||
import com.Acrobot.ChestShop.Listeners.Block.BlockPlace;
|
||||
import com.Acrobot.ChestShop.Listeners.Block.Break.ChestBreak;
|
||||
import com.Acrobot.ChestShop.Listeners.Block.Break.SignBreak;
|
||||
import com.Acrobot.ChestShop.Listeners.Block.SignCreate;
|
||||
import com.Acrobot.ChestShop.Listeners.Economy.ServerAccountCorrector;
|
||||
import com.Acrobot.ChestShop.Listeners.Economy.TaxModule;
|
||||
import com.Acrobot.ChestShop.Listeners.GarbageTextListener;
|
||||
import com.Acrobot.ChestShop.Listeners.Item.ItemMoveListener;
|
||||
import com.Acrobot.ChestShop.Listeners.ItemInfoListener;
|
||||
import com.Acrobot.ChestShop.Listeners.Modules.DiscountModule;
|
||||
@ -39,12 +36,6 @@ import com.Acrobot.ChestShop.Metadata.ItemDatabase;
|
||||
import com.Acrobot.ChestShop.Signs.RestrictedSign;
|
||||
import com.Acrobot.ChestShop.UUIDs.NameManager;
|
||||
import com.Acrobot.ChestShop.Updater.Updater;
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.j256.ormlite.dao.Dao;
|
||||
import com.j256.ormlite.dao.DaoManager;
|
||||
import com.j256.ormlite.jdbc.JdbcConnectionSource;
|
||||
import com.j256.ormlite.support.ConnectionSource;
|
||||
import com.lennardf1989.bukkitex.Database;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Marker;
|
||||
@ -55,7 +46,6 @@ import org.apache.logging.log4j.core.filter.AbstractFilter;
|
||||
import org.apache.logging.log4j.message.Message;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.Listener;
|
||||
@ -65,8 +55,6 @@ import org.mcstats.Metrics;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.FileHandler;
|
||||
import java.util.logging.Logger;
|
||||
@ -82,19 +70,20 @@ public class ChestShop extends JavaPlugin {
|
||||
private static PluginDescriptionFile description;
|
||||
|
||||
private static File dataFolder;
|
||||
private static EbeanServer database;
|
||||
private static ItemDatabase itemDatabase;
|
||||
|
||||
private static Logger logger;
|
||||
private FileHandler handler;
|
||||
|
||||
public void onEnable() {
|
||||
plugin = this;
|
||||
logger = getLogger();
|
||||
public ChestShop() {
|
||||
dataFolder = getDataFolder();
|
||||
logger = getLogger();
|
||||
description = getDescription();
|
||||
server = getServer();
|
||||
plugin = this;
|
||||
}
|
||||
|
||||
public void onEnable() {
|
||||
Configuration.pairFileAndClass(loadFile("config.yml"), Properties.class);
|
||||
Configuration.pairFileAndClass(loadFile("local.yml"), Messages.class);
|
||||
|
||||
@ -109,15 +98,6 @@ public class ChestShop extends JavaPlugin {
|
||||
|
||||
registerEvents();
|
||||
|
||||
if (Properties.LOG_TO_DATABASE || Properties.GENERATE_STATISTICS_PAGE) {
|
||||
setupDB();
|
||||
}
|
||||
|
||||
if (Properties.GENERATE_STATISTICS_PAGE) {
|
||||
File htmlFolder = new File(Properties.STATISTICS_PAGE_PATH);
|
||||
scheduleTask(new Generator(htmlFolder), 300L, Properties.STATISTICS_PAGE_GENERATION_INTERVAL * 20L);
|
||||
}
|
||||
|
||||
if (Properties.LOG_TO_FILE) {
|
||||
File log = loadFile("ChestShop.log");
|
||||
|
||||
@ -181,14 +161,13 @@ public class ChestShop extends JavaPlugin {
|
||||
});
|
||||
}
|
||||
|
||||
private final int CURRENT_DATABASE_VERSION = 2;
|
||||
|
||||
private void handleMigrations() {
|
||||
File versionFile = loadFile("version");
|
||||
YamlConfiguration previousVersion = YamlConfiguration.loadConfiguration(versionFile);
|
||||
|
||||
if (previousVersion.get("version") == null) {
|
||||
previousVersion.set("version", CURRENT_DATABASE_VERSION);
|
||||
previousVersion.set("version", Migrations.CURRENT_DATABASE_VERSION);
|
||||
|
||||
try {
|
||||
previousVersion.save(versionFile);
|
||||
@ -198,32 +177,16 @@ public class ChestShop extends JavaPlugin {
|
||||
}
|
||||
|
||||
int lastVersion = previousVersion.getInt("version");
|
||||
int newVersion = Migrations.migrate(lastVersion);
|
||||
|
||||
if (CURRENT_DATABASE_VERSION != lastVersion) {
|
||||
logger.info("Updating database...");
|
||||
}
|
||||
if (lastVersion != newVersion) {
|
||||
previousVersion.set("version", newVersion);
|
||||
|
||||
switch (lastVersion) {
|
||||
case 1:
|
||||
try {
|
||||
File databaseFile = ChestShop.loadFile("users.db");
|
||||
String uri = ConnectionManager.getURI(databaseFile);
|
||||
ConnectionSource connection = new JdbcConnectionSource(uri);
|
||||
|
||||
Dao<Account, String> accounts = DaoManager.createDao(connection, Account.class);
|
||||
|
||||
accounts.executeRaw("ALTER TABLE `accounts` ADD COLUMN lastSeenName VARCHAR");
|
||||
|
||||
previousVersion.set("version", 2);
|
||||
previousVersion.save(versionFile);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
case CURRENT_DATABASE_VERSION:
|
||||
default:
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,6 +259,7 @@ public class ChestShop extends JavaPlugin {
|
||||
registerEvent(new PlayerTeleport());
|
||||
|
||||
registerEvent(new ItemInfoListener());
|
||||
registerEvent(new GarbageTextListener());
|
||||
|
||||
registerEvent(new RestrictedSign());
|
||||
|
||||
@ -394,40 +358,6 @@ public class ChestShop extends JavaPlugin {
|
||||
new Updater(this, PROJECT_BUKKITDEV_ID, this.getFile(), Updater.UpdateType.DEFAULT, true);
|
||||
}
|
||||
|
||||
///////////////////// DATABASE STUFF ////////////////////////////////
|
||||
private void setupDB() {
|
||||
loadFile(new File("ebean.properties"));
|
||||
|
||||
Database DB;
|
||||
|
||||
DB = new Database(this) {
|
||||
protected java.util.List<Class<?>> getDatabaseClasses() {
|
||||
List<Class<?>> list = new ArrayList<Class<?>>();
|
||||
list.add(Transaction.class);
|
||||
return list;
|
||||
}
|
||||
};
|
||||
|
||||
FileConfiguration config = YamlConfiguration.loadConfiguration(new File("bukkit.yml"));
|
||||
|
||||
DB.initializeDatabase(
|
||||
config.getString("database.driver"),
|
||||
config.getString("database.url"),
|
||||
config.getString("database.username"),
|
||||
config.getString("database.password"),
|
||||
config.getString("database.isolation")
|
||||
);
|
||||
|
||||
database = DB.getDatabase();
|
||||
|
||||
scheduleTask(new Queue(), 200L, 200L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EbeanServer getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static ItemDatabase getItemDatabase() {
|
||||
@ -454,10 +384,6 @@ public class ChestShop extends JavaPlugin {
|
||||
return description.getName();
|
||||
}
|
||||
|
||||
public static EbeanServer getDB() {
|
||||
return database;
|
||||
}
|
||||
|
||||
public static List<String> getDependencies() {
|
||||
return description.getSoftDepend();
|
||||
}
|
||||
|
@ -1,171 +0,0 @@
|
||||
package com.Acrobot.ChestShop.DB;
|
||||
|
||||
import com.Acrobot.Breeze.Utils.StringUtil;
|
||||
import com.Acrobot.ChestShop.ChestShop;
|
||||
import com.avaje.ebean.ExpressionList;
|
||||
import org.bukkit.Material;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Acrobot
|
||||
*/
|
||||
public class Generator implements Runnable {
|
||||
private final File pagePath;
|
||||
|
||||
private static String header;
|
||||
private static String row;
|
||||
private static String footer;
|
||||
|
||||
private static BufferedWriter buf;
|
||||
|
||||
|
||||
public Generator(File pagePath) {
|
||||
this.pagePath = pagePath;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
header = fileToString("header");
|
||||
row = fileToString("row");
|
||||
footer = fileToString("footer");
|
||||
|
||||
if (row.isEmpty()) {
|
||||
ChestShop.getBukkitLogger().severe("You lack the necessary HTML files in your plugins/ChestShop/HTML folder!");
|
||||
return;
|
||||
}
|
||||
|
||||
generateStats();
|
||||
}
|
||||
|
||||
private void fileStart() throws IOException {
|
||||
FileWriter fw = new FileWriter(pagePath);
|
||||
fw.write(header);
|
||||
fw.close();
|
||||
}
|
||||
|
||||
private void fileEnd(long generationTime) throws IOException {
|
||||
FileWriter fw = new FileWriter(pagePath, true);
|
||||
fw.write(footer.replace("%time", String.valueOf(generationTime)));
|
||||
fw.close();
|
||||
}
|
||||
|
||||
private static String fileToString(String fileName) {
|
||||
try {
|
||||
File htmlFolder = new File(ChestShop.getFolder(), "HTML");
|
||||
File fileToRead = new File(htmlFolder, fileName + ".html");
|
||||
|
||||
FileReader rd = new FileReader(fileToRead);
|
||||
char[] buf = new char[(int) fileToRead.length()];
|
||||
rd.read(buf);
|
||||
return new String(buf);
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static double generateItemTotal(Material item, boolean bought, boolean sold) {
|
||||
List<Transaction> list;
|
||||
ExpressionList<Transaction> checkIf = ChestShop.getDB().find(Transaction.class).where();
|
||||
|
||||
if (bought || sold) {
|
||||
list = checkIf.eq("buy", bought ? 1 : 0).eq("itemID", item.getId()).findList();
|
||||
} else {
|
||||
list = checkIf.eq("itemID", item.getId()).findList();
|
||||
}
|
||||
|
||||
return countTransactionAmount(list);
|
||||
}
|
||||
|
||||
private static double countTransactionAmount(List<Transaction> list) {
|
||||
double amount = 0;
|
||||
|
||||
for (Transaction transaction : list) {
|
||||
amount += transaction.getAmount();
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
private static double generateTotalBought(Material item) {
|
||||
return generateItemTotal(item, true, false);
|
||||
}
|
||||
|
||||
private static double generateTotalSold(Material item) {
|
||||
return generateItemTotal(item, false, true);
|
||||
}
|
||||
|
||||
private static double generateItemTotal(Material item) {
|
||||
return generateItemTotal(item, false, false);
|
||||
}
|
||||
|
||||
private static float generateAveragePrice(Material item) {
|
||||
float price = 0;
|
||||
List<Transaction> prices = ChestShop.getDB().find(Transaction.class).where().eq("itemID", item.getId()).eq("buy", true).findList();
|
||||
|
||||
for (Transaction t : prices) {
|
||||
price += t.getAveragePricePerItem();
|
||||
}
|
||||
|
||||
float toReturn = price / prices.size();
|
||||
return (!Float.isNaN(toReturn) ? toReturn : 0);
|
||||
}
|
||||
|
||||
private static float generateAverageBuyPrice(Material item) {
|
||||
return generateAveragePrice(item);
|
||||
}
|
||||
|
||||
private static void generateItemStats(Material material) throws IOException {
|
||||
double total = generateItemTotal(material);
|
||||
|
||||
if (total == 0) return;
|
||||
|
||||
double bought = generateTotalBought(material);
|
||||
double sold = generateTotalSold(material);
|
||||
|
||||
String matName = StringUtil.capitalizeFirstLetter(material.name(), '_');
|
||||
|
||||
int maxStackSize = material.getMaxStackSize();
|
||||
|
||||
float buyPrice = generateAverageBuyPrice(material);
|
||||
|
||||
buf.write(row.replace("%material", matName)
|
||||
.replace("%total", String.valueOf(total))
|
||||
.replace("%bought", String.valueOf(bought))
|
||||
.replace("%sold", String.valueOf(sold))
|
||||
.replace("%maxStackSize", String.valueOf(maxStackSize))
|
||||
.replace("%pricePerStack", String.valueOf((buyPrice * maxStackSize)))
|
||||
.replace("%pricePerItem", String.valueOf(buyPrice)));
|
||||
}
|
||||
|
||||
private void generateStats() {
|
||||
try {
|
||||
File parentFolder = pagePath.getParentFile();
|
||||
if (!parentFolder.exists()) {
|
||||
parentFolder.mkdir();
|
||||
}
|
||||
|
||||
fileStart();
|
||||
|
||||
buf = new BufferedWriter(new FileWriter(pagePath, true));
|
||||
|
||||
long genTime = System.currentTimeMillis();
|
||||
for (Material m : Material.values()) {
|
||||
generateItemStats(m);
|
||||
}
|
||||
|
||||
buf.close();
|
||||
|
||||
long generationTime = (System.currentTimeMillis() - genTime) / 1000;
|
||||
|
||||
fileEnd(generationTime);
|
||||
} catch (Exception e) {
|
||||
ChestShop.getBukkitLogger().severe("Couldn't generate statistics page!");
|
||||
|
||||
if (!(e instanceof PersistenceException)) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package com.Acrobot.ChestShop.DB;
|
||||
|
||||
import com.Acrobot.ChestShop.ChestShop;
|
||||
import com.Acrobot.ChestShop.Configuration.Properties;
|
||||
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* @author Acrobot
|
||||
*/
|
||||
public class Queue implements Runnable {
|
||||
private static final ConcurrentLinkedQueue<Transaction> queue = new ConcurrentLinkedQueue<Transaction>();
|
||||
|
||||
public static void addToQueue(Transaction t) {
|
||||
queue.add(t);
|
||||
}
|
||||
|
||||
public synchronized void run() {
|
||||
if (Properties.RECORD_TIME_TO_LIVE != -1)
|
||||
deleteOld();
|
||||
|
||||
ChestShop.getDB().save(queue);
|
||||
queue.clear();
|
||||
}
|
||||
|
||||
public synchronized static boolean deleteOld() {
|
||||
try {
|
||||
ChestShop.getDB().delete(getOld());
|
||||
return true;
|
||||
} catch (OptimisticLockException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static List getOld() throws OptimisticLockException {
|
||||
return ChestShop
|
||||
.getDB()
|
||||
.find(Transaction.class)
|
||||
.where()
|
||||
.lt("sec", (System.currentTimeMillis() / 1000L) - Properties.RECORD_TIME_TO_LIVE)
|
||||
.findList();
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
package com.Acrobot.ChestShop.DB;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author Acrobot
|
||||
*/
|
||||
@Entity()
|
||||
@Table(name = "cs_transactions")
|
||||
public class Transaction {
|
||||
|
||||
@Id
|
||||
private int id;
|
||||
|
||||
private boolean buy;
|
||||
private String shopOwner;
|
||||
private String shopUser;
|
||||
private int itemID;
|
||||
private int itemDurability;
|
||||
private int amount;
|
||||
private float price;
|
||||
private long sec;
|
||||
|
||||
public Transaction() {
|
||||
}
|
||||
|
||||
public float getAveragePricePerItem() {
|
||||
return price / amount;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean isBuy() {
|
||||
return buy;
|
||||
}
|
||||
|
||||
public String getShopOwner() {
|
||||
return shopOwner;
|
||||
}
|
||||
|
||||
public String getShopUser() {
|
||||
return shopUser;
|
||||
}
|
||||
|
||||
public int getItemID() {
|
||||
return itemID;
|
||||
}
|
||||
|
||||
public int getItemDurability() {
|
||||
return itemDurability;
|
||||
}
|
||||
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public float getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public long getSec() {
|
||||
return sec;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setBuy(boolean buy) {
|
||||
this.buy = buy;
|
||||
}
|
||||
|
||||
public void setShopOwner(String shopOwner) {
|
||||
this.shopOwner = shopOwner;
|
||||
}
|
||||
|
||||
public void setShopUser(String shopUser) {
|
||||
this.shopUser = shopUser;
|
||||
}
|
||||
|
||||
public void setItemID(int itemID) {
|
||||
this.itemID = itemID;
|
||||
}
|
||||
|
||||
public void setItemDurability(int itemDurability) {
|
||||
this.itemDurability = itemDurability;
|
||||
}
|
||||
|
||||
public void setAmount(int amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public void setPrice(float price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public void setSec(long sec) {
|
||||
this.sec = sec;
|
||||
}
|
||||
}
|
@ -25,10 +25,7 @@ import org.bukkit.material.PistonBaseMaterial;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
import org.bukkit.metadata.MetadataValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import static com.Acrobot.Breeze.Utils.BlockUtil.getAttachedBlock;
|
||||
import static com.Acrobot.Breeze.Utils.BlockUtil.isSign;
|
||||
@ -174,7 +171,7 @@ public class SignBreak implements Listener {
|
||||
}
|
||||
|
||||
if (isSign(block)) {
|
||||
return Arrays.asList((Sign) block.getState());
|
||||
return Collections.singletonList((Sign) block.getState());
|
||||
} else {
|
||||
List<Sign> attachedSigns = new LinkedList<Sign>();
|
||||
|
||||
|
@ -1,389 +0,0 @@
|
||||
package com.lennardf1989.bukkitex;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.EbeanServerFactory;
|
||||
import com.avaje.ebean.config.DataSourceConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.SQLitePlatform;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.ddl.DdlGenerator;
|
||||
import com.avaje.ebeaninternal.server.lib.sql.TransactionIsolation;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public abstract class Database {
|
||||
private JavaPlugin javaPlugin;
|
||||
private ClassLoader classLoader;
|
||||
private Level loggerLevel;
|
||||
private boolean usingSQLite;
|
||||
private ServerConfig serverConfig;
|
||||
private EbeanServer ebeanServer;
|
||||
|
||||
/**
|
||||
* Create an instance of Database
|
||||
*
|
||||
* @param javaPlugin Plugin instancing this database
|
||||
*/
|
||||
public Database(JavaPlugin javaPlugin) {
|
||||
//Store the JavaPlugin
|
||||
this.javaPlugin = javaPlugin;
|
||||
|
||||
//Try to get the ClassLoader of the plugin using Reflection
|
||||
try {
|
||||
//Find the "getClassLoader" method and make it "public" instead of "protected"
|
||||
Method method = JavaPlugin.class.getDeclaredMethod("getClassLoader");
|
||||
method.setAccessible(true);
|
||||
|
||||
//Store the ClassLoader
|
||||
this.classLoader = (ClassLoader) method.invoke(javaPlugin);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Failed to retrieve the ClassLoader of the plugin using Reflection", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the database using the passed arguments
|
||||
*
|
||||
* @param driver Database-driver to use. For example: org.sqlite.JDBC
|
||||
* @param url Location of the database. For example: jdbc:sqlite:{DIR}{NAME}.db
|
||||
* @param username Username required to access the database
|
||||
* @param password Password belonging to the username, may be empty
|
||||
* @param isolation Isolation type. For example: SERIALIZABLE, also see TransactionIsolation
|
||||
*/
|
||||
public void initializeDatabase(String driver, String url, String username, String password, String isolation) {
|
||||
//Logging needs to be set back to the original level, no matter what happens
|
||||
try {
|
||||
//Disable all logging
|
||||
disableDatabaseLogging(false);
|
||||
|
||||
//Prepare the database
|
||||
prepareDatabase(driver, url, username, password, isolation);
|
||||
|
||||
//Load the database
|
||||
loadDatabase();
|
||||
|
||||
//Create all tables
|
||||
installDatabase(false);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("An exception has occured while initializing the database", ex);
|
||||
} finally {
|
||||
//Enable all logging
|
||||
enableDatabaseLogging(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareDatabase(String driver, String url, String username, String password, String isolation) {
|
||||
//Setup the data source
|
||||
DataSourceConfig ds = new DataSourceConfig();
|
||||
ds.setDriver(driver);
|
||||
ds.setUrl(replaceDatabaseString(url));
|
||||
ds.setUsername(username);
|
||||
ds.setPassword(password);
|
||||
ds.setIsolationLevel(TransactionIsolation.getLevel(isolation));
|
||||
|
||||
//Setup the server configuration
|
||||
ServerConfig sc = new ServerConfig();
|
||||
sc.setDefaultServer(false);
|
||||
sc.setRegister(false);
|
||||
sc.setName(ds.getUrl().replaceAll("[^a-zA-Z0-9]", ""));
|
||||
|
||||
//Get all persistent classes
|
||||
List<Class<?>> classes = getDatabaseClasses();
|
||||
|
||||
//Do a sanity check first
|
||||
if (classes.isEmpty()) {
|
||||
//Exception: There is no use in continuing to load this database
|
||||
throw new RuntimeException("Database has been enabled, but no classes are registered to it");
|
||||
}
|
||||
|
||||
//Register them with the EbeanServer
|
||||
sc.setClasses(classes);
|
||||
|
||||
//Check if the SQLite JDBC supplied with Bukkit is being used
|
||||
if (ds.getDriver().equalsIgnoreCase("org.sqlite.JDBC")) {
|
||||
//Remember the database is a SQLite-database
|
||||
usingSQLite = true;
|
||||
|
||||
//Modify the platform, as SQLite has no AUTO_INCREMENT field
|
||||
sc.setDatabasePlatform(new SQLitePlatform());
|
||||
sc.getDatabasePlatform().getDbDdlSyntax().setIdentity("");
|
||||
}
|
||||
|
||||
prepareDatabaseAdditionalConfig(ds, sc);
|
||||
|
||||
//Finally the data source
|
||||
sc.setDataSourceConfig(ds);
|
||||
|
||||
//Store the ServerConfig
|
||||
serverConfig = sc;
|
||||
}
|
||||
|
||||
private void loadDatabase() {
|
||||
//Declare a few local variables for later use
|
||||
ClassLoader currentClassLoader = null;
|
||||
Field cacheField = null;
|
||||
boolean cacheValue = true;
|
||||
|
||||
try {
|
||||
//Store the current ClassLoader, so it can be reverted later
|
||||
currentClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
//Set the ClassLoader to Plugin ClassLoader
|
||||
Thread.currentThread().setContextClassLoader(classLoader);
|
||||
|
||||
//Get a reference to the private static "defaultUseCaches"-field in URLConnection
|
||||
cacheField = URLConnection.class.getDeclaredField("defaultUseCaches");
|
||||
|
||||
//Make it accessible, store the default value and set it to false
|
||||
cacheField.setAccessible(true);
|
||||
cacheValue = cacheField.getBoolean(null);
|
||||
cacheField.setBoolean(null, false);
|
||||
|
||||
//Setup Ebean based on the configuration
|
||||
ebeanServer = EbeanServerFactory.create(serverConfig);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Failed to create a new instance of the EbeanServer", ex);
|
||||
} finally {
|
||||
//Revert the ClassLoader back to its original value
|
||||
if (currentClassLoader != null) {
|
||||
Thread.currentThread().setContextClassLoader(currentClassLoader);
|
||||
}
|
||||
|
||||
//Revert the "defaultUseCaches"-field in URLConnection back to its original value
|
||||
try {
|
||||
if (cacheField != null) {
|
||||
cacheField.setBoolean(null, cacheValue);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to revert the \"defaultUseCaches\"-field back to its original value, URLConnection-caching remains disabled.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void installDatabase(boolean rebuild) {
|
||||
//Check if the database already (partially) exists
|
||||
boolean databaseExists = false;
|
||||
|
||||
List<Class<?>> classes = getDatabaseClasses();
|
||||
for (Class<?> aClass : classes) {
|
||||
try {
|
||||
//Do a simple query which only throws an exception if the table does not exist
|
||||
ebeanServer.find(aClass).findRowCount();
|
||||
|
||||
//Query passed without throwing an exception, a database therefore already exists
|
||||
databaseExists = true;
|
||||
break;
|
||||
} catch (Exception ex) {
|
||||
//Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
//Check if the database has to be created or rebuilt
|
||||
if (!rebuild && databaseExists) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Create a DDL generator
|
||||
SpiEbeanServer serv = (SpiEbeanServer) ebeanServer;
|
||||
DdlGenerator gen = serv.getDdlGenerator();
|
||||
|
||||
//Fire "before drop" event
|
||||
try {
|
||||
beforeDropDatabase();
|
||||
} catch (Exception ex) {
|
||||
//If the database exists, dropping has to be canceled to prevent data-loss
|
||||
if (databaseExists) {
|
||||
throw new RuntimeException("An unexpected exception occured", ex);
|
||||
}
|
||||
}
|
||||
|
||||
//Generate a DropDDL-script
|
||||
gen.runScript(true, gen.generateDropDdl());
|
||||
|
||||
//If SQLite is being used, the database has to reloaded to release all resources
|
||||
if (usingSQLite) {
|
||||
loadDatabase();
|
||||
}
|
||||
|
||||
//Generate a CreateDDL-script
|
||||
if (usingSQLite) {
|
||||
//If SQLite is being used, the CreateDLL-script has to be validated and potentially fixed to be valid
|
||||
gen.runScript(false, validateCreateDDLSqlite(gen.generateCreateDdl()));
|
||||
} else {
|
||||
gen.runScript(false, gen.generateCreateDdl());
|
||||
}
|
||||
|
||||
//Fire "after create" event
|
||||
try {
|
||||
afterCreateDatabase();
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("An unexpected exception occured", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String replaceDatabaseString(String input) {
|
||||
input = input.replaceAll("\\{DIR\\}", javaPlugin.getDataFolder().getPath().replaceAll("\\\\", "/") + '/');
|
||||
input = input.replaceAll("\\{NAME\\}", javaPlugin.getDescription().getName().replaceAll("[^\\w_-]", ""));
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
private static String validateCreateDDLSqlite(String oldScript) {
|
||||
try {
|
||||
//Create a BufferedReader out of the potentially invalid script
|
||||
BufferedReader scriptReader = new BufferedReader(new StringReader(oldScript));
|
||||
|
||||
//Create an array to store all the lines
|
||||
List<String> scriptLines = new ArrayList<String>();
|
||||
|
||||
//Create some additional variables for keeping track of tables
|
||||
HashMap<String, Integer> foundTables = new HashMap<String, Integer>();
|
||||
String currentTable = null;
|
||||
int tableOffset = 0;
|
||||
|
||||
//Loop through all lines
|
||||
String currentLine;
|
||||
while ((currentLine = scriptReader.readLine()) != null) {
|
||||
//Trim the current line to remove trailing spaces
|
||||
currentLine = currentLine.trim();
|
||||
|
||||
//Add the current line to the rest of the lines
|
||||
scriptLines.add(currentLine.trim());
|
||||
|
||||
//Check if the current line is of any use
|
||||
if (currentLine.startsWith("create table")) {
|
||||
//Found a table, so get its name and remember the line it has been encountered on
|
||||
currentTable = currentLine.split(" ", 4)[2];
|
||||
foundTables.put(currentLine.split(" ", 3)[2], scriptLines.size() - 1);
|
||||
} else if (!currentLine.isEmpty() && currentLine.charAt(0) == ';' && currentTable != null && !currentTable.isEmpty()) {
|
||||
//Found the end of a table definition, so update the entry
|
||||
int index = scriptLines.size() - 1;
|
||||
foundTables.put(currentTable, index);
|
||||
|
||||
//Remove the last ")" from the previous line
|
||||
String previousLine = scriptLines.get(index - 1);
|
||||
previousLine = previousLine.substring(0, previousLine.length() - 1);
|
||||
scriptLines.set(index - 1, previousLine);
|
||||
|
||||
//Change ";" to ");" on the current line
|
||||
scriptLines.set(index, ");");
|
||||
|
||||
//Reset the table-tracker
|
||||
currentTable = null;
|
||||
} else if (currentLine.startsWith("alter table")) {
|
||||
//Found a potentially unsupported action
|
||||
String[] alterTableLine = currentLine.split(" ", 4);
|
||||
|
||||
if (alterTableLine[3].startsWith("add constraint")) {
|
||||
//Found an unsupported action: ALTER TABLE using ADD CONSTRAINT
|
||||
String[] addConstraintLine = alterTableLine[3].split(" ", 4);
|
||||
|
||||
//Check if this line can be fixed somehow
|
||||
if (addConstraintLine[3].startsWith("foreign key")) {
|
||||
//Calculate the index of last line of the current table
|
||||
int tableLastLine = foundTables.get(alterTableLine[2]) + tableOffset;
|
||||
|
||||
//Add a "," to the previous line
|
||||
scriptLines.set(tableLastLine - 1, scriptLines.get(tableLastLine - 1) + ',');
|
||||
|
||||
//Add the constraint as a new line - Remove the ";" on the end
|
||||
String constraintLine = String.format("%s %s %s", addConstraintLine[1], addConstraintLine[2], addConstraintLine[3]);
|
||||
scriptLines.add(tableLastLine, constraintLine.substring(0, constraintLine.length() - 1));
|
||||
|
||||
//Remove this line and raise the table offset because a line has been inserted
|
||||
scriptLines.remove(scriptLines.size() - 1);
|
||||
tableOffset++;
|
||||
} else {
|
||||
//Exception: This line cannot be fixed but is known the be unsupported by SQLite
|
||||
throw new RuntimeException("Unsupported action encountered: ALTER TABLE using ADD CONSTRAINT with " + addConstraintLine[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Turn all the lines back into a single string
|
||||
StringBuilder newScript = new StringBuilder(5);
|
||||
for (String newLine : scriptLines) {
|
||||
newScript.append(newLine).append('\n');
|
||||
}
|
||||
|
||||
//Print the new script
|
||||
System.out.println(newScript);
|
||||
|
||||
//Return the fixed script
|
||||
return newScript.toString();
|
||||
} catch (Exception ex) {
|
||||
//Exception: Failed to fix the DDL or something just went plain wrong
|
||||
throw new RuntimeException("Failed to validate the CreateDDL-script for SQLite", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void disableDatabaseLogging(boolean logging) {
|
||||
//If logging is allowed, nothing has to be changed
|
||||
if (logging) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Retrieve the level of the root logger
|
||||
loggerLevel = Logger.getLogger("").getLevel();
|
||||
|
||||
//Set the level of the root logger to OFF
|
||||
Logger.getLogger("").setLevel(Level.OFF);
|
||||
}
|
||||
|
||||
private void enableDatabaseLogging(boolean logging) {
|
||||
//If logging is allowed, nothing has to be changed
|
||||
if (logging) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Set the level of the root logger back to the original value
|
||||
Logger.getLogger("").setLevel(loggerLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of classes which should be registered with the EbeanServer
|
||||
*
|
||||
* @return List List of classes which should be registered with the EbeanServer
|
||||
*/
|
||||
protected List<Class<?>> getDatabaseClasses() {
|
||||
return new ArrayList<Class<?>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called before the loaded database is being dropped
|
||||
*/
|
||||
protected void beforeDropDatabase() {}
|
||||
|
||||
/**
|
||||
* Method called after the loaded database has been created
|
||||
*/
|
||||
protected void afterCreateDatabase() {}
|
||||
|
||||
/**
|
||||
* Method called near the end of prepareDatabase, before the dataSourceConfig is attached to the serverConfig.
|
||||
*
|
||||
* @param dataSourceConfig
|
||||
* @param serverConfig
|
||||
*/
|
||||
protected void prepareDatabaseAdditionalConfig(DataSourceConfig dataSourceConfig, ServerConfig serverConfig) {}
|
||||
|
||||
/**
|
||||
* Get the instance of the EbeanServer
|
||||
*
|
||||
* @return EbeanServer Instance of the EbeanServer
|
||||
*/
|
||||
public EbeanServer getDatabase() {
|
||||
return ebeanServer;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user