2

Hi we are generating a desktop application. In our Project we need to use javax.swing.ImageIcon and if server requests we need to send this object. Here the problem is if we are using same JRE in both environments its working fine, if no, we are getting java.io.InvalidClassException: javax.swing.ImageIcon; local class incompatible: stream classdesc serialVersionUID = -962022720109015502, local class serialVersionUID = 532615968316031794 is there any solution to avoid this, Any suggestion appreciated. Thanks in advance

for this what I've done is, I just got ImageIcon.java file and removed the package by using my own package. Here I've hardcoded serialversionuid, is this recommended...?

Rakesh
  • 221
  • 1
  • 4
  • 9

3 Answers3

4

Javadoc for javax.swing.ImageIcon contains:

Warning: Serialized objects of this class will not be compatible with future Swing releases. The current serialization support is appropriate for short term storage or RMI between applications running the same version of Swing. As of 1.4, support for long term storage of all JavaBeansTM has been added to the java.beans package. Please see java.beans.XMLEncoder.

As suggested there you should try using java.beans.XMLEncoder (and java.beans.XMLDecoder) instead of serializing.

Volker Seibt
  • 1,479
  • 16
  • 19
3

The classes are not compatible. The solution is to either use the same JRE on both sides, or not use serialized ImageIcon.

Kayaman
  • 72,141
  • 5
  • 83
  • 121
  • It is mandatory for me to use ImageIcon, now I can't change it. and as I already mentioned, we are generating desktop application, so I can't force my client to use specific version of jre. Hope you understand the complexity of this – Rakesh Sep 13 '13 at 09:04
  • There are ways to include a jre with your distribution but this depends heavily on how and on which platforms you distribute. For windows a launch4j installer with bundled jre works pretty great: [see this SO question](http://stackoverflow.com/questions/7071133/how-to-bundle-a-jre-with-launch4j) – NickDK Sep 13 '13 at 09:33
  • 1
    Is there another way you could transmit ImageIcon? Do you need all the data in it, or just the graphics data and some attributes? Serialization is your problem here. – Kayaman Sep 13 '13 at 09:54
  • yes, we need to transmit only Image data, so I'm capturing that into ImageIcon and transmitting it. – Rakesh Sep 13 '13 at 10:30
  • for this what I've done is, I just got ImageIcon.java file and removed the package by using my own package. Here I've hardcoded serialversionuid, is this recommended...? – Rakesh Sep 13 '13 at 10:36
  • @Rakesh I wouldn't recommend that. Since you don't need all the data in ImageIcon, you could create your own class that contains the data you need and make it Serializable. Having Swing's ImageIcon and your own ImageIcon can create unnecessary confusion. – Kayaman Sep 13 '13 at 11:26
0

There is a solution to use a serialized ImageIcon class with a version of java less than 1.6.0_26 and deserialized with a java version from 1.6.0_26 by replacing the serialVersionUID field:

/**
 * Replace serialVersionUID of class {@link javax.swing.ImageIcon}.
 *
 * Hack due to serialVersionUID change but data has not changed since java 1.6.0_26. Cf. http://stackoverflow.com/questions/18782275/facing-issue-with-serialversionuid
 *
 * @return the bytes array converted; or null if conversion failed, or no conversion has been done.
 */
private byte[] convertSerializedData(final byte[] viewsData) {
    try (final ByteSequence byteSequence = new ByteSequence(viewsData)) {
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        final DataOutputStream dataOutputStream = new DataOutputStream(baos);
        while (byteSequence.available() > 0) {
            final byte readByte = byteSequence.readByte();
            dataOutputStream.writeByte(readByte);
            if (readByte == ObjectStreamConstants.TC_CLASSDESC) {
                byteSequence.mark(byteSequence.getIndex());
                boolean discard = false;
                try {
                    final String className = byteSequence.readUTF();
                    long serialVersionUID = byteSequence.readLong();
                    if ("javax.swing.ImageIcon".equals(className) && serialVersionUID == 532615968316031794L) {
                        // Replace serialVersionUID of class javax.swing.ImageIcon
                        serialVersionUID = -962022720109015502L;
                        dataOutputStream.writeUTF(className);
                        dataOutputStream.writeLong(serialVersionUID);
                    } else
                        discard = true;
                } catch (final Exception e) {
                    // Failed to read class name, discard this read
                    discard = true;
                }
                if (discard)
                    byteSequence.reset();
            }
        }
        dataOutputStream.flush();
        return baos.toByteArray();
    } catch (final Exception e) {
        // Conversion failed
    }
    return null;
}

Here is an example of using this method that is called only if an exception is thrown:

public void restoreViews(final byte[] viewsData) {
    try {
        if (viewsData != null) {
            final ByteArrayInputStream bais = new ByteArrayInputStream(viewsData);
            final ObjectInputStream objectInputStream = new ObjectInputStream(bais);
            readInputStream(objectInputStream);
            objectInputStream.close();
        }
    } catch (final Exception e) {
        try {
            final byte[] viewsDataConverted = convertSerializedData(viewsData);
            if (viewsDataConverted != null) {
                final ByteArrayInputStream bais = new ByteArrayInputStream(viewsDataConverted);
                final ObjectInputStream objectInputStream = new ObjectInputStream(bais);
                readInputStream(objectInputStream);
                objectInputStream.close();
            }
        } catch (final Exception e2) {
            InfonodeViewManager.LOGGER.error("Unable to restore views", e);
        }
    }
}