2001-03-15 The Java™ Specialists' Newsletter [Issue 013] - Follow-up

Author: Dr. Heinz M. Kabutz

(C)opyright Maximum Solutions, South Africa

If you are not already subscribed to this newsletter, you can either subscribe via email or subscribe via the web. Be warned that if you are a beginner in Java, you will at times struggle to keep up.

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.


(C)opyright Maximum Solutions, South Africa

The Fine Print
Mission. This newsletter began at the end of 2000 with about 150 readers that I had gleaned from my contacts list. Since then, it has grown to its current readership exceeded 1'000 readers, necessitating a move to a "proper" mailing system. It's goal is to share some advanced ideas and thoughts on Java programming and the way that a Java specialist would deal with problems "in the field". Carl Smotricz is currently hosting an archive of past newsletters.
Reprint Rights. Copyright subsists in all the material included in this email, but you may freely share the entire email with anyone you feel may be interested, and you may reprint excerpts both online and offline provided that you acknowledge the source as follows: This material from The Java(tm) Specialists' Newsletter by Maximum Solutions (South Africa) - The Java(tm) Specialists. Please contact Dr. Heinz M. Kabutz (h.kabutz@computer.org) for more information.


Please contact me if you would like to put a link to this newsletter on your website.