Solved any good ways to serialize/deserialize HashMaps for sqlite?

Discussion in 'Plugin Development' started by xize, Apr 19, 2014.

Thread Status:
Not open for further replies.
  1. Offline

    xize

    Hello,

    so I'm trying to convert my Yaml player backend to a sqlite backend my intention whas to serialize the HashMap including all contents of that invidual player and put it in the database, and also retrieve it that way.

    however I know that HashMap implements Serializeable but for some reason I cannot get my deserialize method to work, atleast serializing works now but getting throws a Exception, note I'm using Arrays.toString() to convert the jvm reference to a real byte[] array.

    my whole db class(there are also other errors but I need to know why deserializing fails):
    serializing and deserializing start on the methods getMap() and saveMap()
    Code:
    public class xEssentialsDB {
    private String player;
    private String uuid;
    private Player p;
    public xEssentialsDB(String player) throws Exception {
      this.player = player;
      this.uuid = getLastKnownUUID(player);
      if(this.uuid == null || this.uuid.equalsIgnoreCase("null")) {
      throw new NullPointerException("player does not exist!");
      }
      if(isNewDatabase()) {
      xEssentials.getPlugin().log("player sqlite backend not active, activating right now.", LogType.INFO);
      createTables();
      }
    }
    public xEssentialsDB(Player player, String uuid) {
      this.player = player.getName();
      this.uuid = uuid;
      this.p = player;
      if(isNewDatabase()) {
      xEssentials.getPlugin().log("player sqlite backend not active, activating right now.", LogType.INFO);
      createTables();
      if(!doesPlayerDataExist()) {
        player.sendMessage("boolean works, new player");
        registerPlayer();
      }
      } else {
      if(!doesPlayerDataExist()) {
        player.sendMessage("boolean works, new player");
        registerPlayer();
      }
      }
    }
    private final String getLastKnownUUID(String player) {
      try {
      Connection con = getConnection();
      Statement state = con.createStatement();
      ResultSet set = state.executeQuery("SELECT uuid FROM players WHERE user='" + player + "'");
      String id = set.getString("uuid");
      state.close();
      set.close();
      con.close();
      return id;
      } catch(Exception e) {}
      return null;
    }
    public void updatePlayer(String name, String uid) {
      try {
      Connection con = getConnection();
      Statement state = con.createStatement();
      state.executeUpdate("UPDATE players SET user='" + name + "' WHERE uuid='" + uid + "'");
      state.close();
      con.close();
      } catch(Exception e) {
      e.printStackTrace();
      }
    }
    public void registerPlayer() {
      if(p instanceof Player) {
      HashMap<String, Object> hash = new HashMap<String, Object>();
      hash.put("isDefault", true);
      hash.put("ip", p.getAddress().getAddress().getHostAddress());
      hash.put("user", player);
      hash.put("fly", false);
      hash.put("torch", false);
      hash.put("uuid", uuid);
      if(Configuration.getEconomyConfig().isEconomyEnabled()) {
        hash.put("money", Configuration.getEconomyConfig().getStartersMoney());
      }
      saveMap(hash);
      }
    }
    /**
      * @author xize
      * @param returns true whenever the database does not exists, else false
      * @return Boolean
      */
    public boolean isNewDatabase() {
      return !new File(xEssentials.getPlugin().getDataFolder() + File.separator + "databases" + File.separator + "players.db").exists();
    }
    /**
      * @author xize
      * @param creates the new tables into the database
      */
    public void createTables() {
      String table = "CREATE TABLE IF NOT EXISTS `players` ("+
        "`id` INT," +
        "`user` TEXT NOT NULL, " +
        "`uuid` TEXT UNIQUE NOT NULL, " +
        "`map` LONGBLOB NOT NULL, " +
        "PRIMARY KEY (`id`) " +
        ")";
      try {
      Connection con = getConnection();
      Statement state = con.createStatement();
      state.executeUpdate(table);
      state.close();
      con.close();
      } catch(Exception e) {
      e.printStackTrace();
      }
    }
    /**
      * @author xize
      * @param returns true whenever the map data has been found, else false
      * @return Boolean
      */
    public boolean doesPlayerDataExist() {
      try {
      Connection con = getConnection();
      Statement state = con.createStatement();
      ResultSet set = state.executeQuery("SELECT map FROM players WHERE uuid='" + uuid + "'");
      if(set.isBeforeFirst()) {
        state.close();
        set.close();
        con.close();
        return true;
      } else {
        state.close();
        set.close();
        con.close();
        return false;
      }
      } catch(Exception e) {
      e.printStackTrace();
      }
      return false;
    }
    /**
      * @author xize
      * @param saves the player data to the database, if the table does not exist creating a new one.
      * @param hash - the HashMap with default data or modified data which could be overwritten.
      */
    public void saveMap(HashMap<String, Object> hash) {
      try {
      Connection con = getConnection();
      Statement state = con.createStatement();
      SerializeObjectToBlob data = new SerializeObjectToBlob(hash);
      if(!doesPlayerDataExist()) {
        String query = "INSERT INTO players(user, uuid, map) VALUES('" + player + "','" + uuid + "','" + Arrays.toString(data.getBlob()) + "')";
        state.executeUpdate(query);
        state.close();
        con.close();
        return;
      } else {
        String query = "UPDATE players SET map='" + data + "' WHERE uuid='" + uuid + "'";
        state.executeUpdate(query);
        state.close();
        con.close();
        return;
      }
      } catch(Exception e) {
      e.printStackTrace();
      }
    }
    /**
      * @author xize
      * @param returns the HashMap, from the player, else a new generated HashMap will be generated and also be committed to the database.
      * @return HashMap<String, Object>
      * @throws NullPointerException, when the query is wrong and the player does not exist.
      */
    @SuppressWarnings("unchecked")
    public HashMap<String, Object> getMap() {
      if(doesPlayerDataExist()) {
      try {
        Connection con = getConnection();
        Statement state = con.createStatement();
        ResultSet set = state.executeQuery("SELECT * FROM players WHERE user='" + player + "'");
        DeSerializeObjectFromBlob data = new DeSerializeObjectFromBlob(set.getBytes("map"));
        HashMap<String, Object> hash = (HashMap<String, Object>) data.getObject();
        state.close();
        set.close();
        con.close();
        return hash;
      } catch(Exception e) {
        e.printStackTrace();
      }
      }
      throw new NullPointerException("unknown data");
    }
    /**
      * @author xize
      * @param returns fresh sqlite Connection
      * @return Connection
      */
    private Connection getConnection() {
      File dir = new File(xEssentials.getPlugin().getDataFolder() + File.separator + "databases");
      if(!dir.isDirectory()) {
      dir.mkdir();
      }
      try {
      Connection con = DriverManager.getConnection("jdbc:sqlite:plugins/xEssentials/databases/players.db");
      con.setAutoCommit(true);
      return con;
      } catch (Exception e) {
      xEssentials.getPlugin().log("couldn't find sqlite in craftbukkit, this is probably because you are running a outdated build!", LogType.SEVERE);
      }
      return null;
    }
    }
    class SerializeObjectToBlob implements Callable<byte[]> {
    private final Object obj;
    public SerializeObjectToBlob(Object obj) {
      this.obj = obj;
    }
    /**
      * @author xize
      * @param returns the byte[] array as a blob for mysql, this will be done through a separated thread.
      * @return byte[]
      * @throws Exception - when the duration takes to long
      */
    public byte[] getBlob() throws Exception {
      return call();
    }
    @Override
    public byte[] call() throws Exception {
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      ObjectOutputStream obout = new ObjectOutputStream(out);
      obout.writeObject(obj);
      obout.flush();
      obout.close();
      out.close();
      return out.toByteArray();
    }
    }
    class DeSerializeObjectFromBlob implements Callable<Object> {
    private final byte[] bytes;
    public DeSerializeObjectFromBlob(byte[] bytes) {
      this.bytes = bytes;
    }
    /**
      * @author xize
      * @param returns the object back to its orginal state byte for byte.
      * @return Object
      * @throws Exception - when the duration takes to long
      */
    public Object getObject() throws Exception {
      return call();
    }
    @Override
    public Object call() throws Exception {
      ByteArrayInputStream input;
      ObjectInputStream objinput;
      input = new ByteArrayInputStream(bytes);
      objinput = new ObjectInputStream(input);
      Object newobj = objinput.readObject();
      input.close();
      objinput.close();
      return newobj;
    }
    }
    
    stacktrace(its not the full stacktrace because I know there are also other errors such that the ResultSet is closed, but I don't know about this part):
    Code:
    [14:52:56] [Server thread/WARN]: java.io.StreamCorruptedException: invalid stream header: 5B2D3834
    [14:52:56] [Server thread/WARN]:  at java.io.ObjectInputStream.readStreamHeader(Unknown Source)
    [14:52:56] [Server thread/WARN]:  at java.io.ObjectInputStream.<init>(Unknown Source)
    [14:52:56] [Server thread/WARN]:  at tv.mineinthebox.essentials.utils.DeSerializeObjectFromBlob.call(xEssentialsDB.java:288)
    [14:52:56] [Server thread/WARN]:  at tv.mineinthebox.essentials.utils.DeSerializeObjectFromBlob.getObject(xEssentialsDB.java:280)
    [14:52:56] [Server thread/WARN]:  at tv.mineinthebox.essentials.utils.xEssentialsDB.getMap(xEssentialsDB.java:202)
    [14:52:56] [Server thread/WARN]:  at tv.mineinthebox.essentials.instances.HashCon.<init>(HashCon.java:27)
    [14:52:56] [Server thread/WARN]:  at tv.mineinthebox.essentials.instances.xEssentialsOfflinePlayer.<init>(xEssentialsOfflinePlayer.java:41)
    [14:52:56] [Server thread/WARN]:  at tv.mineinthebox.essentials.xEssentials.getOfflinePlayers(xEssentials.java:140)
    [14:52:56] [Server thread/WARN]:  at tv.mineinthebox.essentials.events.chat.ChatHighLightEvent.onTabComplete(ChatHighLightEvent.java:45)
    [14:52:56] [Server thread/WARN]:  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    [14:52:56] [Server thread/WARN]:  at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    [14:52:56] [Server thread/WARN]:  at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    [14:52:56] [Server thread/WARN]:  at java.lang.reflect.Method.invoke(Unknown Source)
    [14:52:56] [Server thread/WARN]:  at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:292)
    [14:52:56] [Server thread/WARN]:  at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:62)
    [14:52:56] [Server thread/WARN]:  at org.bukkit.plugin.TimedRegisteredListener.callEvent(TimedRegisteredListener.java:36)
    [14:52:56] [Server thread/WARN]:  at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:501)
    [14:52:56] [Server thread/WARN]:  at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:486)
    [14:52:56] [Server thread/WARN]:  at org.bukkit.craftbukkit.v1_7_R2.CraftServer.tabCompleteChat(CraftServer.java:1570)
    [14:52:56] [Server thread/WARN]:  at org.bukkit.craftbukkit.v1_7_R2.CraftServer.tabComplete(CraftServer.java:1544)
    [14:52:56] [Server thread/WARN]:  at net.minecraft.server.v1_7_R2.MinecraftServer.a(MinecraftServer.java:962)
    [14:52:56] [Server thread/WARN]:  at net.minecraft.server.v1_7_R2.PlayerConnection.a(PlayerConnection.java:1626)
    [14:52:56] [Server thread/WARN]:  at net.minecraft.server.v1_7_R2.PacketPlayInTabComplete.a(SourceFile:31)
    [14:52:56] [Server thread/WARN]:  at net.minecraft.server.v1_7_R2.PacketPlayInTabComplete.handle(SourceFile:9)
    [14:52:56] [Server thread/WARN]:  at net.minecraft.server.v1_7_R2.NetworkManager.a(NetworkManager.java:147)
    [14:52:56] [Server thread/WARN]:  at net.minecraft.server.v1_7_R2.ServerConnection.c(SourceFile:134)
    [14:52:56] [Server thread/WARN]:  at net.minecraft.server.v1_7_R2.MinecraftServer.v(MinecraftServer.java:657)
    [14:52:56] [Server thread/WARN]:  at net.minecraft.server.v1_7_R2.DedicatedServer.v(DedicatedServer.java:250)
    [14:52:56] [Server thread/WARN]:  at net.minecraft.server.v1_7_R2.MinecraftServer.u(MinecraftServer.java:548)
    [14:52:56] [Server thread/WARN]:  at net.minecraft.server.v1_7_R2.MinecraftServer.run(MinecraftServer.java:459)
    [14:52:56] [Server thread/WARN]:  at net.minecraft.server.v1_7_R2.ThreadServerApplication.run(SourceFile:618)
    
    the table map looks like this is a byte[] array:
    Code:
    [-84, -19, 0, 5, 115, 114, 0, 17, 106, 97, 118, 97, 46, 117, 116, 105, 108, 46, 72, 97, 115, 104, 77, 97, 112, 5, 7, -38, -63, -61, 22, 96, -47, 3, 0, 2, 70, 0, 10, 108, 111, 97, 100, 70, 97, 99, 116, 111, 114, 73, 0, 9, 116, 104, 114, 101, 115, 104, 111, 108, 100, 120, 112, 63, 64, 0, 0, 0, 0, 0, 12, 119, 8, 0, 0, 0, 16, 0, 0, 0, 7, 116, 0, 5, 116, 111, 114, 99, 104, 115, 114, 0, 17, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 66, 111, 111, 108, 101, 97, 110, -51, 32, 114, -128, -43, -100, -6, -18, 2, 0, 1, 90, 0, 5, 118, 97, 108, 117, 101, 120, 112, 0, 116, 0, 3, 102, 108, 121, 113, 0, 126, 0, 4, 116, 0, 9, 105, 115, 68, 101, 102, 97, 117, 108, 116, 115, 113, 0, 126, 0, 3, 1, 116, 0, 5, 109, 111, 110, 101, 121, 115, 114, 0, 16, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 68, 111, 117, 98, 108, 101, -128, -77, -62, 74, 41, 107, -5, 4, 2, 0, 1, 68, 0, 5, 118, 97, 108, 117, 101, 120, 114, 0, 16, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 78, 117, 109, 98, 101, 114, -122, -84, -107, 29, 11, -108, -32, -117, 2, 0, 0, 120, 112, 64, 36, 0, 0, 0, 0, 0, 0, 116, 0, 4, 117, 117, 105, 100, 116, 0, 7, 88, 101, 112, 104, 48, 114, 101, 116, 0, 4, 117, 115, 101, 114, 113, 0, 126, 0, 13, 116, 0, 2, 105, 112, 116, 0, 9, 49, 50, 55, 46, 48, 46, 48, 46, 49, 120]
    


    edit, got it fixed:D ive instead used the PreparedStatement to add the byte[] array through that and it works also implemented Serializeable and removed the player instance.
     
  2. Offline

    TomFromCollege

    Ok - I can't explain your error, but seriously, think about implementing a custom serialization/deserialization of your data. I've just started working with a team who is serializing everything and it means that if I want to update code, all of that default serialization becomes invalid.
     
  3. Offline

    xize

    TomFromCollege
    I understand that, but the problem is that I cannot depend on the keys neither the values if you ment with manually serialize things, for example one player has isTorch in the config the other not.

    though my player api whas based on Yaml and used contains() to check the keys but now I want to write a backend behind a backend with a custom object which actually bridge methods from YamlConfiguration but then through a HashMap from sqlite so my player api won't get affected to much in code, note I'm not using YamlConfiguration anymore just mirroring the methods.
     
Thread Status:
Not open for further replies.

Share This Page