package de.oppermann.bastian.spleef.storage; import java.io.File; import java.sql.SQLException; import java.util.concurrent.ExecutorService; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import org.bukkit.Bukkit; import org.bukkit.inventory.ItemStack; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import de.oppermann.bastian.spleef.SpleefMain; import de.oppermann.bastian.spleef.arena.Lobby; import de.oppermann.bastian.spleef.arena.SpleefArena; import de.oppermann.bastian.spleef.arena.SpleefBlock; import de.oppermann.bastian.spleef.arena.SpleefSpawnLocation; import de.oppermann.bastian.spleef.util.Validator; /** * Class that handles the connection to the database. * * @author Bastian Oppermann */ public class StorageManager { //private ExecutorService threadPool = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); private boolean ownParameters = SpleefMain.getInstance().getConfig().getBoolean("expertsOnly.stats.threadPoolSettings.ownParameters", false); private int ownCorePoolSize = SpleefMain.getInstance().getConfig().getInt("expertsOnly.stats.threadPoolSettings.corePoolSize", 0); private int ownMaximumPoolSize = SpleefMain.getInstance().getConfig().getInt("expertsOnly.stats.threadPoolSettings.maximumPoolSize", Integer.MAX_VALUE); private long ownKeepAliveTime = SpleefMain.getInstance().getConfig().getLong("expertsOnly.stats.threadPoolSettings.keepAliveTimeInMilliSeconds", 60*1000); private ExecutorService threadPool = ownParameters ? new ThreadPoolExecutor(ownCorePoolSize, ownMaximumPoolSize, ownKeepAliveTime, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>()) : new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); private ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(threadPool); private static StorageManager instance; // singleton instance of the class private static boolean sqlLite = true; private SQLConnector connector; private ReentrantLock lock = new ReentrantLock(); private StorageManager() { instance = this; lock.lock(); // database must be created first... threadPool.submit(new Runnable() { @Override public void run() { try { if (sqlLite) { connector = new SQLiteConnector(SpleefMain.getInstance(), "database.db"); SpleefMain.getInstance().log(Level.INFO, "Database created!"); Bukkit.getScheduler().runTask(SpleefMain.getInstance(), new Runnable() { @Override public void run() { lock.unlock(); // unlock cause database was created :) } }); } else { // TODO add mySQL support } } catch (SQLException e) { // would be very sad, if this happens :( SpleefMain.getInstance().log(Level.SEVERE, "Falied to connect to database. Stopping plugin."); e.printStackTrace(); Bukkit.getPluginManager().disablePlugin(SpleefMain.getInstance()); return; } try { lock.lock(); connector.getStatement().execute( "CREATE TABLE IF NOT EXISTS `epicspleef_stats` (" + "`uuid` varchar(16) NOT NULL," + "`points` int(11) NOT NULL," + "PRIMARY KEY (uuid) )" ); lock.unlock(); // TODO for future database changes: add missing columns } catch (SQLException e) { // would be very sad, if this happens :( SpleefMain.getInstance().log(Level.SEVERE, "Falied to create table in database. Stopping plugin."); e.printStackTrace(); Bukkit.getPluginManager().disablePlugin(SpleefMain.getInstance()); return; } try { lock.lock(); connector.getStatement().execute( "CREATE TABLE IF NOT EXISTS `epicspleef_shop` (" + "`uuid` varchar(16) NOT NULL," + "`particleId` int(11) NOT NULL," + "PRIMARY KEY (uuid, particleId) )" ); lock.unlock(); // TODO for future database changes: add missing columns } catch (SQLException e) { // would be very sad, if this happens :( SpleefMain.getInstance().log(Level.SEVERE, "Falied to create table in database. Stopping plugin."); e.printStackTrace(); Bukkit.getPluginManager().disablePlugin(SpleefMain.getInstance()); return; } } }); } /** * Creates a new table if it doesn't exits. */ public void createTableForArena(final String arenaName) { Validator.validateNotNull(arenaName, "arenaName"); if (arenaName.contains("`")) { throw new IllegalArgumentException("arenaName contains an invalid character. ( ` )"); } submit(new Runnable() { @Override public void run() { try { lock.lock(); connector.getStatement().execute( "CREATE TABLE IF NOT EXISTS `epicspleef_stats_" + arenaName + "` (" + "`uuid` varchar(16) NOT NULL," + // the uuid of the player "`wins` int(11) NOT NULL," + // the wins of the player in this arena "`losses` int(11) NOT NULL," + // the losses of the player in this arena "`points` int(11) NOT NULL," + // the earned points of the player in this arena (!= the total amount of points) "`jumps` int(11) NOT NULL," + // the jums of the player "`destroyedblocks` int(11) NOT NULL," + // the amount of destroyed blocks "PRIMARY KEY (uuid) )" ); lock.unlock(); // TODO for future database changes: add missing columns // TODO option to enable loading the data to memory on startup. } catch (SQLException e) { // would be very sad, if this happens :( SpleefMain.getInstance().log(Level.SEVERE, "Falied to create table \"" + arenaName + "\" in database. Stopping plugin."); e.printStackTrace(); Bukkit.getPluginManager().disablePlugin(SpleefMain.getInstance()); return; } } }); } /** * Creates a new {@link ConfigAccessor} for the arena if it doesn't already exits. */ public ConfigAccessor createConfigForArena(SpleefArena arena) { ConfigAccessor accessor = SpleefMain.getInstance().getArenaAccessor(arena.getName()); if (accessor == null) { File folder = new File(SpleefMain.getInstance().getDataFolder().getPath(), "arenas"); accessor = new ConfigAccessor(SpleefMain.getInstance(), arena.getName() + ".yml", folder); accessor.getConfig().set("enabled", !arena.getConfiguration().isDisabled()); accessor.getConfig().set("mode", arena.getConfiguration().getMode().name().toLowerCase()); accessor.getConfig().set("world", arena.getWorldName()); accessor.getConfig().set("lobby", arena.getConfiguration().getLobby() == null ? null : arena.getConfiguration().getLobby().getName()); accessor.getConfig().set("modifygravity.enable", arena.getConfiguration().modifyGravity()); accessor.getConfig().set("modifygravity.gravity", arena.getConfiguration().getGravity()); accessor.getConfig().set("snowballs.enable", arena.getConfiguration().isEnableSnowballs()); accessor.getConfig().set("snowballs.maxSnowballs", arena.getConfiguration().getMaxSnowballs()); accessor.getConfig().set("minPlayers", arena.getConfiguration().getMinPlayers()); accessor.getConfig().set("requiredPlayersToStartCountdown", arena.getConfiguration().getRequiredPlayersToStartCountdown()); accessor.getConfig().set("freezePlayers", arena.getConfiguration().freezePlayers()); accessor.getConfig().set("customInventory.enabled", arena.getConfiguration().hasCustomInventory()); accessor.getConfig().set("reward.points.winning", arena.getConfiguration().getPointsWinningReward()); accessor.getConfig().set("reward.points.participation", arena.getConfiguration().getPointsParticipationReward()); accessor.getConfig().set("reward.money.winning", arena.getConfiguration().getMoneyWinningReward()); accessor.getConfig().set("reward.money.participation", arena.getConfiguration().getMoneyParticipationReward()); accessor.getConfig().set("arenaCountdown", arena.getConfiguration().getArenaCountdown()); accessor.getConfig().set("lobbyCountdown", arena.getConfiguration().getLobbyCountdown()); int i = 1; for (SpleefSpawnLocation spawnLocation : arena.getSpawnLocations()) { accessor.getConfig().set("spawnlocs." + i + ".x", spawnLocation.getX()); accessor.getConfig().set("spawnlocs." + i + ".y", spawnLocation.getY()); accessor.getConfig().set("spawnlocs." + i + ".z", spawnLocation.getZ()); accessor.getConfig().set("spawnlocs." + i + ".yaw", spawnLocation.getYaw()); accessor.getConfig().set("spawnlocs." + i + ".pitch", spawnLocation.getPitch()); i++; } i = 1; // reset i for (SpleefBlock block : arena.getBlocks()) { accessor.getConfig().set("blocks." + i + ".x", block.getX()); accessor.getConfig().set("blocks." + i + ".y", block.getY()); accessor.getConfig().set("blocks." + i + ".z", block.getZ()); i++; } i = 0; for (ItemStack is : arena.getConfiguration().getCustomInventoryContents()) { accessor.getConfig().set("customInventory.items." + i, is); i++; } accessor.saveConfig(); SpleefMain.getInstance().addArenaConfiguration(arena.getName(), accessor); } return accessor; } /** * Deletes the config file for the arena. * * @param spleefArena The arena. */ public void deleteConfig(SpleefArena spleefArena) { ConfigAccessor accessor = SpleefMain.getInstance().getArenaAccessor(spleefArena.getName()); accessor.getFile().delete(); SpleefMain.getInstance().removeArenaConfiguration(spleefArena.getName()); } /** * Deletes the table for the arena. * * @param arenaName The name of the arena. */ public void deleteTable(final String arenaName) { submit(new Runnable() { @Override public void run() { try { if (needWriteLock()) { lock.lock(); } connector.getStatement().execute("DROP TABLE `epicspleef_stats_" + arenaName + "`;"); if (needWriteLock()) { lock.unlock(); } } catch (SQLException e) { // would be very sad, if this happens :( SpleefMain.getInstance().log(Level.SEVERE, "Falied to delete table \"" + arenaName + "\" in database."); e.printStackTrace(); return; } } }); } /** * Creates a new {@link ConfigAccessor} for the lobby if it doesn't already exits. */ public ConfigAccessor createConfigForLobby(Lobby lobby) { ConfigAccessor accessor = SpleefMain.getInstance().getLobbyAccessor(lobby.getName()); if (accessor == null) { File folder = new File(SpleefMain.getInstance().getDataFolder().getPath(), "lobbies"); accessor = new ConfigAccessor(SpleefMain.getInstance(), lobby.getName() + ".yml", folder); accessor.getConfig().set("world", lobby.getWorldName()); int i = 1; for (SpleefSpawnLocation spawnLocation : lobby.getSpawnLocations()) { accessor.getConfig().set("spawnlocs." + i + ".x", spawnLocation.getX()); accessor.getConfig().set("spawnlocs." + i + ".y", spawnLocation.getY()); accessor.getConfig().set("spawnlocs." + i + ".z", spawnLocation.getZ()); accessor.getConfig().set("spawnlocs." + i + ".yaw", spawnLocation.getYaw()); accessor.getConfig().set("spawnlocs." + i + ".pitch", spawnLocation.getPitch()); i++; } accessor.saveConfig(); SpleefMain.getInstance().addLobbyConfiguration(lobby.getName(), accessor); } return accessor; } /** * Gets the threadpool. */ public ExecutorService getThreadpool() { return this.threadPool; } /** * Gets the lock. */ public ReentrantLock getLock() { return lock; } /** * Gets if the database supports asynchronous writing or not. */ public boolean needWriteLock() { return sqlLite; } /** * Checks if the database type is SQLLite */ public boolean isSqlLite() { return sqlLite; } /** * Checks if the database type is MySQL */ public boolean isMySQL() { return !sqlLite; } /** * Gets the {@link ListeningExecutorService}. */ public ListeningExecutorService getListeningExecutorService() { return this.listeningExecutorService; } /** * Adds a runnable that should be performed in the storage thread. * * @param runnable The runnable that should be performed. */ public void submit(Runnable runnable) { Validator.validateNotNull(runnable, "runnable"); threadPool.submit(runnable); } /** * Gets the {@link SQLConnector}. */ public SQLConnector getSqlConnector() { return connector; } /** * Gets a singleton instance of the class. * * @return A singleton instance of the class. */ public synchronized static StorageManager getInstance() { if (instance == null) { instance = new StorageManager(); } return instance; } }