JDK-4513615 : AbstractDocument containing multibyte content cannot be serialized
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.4.0
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2001-10-11
  • Updated: 2003-02-14
  • Resolved: 2003-02-14
Related Reports
Duplicate :  
Description

Name: bsT130419			Date: 10/11/2001


java version "1.3.1"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1-b24)
Java HotSpot(TM) Client VM (build 1.3.1-b24, mixed mode)

The Swing AbstractDocument uses a non-serializable object to indicate the
multibyte nature of the document content.  Rather than using a String (like all
other properties), the following line appears in the jdk1.3.1 source for
javax.swing.text.AbstractDocument:

    static final Object MultiByteProperty = new Object();

Since this property is added to the document property Dictionary when multibyte
content is encountered, the document becomes un-serializable.

1. Steps to reproduce:
   a) Create a document derived from javax.swing.text.AbstractDocument.
   b) Insert a string containing multibyte content.
   c) Attempt to serialize the document object, and notice that it fails.

2) Source code:

<source-code>
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.Serializable;
import java.util.Dictionary;
import java.util.Enumeration;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.BadLocationException;

public class DocumentSerializationBug {
    
    public static void main(String[] args) {
        // Test a "normal" document.
        String goodContent = "This is a character <= 255: " + '\u00FF';
        testDocument(goodContent);

        // Test a document with multibyte content.
        String badContent  = "This is a character > 255: "  + '\u0100';
        testDocument(badContent);
    }
    
    public static void testDocument(String theString) {
        System.out.println("Testing: \"" + theString + "\"");
        
        // Build a document and insert the string.
        Document doc = new PlainDocument();
        try {
            doc.insertString(0, theString, new SimpleAttributeSet());
        }
        catch (BadLocationException e) {
            System.out.println("Ignored: " + e);
        }
        
        // Dump the document properties.  All properties need to be
        // serializable for the document to serialize.
        Dictionary props = ((PlainDocument)doc).getDocumentProperties();
        Enumeration enum = props.keys();
        while (enum.hasMoreElements()) {
            Object key = enum.nextElement();
            System.out.print(">> Doc Property key=" + key);
            System.out.print(" [class=" + key.getClass() + "] ");
            if (key instanceof Serializable) {
                System.out.println(" Serializable");
            }
            else {
                System.out.println(" NOT Serializable");
            }
            System.out.println(">>              val=" + props.get(key));
        }

        // Attempt to serialize the document.
        try {
            FileOutputStream fos = new FileOutputStream("test.ser");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(doc);
            fos.close();
        }
        catch (NotSerializableException ns) {
            System.out.println("** Document Not Serializable: " + ns);
            ns.printStackTrace(System.out);
        }
        catch (IOException e) {
            System.out.println("File Error: " + e);
        }
    }
    
}
</source-code>

3+4. Error Message and Trace:
java.io.NotSerializableException: java.lang.Object
        at java.io.ObjectOutputStream.outputObject(ObjectOutputStream.java:1148)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:366)
        at java.util.Hashtable.writeObject(Hashtable.java:756)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.io.ObjectOutputStream.invokeObjectWriter
(ObjectOutputStream.java:1864)
        at java.io.ObjectOutputStream.outputObject(ObjectOutputStream.java:1210)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:366)
        at java.io.ObjectOutputStream.outputClassFields
(ObjectOutputStream.java:1827)
        at java.io.ObjectOutputStream.defaultWriteObject
(ObjectOutputStream.java:480)
        at java.io.ObjectOutputStream.outputObject(ObjectOutputStream.java:1214)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:366)
        at DocumentSerializationBug.testDocument
(DocumentSerializationBug.java:63)
        at DocumentSerializationBug.main(DocumentSerializationBug.java:26)
(Review ID: 133579) 
======================================================================

Comments
WORK AROUND Name: bsT130419 Date: 10/11/2001 Only use ASCII content for documents that need to be serialized. Alternatively, create an extension to AbstractDocument that overrides getDocumentProperties() to return a custom Dictionary which wraps the normal property dictionary and filters out any non-serializable properties that are added. I'm not sure of the impact on line-breaking if you do this, since this is where the MultiByteProperty is used. Note that you cannot override the AbstractDocument.putProperty() method, since it is final. ======================================================================
11-06-2004