diff --git a/pom.xml b/pom.xml
index 73b4ee541..1073d9434 100644
--- a/pom.xml
+++ b/pom.xml
@@ -64,7 +64,7 @@
org.bukkit
bukkit
- 1.12-pre2-SNAPSHOT
+ 1.12-pre5-SNAPSHOT
provided
diff --git a/src/main/java/us/tastybento/bskyblock/BSkyBlock.java b/src/main/java/us/tastybento/bskyblock/BSkyBlock.java
index 8643fb52e..bba446562 100755
--- a/src/main/java/us/tastybento/bskyblock/BSkyBlock.java
+++ b/src/main/java/us/tastybento/bskyblock/BSkyBlock.java
@@ -1,8 +1,11 @@
package us.tastybento.bskyblock;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
import java.util.UUID;
+import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
@@ -15,6 +18,8 @@ import us.tastybento.bskyblock.database.BSBDatabase.DatabaseType;
import us.tastybento.bskyblock.database.IslandsManager;
import us.tastybento.bskyblock.database.OfflineHistoryMessages;
import us.tastybento.bskyblock.database.PlayersManager;
+import us.tastybento.bskyblock.database.objects.Island;
+import us.tastybento.bskyblock.database.objects.Island.SettingsFlag;
import us.tastybento.bskyblock.util.VaultHelper;
/**
@@ -38,7 +43,7 @@ public class BSkyBlock extends JavaPlugin{
@Override
public void onEnable(){
plugin = this;
-
+
// Load configuration and locales. If there are no errors, load the plugin.
if(PluginConfig.loadPluginConfig(this)){
// TEMP DEBUG DATABASE
@@ -48,12 +53,16 @@ public class BSkyBlock extends JavaPlugin{
Settings.dbName = "ASkyBlock";
Settings.dbUsername = "username";
Settings.dbPassword = "password";
-
+
playersManager = new PlayersManager(this);
islandsManager = new IslandsManager(this);
+ // Only load metrics if set to true in config
+ if(Settings.metrics) metrics = new Metrics(plugin);
- playersManager.load();
- islandsManager.load();
+ // If metrics are loaded, register the custom data charts
+ if(metrics != null){
+ registerCustomCharts();
+ }
offlineHistoryMessages = new OfflineHistoryMessages(this);
offlineHistoryMessages.load();
@@ -63,24 +72,57 @@ public class BSkyBlock extends JavaPlugin{
Settings.useEconomy = false;
}
- // Only load metrics if set to true in config
- if(Settings.metrics) metrics = new Metrics(this);
-
- // If metrics are loaded, register the custom data charts
- if(metrics != null){
- registerCustomCharts();
- }
-
- // Save islands & players data asynchronously every X minutes
- plugin.getServer().getScheduler().runTaskTimer(this, new Runnable() {
+ // These items have to be loaded when the server has done 1 tick.
+ // Note Worlds are not loaded this early, so any Locations or World reference will be null
+ // at this point. Therefore, the 1 tick scheduler is required.
+ getServer().getScheduler().runTask(this, new Runnable() {
@Override
public void run() {
- playersManager.save(true);
- islandsManager.save(true);
- offlineHistoryMessages.save(true);
+
+ // Test: Create a random island and save it
+ // TODO: ideally this should be in a test class!
+ /*
+ Island island = islandsManager.createIsland(new Location(getServer().getWorld("world"),0,0,0,0,0), UUID.randomUUID());
+ // Add members
+ Set randomSet = new HashSet();
+ for (int i = 0; i < 10; i++) {
+ randomSet.add(UUID.randomUUID());
+ island.addMember(UUID.randomUUID());
+ island.addToBanList(UUID.randomUUID());
+ }
+ island.setBanned(randomSet);
+ island.setCoops(randomSet);
+ island.setTrustees(randomSet);
+ island.setMembers(randomSet);
+ for (SettingsFlag flag: SettingsFlag.values()) {
+ island.setFlag(flag, true);
+ }
+ island.setLocked(true);
+ island.setName("new name");
+ island.setPurgeProtected(true);
+ islandsManager.save(false);
+ */
+ // TODO: Write loading code for MySQL
+ playersManager.load();
+ islandsManager.load();
+
+
+ // Save islands & players data asynchronously every X minutes
+ Settings.databaseBackupPeriod = 10 * 60 * 20;
+ plugin.getServer().getScheduler().runTaskTimer(plugin, new Runnable() {
+
+ @Override
+ public void run() {
+ playersManager.save(true);
+ islandsManager.save(true);
+ offlineHistoryMessages.save(true);
+ }
+ }, Settings.databaseBackupPeriod, Settings.databaseBackupPeriod);
}
- }, Settings.databaseBackupPeriod, Settings.databaseBackupPeriod);
+ // TODO Auto-generated method stub
+
+ });
}
}
diff --git a/src/main/java/us/tastybento/bskyblock/database/AbstractDatabaseHandler.java b/src/main/java/us/tastybento/bskyblock/database/AbstractDatabaseHandler.java
index cb19f34c3..358cbc5bb 100644
--- a/src/main/java/us/tastybento/bskyblock/database/AbstractDatabaseHandler.java
+++ b/src/main/java/us/tastybento/bskyblock/database/AbstractDatabaseHandler.java
@@ -134,7 +134,8 @@ public abstract class AbstractDatabaseHandler {
* @throws InstantiationException
* @throws SecurityException
* @throws SQLException
+ * @throws NoSuchMethodException
*/
- protected abstract void insertObject(T instance) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException, SQLException, SecurityException, InstantiationException;
+ protected abstract void insertObject(T instance) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException, SQLException, SecurityException, InstantiationException, NoSuchMethodException;
}
diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java
index 8298cf8f3..d4d1a1ea1 100644
--- a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java
+++ b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java
@@ -7,7 +7,6 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
-import java.lang.reflect.TypeVariable;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -17,10 +16,13 @@ import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
@@ -63,7 +65,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler {
mySQLmapping.put(Date.class.getTypeName(), "DATE");
mySQLmapping.put(Time.class.getTypeName(), "TIME");
mySQLmapping.put(Timestamp.class.getTypeName(), "TIMESTAMP");
- mySQLmapping.put(UUID.class.getTypeName(), "VARCHAR(32)"); // TODO: How long is a UUID to string?
+ mySQLmapping.put(UUID.class.getTypeName(), "VARCHAR(36)");
// Bukkit Mappings
mySQLmapping.put(Location.class.getTypeName(), "VARCHAR(254)");
@@ -107,21 +109,29 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler {
private void createSchema() throws IntrospectionException, SQLException {
PreparedStatement pstmt = null;
try {
- String sql = "CREATE TABLE IF NOT EXISTS " + type.getSimpleName() + "(";
+ String sql = "CREATE TABLE IF NOT EXISTS `" + type.getCanonicalName() + "` (";
for (Field field : type.getDeclaredFields()) {
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), type);
plugin.getLogger().info("DEBUG: Field = " + field.getName() + "(" + propertyDescriptor.getPropertyType().getTypeName() + ")");
+ // Get default SQL mappings
+ Method writeMethod = propertyDescriptor.getWriteMethod();
+ String columnName = field.getName();
String mapping = mySQLmapping.get(propertyDescriptor.getPropertyType().getTypeName());
if (mapping != null) {
- sql += "`" + field.getName() + "` " + mapping + ",";
+ sql += "`" + columnName + "` " + mapping + ",";
// Create set and map tables.
if (propertyDescriptor.getPropertyType().equals(Set.class) ||
propertyDescriptor.getPropertyType().equals(Map.class) ||
propertyDescriptor.getPropertyType().equals(HashMap.class) ||
propertyDescriptor.getPropertyType().equals(ArrayList.class)) {
- String setSql = "CREATE TABLE IF NOT EXISTS " + type.getSimpleName() + "_" + field.getName() + " (";
- // Get the type
- setSql += getMethodParameterTypes(propertyDescriptor.getWriteMethod());
+ // The ID in this table relates to the parent table
+ String setSql = "CREATE TABLE IF NOT EXISTS `" + type.getCanonicalName() + "." + field.getName() + "` ("
+ + "uniqueId VARCHAR(36) NOT NULL, ";
+ // Get columns separated by commas
+ setSql += getCollectionColumns(writeMethod,false,true);
+ // Add primary key
+ setSql += ")";
+
plugin.getLogger().info(setSql);
PreparedStatement collections = connection.prepareStatement(setSql);
collections.executeUpdate();
@@ -132,7 +142,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler {
}
}
//plugin.getLogger().info("DEBUG: SQL before trim string = " + sql);
- sql = sql.substring(0,(sql.length()-1)) + ")";
+ sql += " PRIMARY KEY (uniqueId))";
plugin.getLogger().info("DEBUG: SQL string = " + sql);
pstmt = connection.prepareStatement(sql.toString());
pstmt.executeUpdate();
@@ -144,42 +154,54 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler {
}
}
+
/**
- * Gets the types for parameters in a method
+ * Returns a string of columns separated by commas that represent the parameter types of this method
* @param writeMethod
- * @return List of strings with the SQL for parameter and type set
+ * @param usePlaceHolders
+ * true, if PreparedStatement-placeholders ('?') should be used
+ * instead of the names of the variables
+ * @param createSchema if true contains the columns types
+ * @return Returns a string of columns separated by commas.
*/
- private String getMethodParameterTypes(Method method) {
- String result = "";
+ private String getCollectionColumns(Method writeMethod, boolean usePlaceHolders, boolean createSchema) {
+ String columns = "";
// Get the return type
- Type[] genericParameterTypes = method.getGenericParameterTypes();
+ Type[] genericParameterTypes = writeMethod.getGenericParameterTypes();
for (int i = 0; i < genericParameterTypes.length; i++) {
if( genericParameterTypes[i] instanceof ParameterizedType ) {
Type[] parameters = ((ParameterizedType)genericParameterTypes[i]).getActualTypeArguments();
//parameters[0] contains java.lang.String for method like "method(List value)"
int index = 0;
- String firstColumn = "";
+ boolean first = true;
for (Type type : parameters) {
plugin.getLogger().info("DEBUG: set type = " + type.getTypeName());
- String setMapping = mySQLmapping.get(type.getTypeName());
- String notNull = "";
- if (index == 0) {
- firstColumn = "`" + type.getTypeName() + "_" + index + "`";
- notNull = " NOT NULL";
- }
- if (setMapping != null) {
- result += "`" + type.getTypeName() + "_" + index + "` " + setMapping + notNull + ",";
+ if (first)
+ first = false;
+ else
+ columns += ", ";
+ if (usePlaceHolders) {
+ columns +="?";
} else {
- result += "`" + type.getTypeName() + "_" + index + "` VARCHAR(254)" + notNull + ",";
- plugin.getLogger().severe("Unknown type! Hoping it'll fit in a string!");
+ String setMapping = mySQLmapping.get(type.getTypeName());
+ if (setMapping != null) {
+ columns += "`" + type.getTypeName() + "_" + index + "`";
+ if (createSchema) {
+ columns += " " + setMapping;
+ }
+ } else {
+ columns += "`" + type.getTypeName() + "_" + index + "`";
+ if (createSchema) {
+ columns += " VARCHAR(254)";
+ plugin.getLogger().warning("Unknown type! Hoping it'll fit in a string!");
+ }
+ }
}
index++;
}
- // Add primary key
- result += " PRIMARY KEY (" + firstColumn + "))";
}
}
- return result;
+ return columns;
}
@Override
@@ -191,8 +213,10 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler {
sb.append(super.getColumns(false));
sb.append(" FROM ");
- /* We assume the table-name exactly matches the simpleName of T */
- sb.append(type.getSimpleName());
+ /* We assume the table-name exactly matches the canonical Name of T */
+ sb.append("`");
+ sb.append(type.getCanonicalName());
+ sb.append("`");
return sb.toString();
}
@@ -202,8 +226,10 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler {
StringBuilder sb = new StringBuilder();
- sb.append("INSERT INTO ");
- sb.append(type.getSimpleName());
+ sb.append("REPLACE INTO ");
+ sb.append("`");
+ sb.append(type.getCanonicalName());
+ sb.append("`");
sb.append("(");
sb.append(super.getColumns(false));
sb.append(")");
@@ -225,33 +251,39 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler {
* @throws IllegalAccessException
* @throws IntrospectionException
* @throws InvocationTargetException
+ * @throws NoSuchMethodException
*/
@Override
public void insertObject(T instance) throws SQLException,
SecurityException, IllegalArgumentException,
InstantiationException, IllegalAccessException,
- IntrospectionException, InvocationTargetException {
+ IntrospectionException, InvocationTargetException, NoSuchMethodException {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = databaseConnecter.createConnection();
- preparedStatement = connection.prepareStatement(selectQuery);
-
+ preparedStatement = connection.prepareStatement(insertQuery);
+ // Get the uniqueId
+ Method getUniqueId = type.getMethod("getUniqueId");
+ String uniqueId = (String) getUniqueId.invoke(instance);
+ plugin.getLogger().info("Unique Id = " + uniqueId);
+ if (uniqueId.isEmpty()) {
+ throw new SQLException("uniqueId is blank");
+ }
int i = 0;
-
+ plugin.getLogger().info("DEBUG: insert Query " + insertQuery);
for (Field field : type.getDeclaredFields()) {
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(
field.getName(), type);
- Method method = propertyDescriptor
- .getReadMethod();
+ Method method = propertyDescriptor.getReadMethod();
plugin.getLogger().info("DEBUG: Field = " + field.getName() + "(" + propertyDescriptor.getPropertyType().getTypeName() + ")");
//sql += "`" + field.getName() + "` " + mapping + ",";
-
+
Object value = method.invoke(instance);
-
+
// Create set and map tables.
if (propertyDescriptor.getPropertyType().equals(Set.class) ||
propertyDescriptor.getPropertyType().equals(Map.class) ||
@@ -259,23 +291,50 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler {
propertyDescriptor.getPropertyType().equals(ArrayList.class)) {
// Collection
// TODO Set the values in the subsidiary tables.
+ // TODO clear the table?
+ String setSql = "INSERT INTO `" + type.getCanonicalName() + "." + field.getName() + "` (uniqueId, ";
+ setSql += getCollectionColumns(propertyDescriptor.getWriteMethod(), false, false) + ") ";
+ setSql += "VALUES ('" + uniqueId + "'," + getCollectionColumns(propertyDescriptor.getWriteMethod(), true, false) + ")";
+ PreparedStatement collStatement = connection.prepareStatement(setSql);
+ plugin.getLogger().info("DEBUG: collection insert =" + setSql);
+ // Do single dimension types (set and list)
+ if (propertyDescriptor.getPropertyType().equals(Set.class) ||
+ propertyDescriptor.getPropertyType().equals(ArrayList.class)) {
+ plugin.getLogger().info("DEBUG: set class for ");
+ // Loop through the collection
+ Collection> collection = (Collection>)value;
+ Iterator> it = collection.iterator();
+ while (it.hasNext()) {
+ Object setValue = it.next();
+ if (setValue instanceof UUID) {
+ // Serialize everything
+ setValue = serialize(setValue, setValue.getClass());
+ }
+ collStatement.setObject(1, setValue);
+ plugin.getLogger().info("DEBUG: " + collStatement.toString());
+ collStatement.execute();
+ }
+ } else if (propertyDescriptor.getPropertyType().equals(Map.class) ||
+ propertyDescriptor.getPropertyType().equals(HashMap.class)) {
+ // Loop through the collection
+ Map,?> collection = (Map,?>)value;
+ Iterator> it = collection.entrySet().iterator();
+ while (it.hasNext()) {
+ Entry,?> en = (Entry, ?>) it.next();
+ Object key = serialize(en.getKey(), en.getKey().getClass());
+ plugin.getLogger().info("DEBUG: key class = " + en.getKey().getClass().getTypeName());
+ Object mapValue = serialize(en.getValue(), en.getValue().getClass());;
+ collStatement.setObject(1, key);
+ collStatement.setObject(2, mapValue);
+ plugin.getLogger().info("DEBUG: " + collStatement.toString());
+ collStatement.execute();
+ }
+ }
+ // Set value for the main insert
value = true;
+ } else {
+ value = serialize(value, propertyDescriptor.getPropertyType());
}
- // Types that need to be serialized
- if (propertyDescriptor.getPropertyType().equals(UUID.class)) {
- value = ((UUID)value).toString();
- }
- // Bukkit Types
- if (propertyDescriptor.getPropertyType().equals(Location.class)) {
- // Serialize
- value = Util.getStringLocation(((Location)value));
- }
- if (propertyDescriptor.getPropertyType().equals(World.class)) {
- // Serialize - get the name
- value = ((World)value).getName();
- }
-
-
preparedStatement.setObject(++i, value);
}
@@ -289,6 +348,41 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler {
}
}
+ /**
+ * Serializes value
+ * @param value
+ * @param clazz - the known class of value
+ * @return
+ */
+ private Object serialize(Object value, Class extends Object> clazz) {
+ plugin.getLogger().info("DEBUG: serialize - class is " + clazz.getTypeName());
+ if (value == null) {
+ return "null";
+ }
+ // Types that need to be serialized
+ if (clazz.equals(UUID.class)) {
+ value = ((UUID)value).toString();
+ } else
+ // Bukkit Types
+ if (clazz.equals(Location.class)) {
+ // Serialize
+ value = Util.getStringLocation(((Location)value));
+ } else
+ if (clazz.equals(World.class)) {
+ // Serialize - get the name
+ value = ((World)value).getName();
+ } else
+ if (clazz.getSuperclass() != null && clazz.getSuperclass().equals(Enum.class)) {
+ //Custom enums are a child of the Enum class. Just get the names of each one.
+ value = ((Enum>)value).name();
+ }
+ if (value == null) {
+ return "null";
+ }
+ return value;
+
+ }
+
/**
* Creates a list of s filled with values from the corresponding
* database-table
@@ -376,7 +470,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler {
field.getName(), type);
Method method = propertyDescriptor.getWriteMethod();
-
+
// Create set and map tables.
if (propertyDescriptor.getPropertyType().equals(Set.class) ||
propertyDescriptor.getPropertyType().equals(Map.class) ||
diff --git a/src/main/java/us/tastybento/bskyblock/database/objects/Island.java b/src/main/java/us/tastybento/bskyblock/database/objects/Island.java
index 84d09df4c..a50165314 100755
--- a/src/main/java/us/tastybento/bskyblock/database/objects/Island.java
+++ b/src/main/java/us/tastybento/bskyblock/database/objects/Island.java
@@ -22,7 +22,24 @@ import us.tastybento.bskyblock.config.Settings;
* @author Poslovitch
*/
public class Island extends DataObject {
+
+ private String uniqueId = "";
+
+ @Override
+ public String getUniqueId() {
+ // Island's have UUID's that are randomly assigned if they do not exist
+ if (uniqueId.isEmpty()) {
+ uniqueId = UUID.randomUUID().toString();
+ }
+ return uniqueId;
+ }
+ @Override
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+
+ }
+
/**
* Island Guard Settings flags
* Covers island, spawn and system settings
@@ -332,8 +349,6 @@ public class Island extends DataObject {
//// Protection ////
private HashMap flags = new HashMap();
- private String uniqueId = "";
-
public Island() {};
public Island(Location location, UUID owner, int protectionRange) {
@@ -427,6 +442,10 @@ public class Island extends DataObject {
* @return the members of the island (owner included)
*/
public Set getMembers(){
+ if (members == null) {
+ Bukkit.getLogger().info("DEBUG: members = null");
+ members = new HashSet();
+ }
return members;
}
/**
@@ -675,6 +694,7 @@ public class Island extends DataObject {
* @param members - the members to set
*/
public void setMembers(Set members){
+ Bukkit.getLogger().info("DEBUG: setting members = " + members);
this.members = members;
}
@@ -791,15 +811,4 @@ public class Island extends DataObject {
}
}
- @Override
- public String getUniqueId() {
- return uniqueId;
- }
-
- @Override
- public void setUniqueId(String uniqueId) {
- this.uniqueId = uniqueId;
-
- }
-
}
\ No newline at end of file