Running on Java 24-ea+24-2960 (Preview)
Home of The JavaSpecialists' Newsletter

013bSerializing GUI Components Across Network (Follow-Up)

Author: Dr. Heinz M. KabutzDate: 2001-03-15Java Version: 1.3Category: GUI
 

Abstract: The previous newsletter had a dependency that stopped us serializing the components. The code worked when compiled with JBuilder 2, but not when compiled with the JDK 1.3.

 

Hi again,

Imagine my horror this morning when I tried to run the code from last nights newsletter and discovered that it generated an exception! On frantic searching I figured that the JBuilder 3.0 compiler produces a different result to the SUN JDK 1.3 compiler. I had only run the program from within JBuilder, so I got quite a surprise that it did not work in SUN. The reason I'm blaming the compiler is because when I ran the class files generated by JBuilder 3.0 with the JDK 1.3 VM it works perfectly.

The problem is that if you call out.defaultWriteObject(), the two compilers have different ideas of which object you are calling this from, because you are inside an inner class. The SUN compiler thinks you are calling it from ComponentSerializer and that is not serializable.

The solution is quite simple, just move the ComponentEncapsulator class out of the ComponentSerializer class and it should work with the JDK 1.3 compiler.

We thus end up with ComponentSerializer:

//: ComponentSerializer.java
import java.io.*;
import java.awt.*;
public class ComponentSerializer {
  public void write(Component comp, OutputStream out)
      throws IOException {
    System.out.println("writing " + comp);
    ObjectOutputStream oout = new ObjectOutputStream(out);
    oout.writeObject(new ComponentEncapsulator(comp));
    oout.reset();
    oout.flush();
  }
  public Component read(InputStream in)
      throws IOException, ClassNotFoundException {
    System.out.println("reading component");
    ObjectInputStream oin = new ObjectInputStream(in);
    ComponentEncapsulator enc =
      (ComponentEncapsulator)oin.readObject();
    return enc.getComponent();
  }
}

and ComponentEncapsulator:

//: ComponentEncapsulator.java
import java.io.*;
import java.awt.*;
import javax.swing.*;
import java.lang.reflect.*; // wouldn't be right for me to send
           // you a newsletter that doesn't use reflection :)
class ComponentEncapsulator implements Serializable {
  private final Component comp;
  private IOException defaultWriteException;
  public ComponentEncapsulator(Component comp) {
    this.comp = comp;
  }
  public Component getComponent() {
    return comp;
  }
  private void writeObject(final ObjectOutputStream out)
      throws IOException {
    if (SwingUtilities.isEventDispatchThread()) {
      // This is all that is necessary if we are already in
      // the event dispatch thread, e.g. a user clicked a
      // button which caused the object to be written
      out.defaultWriteObject();
    } else {
      try {
        // we want to wait until the object has been written
        // before continuing.  If we called this from the
        // event dispatch thread we would get an exception
        SwingUtilities.invokeAndWait(new Runnable() {
          public void run() {
            try {
              // easiest way to indicate to the enclosing class
              // that an exception occurred is to have a member
              // which keeps the IOException
              defaultWriteException = null;
              // we call the actual write object method
              out.defaultWriteObject();
            } catch(IOException ex) {
              // oops, an exception occurred, remember the
              // exception object
              defaultWriteException = ex;
            }
          }
        });
        if (defaultWriteException != null) {
          // an exception occurred in the code above, throw it!
          throw defaultWriteException;
        }
      } catch(InterruptedException ex) {
        // I'm not quite sure what do here, perhaps:
        Thread.currentThread().interrupt();
        return;
      } catch(InvocationTargetException ex) {
        // This can actually only be a RuntimeException or an
        // Error - in either case we want to rethrow them
        Throwable target = ex.getTargetException();
        if (target instanceof RuntimeException) {
          throw (RuntimeException)target;
        } else if (target instanceof Error) {
          throw (Error)target;
        }
        ex.printStackTrace(); // this should not happen!
        throw new RuntimeException(ex.toString());
      }
    }
  }
}

This highlights again that we have to be quite careful with Java. In the big project that we work on, we have standardized on the JDK 1.3 compiler and ALL our classes have to be compiled with that. This happened only after much nailbiting because of slight compiler differences of various IDEs.

 

Comments

We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)

When you load these comments, you'll be connected to Disqus. Privacy Statement.

Related Articles

Browse the Newsletter Archive

About the Author

Heinz Kabutz Java Conference Speaker

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Superpack '23

Superpack '23 Our entire Java Specialists Training in one huge bundle more...

Free Java Book

Dynamic Proxies in Java Book
Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

We can help make your Java application run faster and trouble-shoot concurrency and performance bugs...