JDK-4993777 : REGRESSION:XMLEncoder/Decoder does not support arrays with more than 1 dimension
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.beans
  • Affected Version: 5.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2004-02-12
  • Updated: 2004-03-15
  • Resolved: 2004-03-15
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
5.0 b43Fixed
Related Reports
Relates :  
Relates :  
Description
Name: rmT116609			Date: 02/12/2004


FULL PRODUCT VERSION :
java version "1.5.0-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta-b32c)
Java HotSpot(TM) Client VM (build 1.5.0-beta-b32c, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows 2000 [Version 5.00.2195]

A DESCRIPTION OF THE PROBLEM :
See attached code.

ERROR reported

java.lang.ClassNotFoundException: [Lde.gaskin.xml.test.EnDecodeBug1$Obj;
Continuing ...
java.lang.RuntimeException: failed to evaluate: <unbound>=Class.forName("[Lde.ga
skin.xml.test.EnDecodeBug1$Obj;");
Continuing ...

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See comments in source code

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
As with JDK1.4.X
ACTUAL -
The XMLEndoder produced file is incomplete

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.ClassNotFoundException: [Lde.gaskin.xml.test.EnDecodeBug1$Obj;
Continuing ...
java.lang.RuntimeException: failed to evaluate: <unbound>=Class.forName("[Lde.ga
skin.xml.test.EnDecodeBug1$Obj;");
Continuing ...

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package de.gaskin.xml.test;

import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

/** Demonstrate XMLEncoder/XMLDecoder BUG with arrays that have a dimension > 1
  in JDK1.5 beta1.
  Compile with 1.4.X or 1.5.
  Run with 1.4 OK.
  Run with 1.5   E R R O R.
  Usage:
     java de.gaskin.xml.test.EnDecodeBug1 [-nowrite] [filename]
     Default filename is "bug1.xml"
     No switch (-nowrite) writes with XMLEncoder and reads back the same file
        with XMLDecoder
     -nowrite only reads
  Tests:
    With 1.4.X
       - java de.gaskin.xml.test.EnDecodeBug1
       - Is OK and produces "bug1.xml" in current directory
    With 1.5.0 beta 1 RUN 1
       - java de.gaskin.xml.test.EnDecodeBug1 -nowrite
       - ERRORS reading in!!!!
    With 1.5.0 beta 1 RUN 2
       - java de.gaskin.xml.test.EnDecodeBug1 -nowrite
       - ERRORS writing out
*/

public class EnDecodeBug1 {
   Obj[]   array;
   Obj[][] array2D;
   public EnDecodeBug1() {
   }
   public EnDecodeBug1(Obj[] array) {
      this.array = array;
   }
   public void setArray(Obj[] array) {
      this.array = array;
   }
   public Obj[] getArray() {
      return array;
   }
   public void setArray2D(Obj[][] array2D) {
      this.array2D = array2D;
   }
   public Obj[][] getArray2D() {
      return array2D;
   }
   public void encode(String filename) throws FileNotFoundException {
      XMLEncoder encoder = new XMLEncoder(new FileOutputStream(filename));
      encoder.writeObject(this);
      encoder.close();
   }
   public void list() {
      list(array, "");
      if (array2D != null) {
         int loopCnt = array2D.length;
         for (int i = 0;i < loopCnt;i++) {
            String prefix = "[" + i + "]";
            list(array2D[i], prefix);
         }
      }
      else {
         System.err.println("'array2D' is 'null' BUT SHOULD NOT BE NULL!!!");
      }
   }
   public void list(Obj[] array, String prefix) {
      if (array != null) {
         int loopCnt = array.length;
         for (int i = 0;i < loopCnt;i++) {
            System.err.println(prefix + array[i]);
         }
      }
   }
   public static EnDecodeBug1 decode(String filename)
   throws FileNotFoundException {
      XMLDecoder decoder = new XMLDecoder(new FileInputStream(filename));
      EnDecodeBug1 rc = (EnDecodeBug1)decoder.readObject();
      decoder.close();
      return rc;
   }
   public static class Obj {
      String attr;
      public Obj() {
      }
      public Obj(String attr) {
         this.attr = attr;
      }
      public void setAttr(String attr) {
         this.attr = attr;
      }
      public String getAttr() {
         return attr;
      }
      public String toString() {
         return attr;
      }
   }
   public static void main(String[] args) throws Exception {
      String  filename = "bug1.xml";
      boolean write    = true;
      if (args.length > 0) {
         if (args[0].charAt(0) == '-') {
            if (args[0].equals("-nowrite")) {
               write = false;
               if (args.length > 1) {
                  filename = args[1];
               }
            }
            else {
               System.err.println("Only known switch is '-nowrite' ABORT");
               System.exit(-1);
            }
         }
         else {
            filename = args[0];
         }
      }
      if (write) {
         Obj[] array = new Obj[2];
         array[0] = new Obj("one");
         array[1] = new Obj("two");
         Obj[][] array2D = {
            array,
         };
         EnDecodeBug1 test = new EnDecodeBug1(array);
         test.setArray2D(array2D);
         test.encode(filename); ;
         test.list();
      }

      System.err.println("And now read back");
      EnDecodeBug1 test1 = EnDecodeBug1.decode(filename);
      test1.list();
   }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Don't use multidimensional arrays with XMLEncoder/XMLDecoder

Release Regression From : 1.4.2
The above release value was the last known release where this 
bug was known to work. Since then there has been a regression.

(Incident Review ID: 237793) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger-beta2 FIXED IN: tiger-beta2 INTEGRATED IN: tiger-b43 tiger-beta2
14-06-2004

EVALUATION Bug is reproducible as described, starting in Tiger b32. The key to this bug is the ClassNotFoundException: java.lang.ClassNotFoundException: [LEnDecodeBug1$Obj; (I changed the test case to not use a package, btw). Because the XMLEncoding mechanism is written to be fault-tolerant, we don't see the full stack trace. Adding a printStack() at the proper spot gives us: java.lang.ClassNotFoundException: [LEnDecodeBug1$Obj; at java.net.URLClassLoader+1.run(URLClassLoader.java:200) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:289) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:279) --> at java.lang.ClassLoader.loadClass(ClassLoader.java:235) at com.sun.beans.ObjectHandler.classForName(ObjectHandler.java:67) at com.sun.beans.ObjectHandler.classForName(ObjectHandler.java:54) at java.beans.Statement.invoke(Statement.java:139) at java.beans.Expression.getValue(Expression.java:98) at java.beans.Encoder.getValue(Encoder.java:85) at java.beans.Encoder.get(Encoder.java:178) at java.beans.PersistenceDelegate.writeObject(PersistenceDelegate.java:94) at java.beans.Encoder.writeObject(Encoder.java:54) at java.beans.XMLEncoder.writeObject(XMLEncoder.java:254) at java.beans.Encoder.writeExpression(Encoder.java:257) at java.beans.XMLEncoder.writeExpression(XMLEncoder.java:369) at java.beans.PersistenceDelegate.writeObject(PersistenceDelegate.java:97) at java.beans.Encoder.writeObject(Encoder.java:54) at java.beans.XMLEncoder.writeObject(XMLEncoder.java:254) Build 32 had very few beans putbacks. But, I did see that the following jmx/classes fix was putback: 4943359 Should use Class.forName(name,loader) not loader.loadClass(name) and I notice that we are in fact calling ClassLoader.loadClass(). A couple other bugs related to 4943359 have been fixed - 4967716 & 4974913. Each states the following: "This is a case of using ClassLoader.loadClass instead of Class.forName(..., classLoader). The typical symptom of using loadClass instead of forName is that it fails if and only if you try to load the class X[] before you have loaded the class X." Given that this bug has been filed in regards to arrays, these certainly seem to be related. I tried the obvious change, calling Class.forName(name, loader) in place of ClassLoader.loadClass(), and the test passes. The question remains: what changed in b32 such that ClassLoader.loadClass() is giving such trouble? ###@###.### 2004-02-20 The answer is: 4872868: need better validation of class names ###@###.### 2004-02-27
20-02-2004