2

I have an object in Jython that extends a Java Swing JFrame. My goal is to serialize it in order to save it on MySql, query the database, deserialize it and review the JFrame exactly as it was before it was deserialized (with all fields filled in).

I honestly don't know where to start. I can't imagine serialization - saving to database. I guess the serialized JFrame could be blob, longblob or bit type on MySql.

For now I have done a local experiment using my old and little academic knowledge on the serialization of objects in java, with poor results:

def saveArt(self, e):
   v = Vector()
   v.add(self) # self = JFrame in question
   out = ObjectOutputStream(BufferedOutputStream(FileOutputStream("prova.dat")))
   out.writeObject(v)
   out.close()

What I did was just put the frame in a Java vector and save it to the file, however I got the following error:

Exception in thread "AWT-EventQueue-0"  at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.writeObject(Unknown Source)
        at java.util.concurrent.ConcurrentHashMap.writeObject(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.writeObject(Unknown Source)
        at java.util.concurrent.ConcurrentHashMap.writeObject(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.writeObject(Unknown Source)
        at javax.swing.event.EventListenerList.writeObject(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
        at java.io.ObjectOutputStream.defaultWriteObject(Unknown Source)
        at javax.swing.JComponent.writeObject(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.writeObject(Unknown Source)
        at java.awt.AWTEventMulticaster.saveInternal(Unknown Source)
        at java.awt.AWTEventMulticaster.saveInternal(Unknown Source)
        at java.awt.AWTEventMulticaster.saveInternal(Unknown Source)
        at java.awt.AWTEventMulticaster.saveInternal(Unknown Source)
        at java.awt.AWTEventMulticaster.saveInternal(Unknown Source)
        at java.awt.AWTEventMulticaster.saveInternal(Unknown Source)
        at java.awt.AWTEventMulticaster.saveInternal(Unknown Source)
        at java.awt.AWTEventMulticaster.saveInternal(Unknown Source)
        at java.awt.AWTEventMulticaster.save(Unknown Source)
        at java.awt.Component.writeObject(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.writeArray(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.access$300(Unknown Source)
        at java.io.ObjectOutputStream$PutFieldImpl.writeFields(Unknown Source)
        at java.io.ObjectOutputStream.writeFields(Unknown Source)
        at java.util.Vector.writeObject(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.writeObject(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
java.io.NotSerializableException: java.io.NotSerializableException: org.python.core.packagecache.SysPackageManager

This experiment is of little importance, because it is certainly not the final goal. It was just to figure out if I could serialize it locally.

The final goal is to save it on the MySql database and extract it exactly as it was saved.

So I need to understand 3 things:

  1. how to serialize JFrame with Jython (assuming serialization is the best way to "package" a filled JFrame for a database, if there's a better way don't hesitate to say so)
  2. what kind of data to set on Mysql (assuming MySql is the perfect dbms to do this, if there are better technologies, don't hesitate to say it!) and if you need to make a particular query
  3. how to deserialize it to see it exactly as I left it (assuming deserialization is the best way to retrieve the JFrame)

Thank you so much in advance my saviors (if there are any)

Memmo
  • 298
  • 3
  • 8
  • 31
  • 1
    Don't do that. Save the field values instead. – Olivier Aug 20 '20 at 20:34
  • That's not what I want, the JFrame in question is too complex to save field by field. – Memmo Aug 21 '20 at 05:25
  • You can write some code that automatically finds all fields and extracts their values. You can also use the [`name`](https://docs.oracle.com/javase/8/docs/api/java/awt/Component.html#setName-java.lang.String-) property to identify the fields. – Olivier Aug 21 '20 at 07:13
  • Yes that's the idea if I can't save the whole frame, but I would seriously try to avoid it... – Memmo Aug 22 '20 at 08:24

2 Answers2

3

Firstly, Java Serialization is a bad idea at the best of times. The Secure Coding Guidelines for Java SE says "Note: Deserialization of untrusted data is inherently dangerous and should be avoided." Unfortunately that text is no longer in red.

It is even more problematic in this case as that data will just be in a blob in the database, and Swing doesn't guarantee serialisation compatibility between versions (perhaps even updates?).

That out of the way: From the stracktrace, the problem object is a SysPackageManager somewhere within ConcurrentHashMap itself within ConcurrentHashMap itself, which you probably don't want to keep.

Setting the system property sun.io.serialization.extendedDebugInfo (may change) to true will give some more information as to how the object is referenced.

Clearly JFrame is too much. There are better ways to store data.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • I will wait for the end of the bounty and I believe I will follow the classic approach. How about saving tables and nested tables as json as mysql field? – Memmo Aug 25 '20 at 09:40
0

Tom has covered reasons of error and possible ways to debug further

I will try to address the 3 things you asked in detail with java code:

  1. How to serialize ? - Answered below
  2. Which datatype to use to create mysql column to store this data ? - I would say mediumblob in case of mysql, blob might to too small and longblob might be too big. I used longtext as well and it works as expected without any issues, but its really long answer to explain why blob and not text fields. May be this stackoverflow answer will throw more light - What column type should be used to store serialized data in a mysql db?
  3. How to de-serialize and use ? - Answered below

Moving to answer: You can achieve this in few steps
  1. create JFrame

  2. serializing java object to mysql database - this is as simple as

    preparedStatement.setObject(jframe_object_to_save_to_database);
    pstmt.executeUpdate();
    
  3. de-serializing java object from mysql database and converting it into JFrame object - this is not that straight forward, but not that complicate too, my approch will be of 4 steps like below

    3.a. Just read your column value as bytes array
    3.b. create a ByteArrayInputStream object of the bytes array we got from database
    3.c. create ObjectInputStream from ByteArrayInputStream
    3.d. use ObjectInputStream.readObject() method to get de-serialized object

    These steps are covered in the below code snippet

        byte[] buf = rs.getBytes(1);
        ObjectInputStream objectIn = null;
        if (buf != null)
            objectIn = new ObjectInputStream(new ByteArrayInputStream(buf));
    
        Object deSerializedObject = objectIn.readObject();
    
  4. Now as we have the de-serialized object ready for our use (from 3.d), you can parse it into a JFrame object easily and use it as you need

Jframe deSerializedObject = (JFrame) objectIn.readObject();

Below is the complete java implementation that I have, it is a simple JFrame working code with mysql database, please follow Step 1 thru Step 5 which I wrote as comments in the code and also read the Note that I mentioned in deSerializeJavaObjectFromDB method

import java.awt.FlowLayout;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class SerializeToDatabase {

    private static final String SQL_CREATE_TABLE = "create table if not exists serialized_java_objects (object_name varchar(1000), serialized_object blob)";
    private static final String SQL_SERIALIZE_OBJECT = "INSERT INTO serialized_java_objects(object_name, serialized_object) VALUES (?, ?)";
    private static final String SQL_DESERIALIZE_OBJECT = "SELECT serialized_object FROM serialized_java_objects limit 1";

    public static void createTable(Connection connection) throws SQLException {
        connection.createStatement().executeUpdate(SQL_CREATE_TABLE);
    }
    
    public static void serializeJavaObjectToDB(Connection connection,
            Object objectToSerialize) throws SQLException {

        PreparedStatement pstmt = connection
                .prepareStatement(SQL_SERIALIZE_OBJECT);

        // just setting the class name
        pstmt.setString(1, objectToSerialize.getClass().getName());
        pstmt.setObject(2, objectToSerialize);
        pstmt.executeUpdate();
        pstmt.close();
        
        System.out.println("Java object serialized to database. Object: " + objectToSerialize);

    }

    /**
     * To de-serialize a java object from database
     *
     * @throws SQLException
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static Object deSerializeJavaObjectFromDB(Connection connection) throws SQLException, IOException,
            ClassNotFoundException {
        PreparedStatement pstmt = connection.prepareStatement(SQL_DESERIALIZE_OBJECT);
        ResultSet rs = pstmt.executeQuery();
        rs.next();

        //NOTE - below is the most basic way of retrieving data from result set, works perfect for general data 
        //Object object = rs.getObject(1);

        //NOTE - below is the way how we need to implement to retrieve serialized objects from resultset
        byte[] buf = rs.getBytes(1);
        ObjectInputStream objectIn = null;
        if (buf != null)
            objectIn = new ObjectInputStream(new ByteArrayInputStream(buf));

        Object deSerializedObject = objectIn.readObject();

        rs.close();
        pstmt.close();

        System.out.println("Java object de-serialized from database. Object: "
                + deSerializedObject + " Classname: "
                + deSerializedObject.getClass().getName());
        
        return deSerializedObject;
    }

    public static Connection getMySqlConnection(String ipAddr, String portNumber, String db, String userName, String password) throws SQLException {
        
        Connection mysqlConn = null;
        
        Properties properties = new Properties();
        properties.put("user", userName);
        properties.put("password", password);
        
        mysqlConn = DriverManager.getConnection("jdbc:mysql://"+ipAddr+":"+portNumber+"/"+db, properties);
        return mysqlConn;
    }
    
    /**
     * Serialization and de-serialization of java object from mysql
     *
     * @throws ClassNotFoundException
     * @throws SQLException
     * @throws IOException
     */
    public static void main(String args[]) throws ClassNotFoundException,
            SQLException, IOException {
    
        //step 1 - create mysql connection
        Connection connection = getMySqlConnection("192.168.1.119", "3306", "xxx", "xxx", "xxx");

        //step 2 - create JFrame
        JFrame frame = new JFrame("JFrame Example");  
        JPanel panel = new JPanel();  
        panel.setLayout(new FlowLayout());  
        JLabel label = new JLabel("JFrame By Example");  
        JButton button = new JButton();  
        button.setText("Button");  
        panel.add(label);  
        panel.add(button);  
        frame.add(panel);  
        frame.setSize(200, 300);  
        frame.setLocationRelativeTo(null);  
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //uncomment below line if you want to see the JFrame before persisting data
        //frame.setVisible(true);

        //step 3 - create table
        createTable(connection);
        
        //step 4 - serializing java object to mysql database
        serializeJavaObjectToDB(connection, frame);

        //step 5 - de-serializing java object from mysql database and converting it into JFrame object
        JFrame objFromDatabase = (JFrame) deSerializeJavaObjectFromDB(connection);
        //setVisible(true) will show the JFrame as is, what ever we have persisted
        objFromDatabase.setVisible(true);
        
        //finally close the connection
        connection.close();
    }
}

Note1: I have no jython setup, so I am unable to provide python/jython code snippet here. But I am trying to answer this in pure java, I think you can relate it easily to your need in jython (as the classes that I've used do not vary)

Note2: I did not want to debate if persistence of serialization is right or wrong, that depends on type of application, type of users, type of project and many other scenarios. You are better judge for your case

Note3: This post helped me to answer this question better - https://javapapers.com/core-java/serialize-de-serialize-java-object-from-database/

Chandan
  • 640
  • 4
  • 10