Return-Path: <env_397293321935286486@hermes.sun.com>
Received: from po10.mit.edu (po10.mit.edu [18.7.21.66])
	by po10.mit.edu (Cyrus v2.1.5) with LMTP; Mon, 04 Aug 2003 21:05:24 -0400
X-Sieve: CMU Sieve 2.2
Received: from pacific-carrier-annex.mit.edu by po10.mit.edu (8.12.4/4.7) id h7515NNR007959; Mon, 4 Aug 2003 21:05:23 -0400 (EDT)
Received: from hermes.sun.com (hermes.sun.com [64.124.140.169])
	by pacific-carrier-annex.mit.edu (8.12.4/8.9.2) with SMTP id h7515MJC014000
	for <alexp@mit.edu>; Mon, 4 Aug 2003 21:05:22 -0400 (EDT)
Date: 4 Aug 2003 16:23:12 -0800
From: "SDN - Core Java Technologies Tech Tips" <sunmail@hermes.sun.com>
To: alexp@mit.edu
Message-Id: <397293321935286486@hermes.sun.com>
Subject: Core Java Technologies Tech Tips, August 5, 2003 (MIDI Sound, Saving Swing Components)
Mime-Version: 1.0
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
X-Mailer: SunMail 1.0
X-Spam-Score: 1.5
X-Spam-Level: * (1.5)
X-Spam-Flag: NO
X-Scanned-By: MIMEDefang 2.28 (www . roaringpenguin . com / mimedefang)

<!DOCtype HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Core Java Technologies Technical Tips</title>

<style type="text/css">

code {color: #333333; font-family: verdana, courier, monospace; font-size: 10pt}
pre {color: #333333; font-family: verdana, courier, monospace; font-size: 10pt}
body, div, span {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
td, th {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
tr {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
table {font-family: arial, helvetica, sans-serif; font-size: 10pt}
p {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
li {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
br {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
div {color: #666699; font-family: arial, helvetica, sans-serif; font-size: 10pt}
sup {font-family: arial, helvetica, sans-serif; font-size: 5pt}
h3 {color: #666699; font-family: arial, helvetica, sans-serif; font-size: 11pt}
h4, b {color: #666699; font-family: arial, helvetica, sans-serif; font-size: 10pt}
blockquote, input, select {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
ul, ol {color: #333333; font-family: arial, helvetica, sans-serif; font-size: 10pt}
a:link {font-size: 10pt; font-family: arial, helvetica, sans-serif; color:#666699 }
span.purple {font-weight: bold; color: #666699; font-family: arial, helvetica, sans-serif; font-size: 10pt}
span.small {font-size: 8pt; font-family: arial, helvetica, sans-serif; color:#333333 }
span.link {font-size: 8pt; font-family: arial, helvetica, sans-serif; color:#666699 }
</style>

</head>

<body bgcolor="#ffffff">
<a name="top"></a>
<table border="0" cellpadding="0" cellspacing="0" width="611">
<tr>
<td colspan="3" bgcolor="#cccccc" width="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" border="0" alt="."></td>
</tr>

<tr>
<td bgcolor="#cccccc" width="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" border="0" alt="."></td>

<td>
<table border="0" cellpadding="0" cellspacing="0" width="611">
<tr>
<td colspan="2"><a href="http://developer.java.sun.com"><img src="http://developer.java.sun.com/images/headers/core_ttips_hdr.jpg" width="611" height="160" alt="Core Java Technologies Technical Tips" border="0"></a></td>
</tr>

<tr>
<td colspan="2" bgcolor="#cccccc" width="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" border="0" alt="."></td>
</tr>

<!-- ================== -->
<!-- Start Main Content -->
<!-- ================== -->

<tr>
<td height="20">&nbsp;&nbsp;&nbsp;<a href="http://developer.java.sun.com/developer/TechTips/txtarchive/2003/Aug03_DanielS.txt" style="text-decoration:none;">View this issue as simple text</a></td>
<td align="right" height="20"><span class="purple">August 5, 2003</span>&nbsp;&nbsp;&nbsp;&nbsp;</td>
</tr>

<tr><td colspan="2">

<table border="0" cellpadding="10" cellspacing="0" width="100%">
<tr>
<td>
     
<form method="get" action="http://search.java.sun.com/search/java/">   
<table border="0" cellpadding="3" cellspacing="0" width="100%">
<tr> 
<td align="left" valign="middle" width="20%"><h3>In this Issue</h3></td>
              
<td valign="middle" align="right" width="70%">
<font size="2">
<input type="text" size="15" maxlength="128" name="qt"></font></td>

<td valign="middle" width="55">
<input type="image" src="http://developer.java.sun.com/images/v4a_search.gif" alt="Search" value="search" border="0" width="55"></td>
</tr>
</table>
</form>

<p>
Welcome to the Core Java Technologies Tech Tips, August 5, 2003. Here you'll get tips on using core Java technologies and APIs, such as those in Java 2 Platform, Standard Edition (J2SE).
</p>

<p>
This issue covers:
</p>

<p>
<a href="#1"><img src="http://developer.java.sun.com/images/anchor.gif" border="0" alt="-">Producing MIDI Sound</a><br>    
<a href="#2"><img src="http://developer.java.sun.com/images/anchor.gif" border="0" alt="-">Saving and Reconstituting Swing Components</a>
</p>

<p>
These tips were developed using Java 2 SDK, Standard Edition, v 1.4. 
</p>

<p>
This issue of the Core Java Technologies Tech Tips is written by Daniel H. Steinberg, Director of Java Offerings for Dim Sum Thinking, Inc, and editor-in-chief for <a href="http://java.net">java.net</a>.
</p>

<p>
See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms.
</p>

<table width="100%" border="0" cellspacing="0" cellpadding="0" align="center">

<!-- Grey Horizontal Line Begins Here -->

<tr>
<td bgcolor="#cccccc" height="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td>
</tr>
</table> 

<table width="100%" border="0" cellspacing="0" cellpadding="0" align="center">

<!-- Grey Horizontal Line Begins Here -->

<tr>
<td bgcolor="#cccccc" height="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td>
</tr>
</table> 

<!-- Grey Horizontal Line ends Here -->

<a name="1"></a>

<h3>PRODUCING MIDI SOUND</h3>

<p>
Adding audio cues to your Java desktop application can give it
more of a polished feel and can dramatically improve usability.
The Musical Instrument Digital Interface (MIDI) is a
communication protocol for passing musical events between
devices. A MIDI file contains audio commands rather than actual
audio. Audio is a digital representation of sound, while MIDI
represents the commands to a sound engine to recreate the sound.
In this tech tip, you'll learn three ways to generate MIDI sound
to augment your J2SE applications. Each of these techniques uses
the <code>javax.sound.midi</code> package which has been part of the Java 
platform since J2SE 1.3.
</p>

<p>
In the first technique, you generate a sound by making a direct
call to a <code>MidiChannel</code> object. This object represent a single MIDI 
channel, that is, a single channel for MIDI transmission. To
start a note playing through the channel, you use the <code>noteOn()</code>
method of the <code>MidiChannel</code> object. To stop a note playing, you use
the <code>noteOff()</code> method. 
</p>

<p>
The <code>noteOn()</code> method requires two ints. The first indicates the 
note being played. In the following program, <code>SingleNoteChannel</code>, 
the int 60 passed to the <code>noteOn()</code> method is the standard MIDI 
note number for middle C. An integer up or down corresponds to 
a half step. Twelve half steps comprise an octave.
</p>

<p>
The second parameter for the <code>noteOn()</code> method indicates the speed
with which the key is struck. Although this parameter is often
referred to as the velocity, you can think of it as a volume
control. In the <code>SingleNoteChannel</code> program, the second parameter
passed to the <code>noteOn()</code> method is 70. After you run 
<code>SingleNoteChannel</code>, experiment with other speeds by changing 70 
to other numbers. 
</p>

<p>
There are two signatures of the <code>noteOff()</code> method. One takes the 
same two parameters taken by <code>noteOn()</code>, and the other requires the 
number corresponding to the note being played.
</p>

<p>
Before you can play a note, there is a certain amount of setup to
be done. This is demonstrated in the <code>SingleNoteChannel</code> 
constructor. In order to generate sound, you need a <code>Synthesizer</code>
object. This object represents a collection of <code>MidiChannel</code>s, 
usually one for each of the 16 channels prescribed by the MIDI 
1.0 specification. The <code>SingleNoteChannel</code> constructor gets a handle 
to a <code>Synthesizer</code> using a factory method in the <code>MidiSystem</code> class.  
The constructor then calls the <code>Synthesizer</code>'s <code>open()</code> method, and 
gets an array of the available <code>MidiChannel</code>s by calling the
<code>getChannels()</code> method. It then selects the first <code>MidiChannel</code> at 
index 0.
</p>

<pre>
   import javax.sound.midi.MidiChannel;
   import javax.sound.midi.Synthesizer;
   import javax.sound.midi.MidiSystem;
   import javax.sound.midi.MidiUnavailableException;

   public class SingleNoteChannel {

      private MidiChannel channel;

      public SingleNoteChannel() {
        try {
          Synthesizer synth = 
                          MidiSystem.getSynthesizer();
          synth.open();
          channel = synth.getChannels()[0];
        } catch (MidiUnavailableException e) {
          e.printStackTrace();
        }
      }

      public void playNote(int note) {
        channel.noteOn(note, 70);
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        channel.noteOff(note, 70);
      }

      public static void main(String[] args) {
        new SingleNoteChannel().playNote(60);
      }
   }
</pre>

<p>
The <code>playNote()</code> method in the <code>SingleNoteChannel</code> program starts 
playing a note with a call to the <code>MidiChannel</code> <code>noteOn()</code> method. 
The note continues to play while the thread sleeps for one
second. Then a call to the <code>MidiChannel</code> <code>noteOff()</code> method ends the 
playing of the note.
</p>

<p>
You could use this first technique to associate certain notes 
with the pressing and releasing of buttons, keys, or other 
on-screen events. 
</p>

<p>
Now let's look at a second technique to generate MIDI sound.
In this approach, you use a <code>Receiver</code> object associated with 
a <code>Synthesizer</code>. Instead of getting a specific <code>MidiChannel</code> for a 
<code>Synthesizer</code>, you get a handle to a <code>Receiver</code>. You create MIDI 
messages of type <code>ShortMessage</code>, customize them, and then play 
them by calling the <code>send()</code> method of the <code>Receiver</code> object. 
</p>

<p>
A <code>ShortMessage</code> object contains a MIDI message that has at most 
two data bytes. You use the <code>setMessage()</code> method to set the
parameters of the MIDI message or to set message parameters for 
a channel message (this depends on the signature of the method)
In the program below, <code>SingleNoteSynthesizer</code>, the signature of the 
<code>setMessage()</code> method sets message parameters for a channel message.
This signature takes four ints. The first indicates the command 
being sent. The choices are <code>NOTE_ON</code> and <code>NOTE_OFF</code>. The second int 
identifies the target channel. To be consistent with the previous 
example, this parameter specifies index 0. For the <code>NOTE_ON</code> and 
<code>NOTE_OFF</code> commands the last two ints are the note identifier and 
velocity. In <code>SingleNoteSynthesizer</code> they are the same ints that 
were passed into the <code>noteOn()</code> and <code>noteOff()</code> methods in the 
<code>SingleNoteChannel</code> example.
</p>

<pre>
   import javax.sound.midi.ShortMessage;
   import javax.sound.midi.InvalidMidiDataException;
   import javax.sound.midi.Receiver;
   import javax.sound.midi.Synthesizer;
   import javax.sound.midi.MidiSystem;
   import javax.sound.midi.MidiUnavailableException;

   public class SingleNoteSynthesizer {

      private ShortMessage message = 
                                    new ShortMessage();
      private Receiver receiver;

      private SingleNoteSynthesizer() {
        try {
          Synthesizer synth = 
                           MidiSystem.getSynthesizer();
          synth.open();
          receiver = synth.getReceiver();
        } catch (MidiUnavailableException e) {
          e.printStackTrace();
        }
      }
  
      public void playNote(int note) {
        setShortMessage(note, ShortMessage.NOTE_ON);
        receiver.send(message, -1);
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
       e.printStackTrace();
        }
        setShortMessage(note, ShortMessage.NOTE_OFF);
        receiver.send(message, -1);
      }

      private void setShortMessage(
                               int note, int onOrOff) {
        try {
          message.setMessage(onOrOff, 0, note, 70);
        } catch (InvalidMidiDataException e) {
          e.printStackTrace();
        }
      }

      public static void main(String[] args) {
        new SingleNoteSynthesizer().playNote(60);
      }
   }
</pre>

<p>
So far you've seen examples that play a single note. One thing
you might not have realized is that the examples played the note
on a default instrument. But what about playing a note on a 
different instrument? In this third technique for generating MIDI
sound you'll see how to do this. Let's start by examining what's 
available. Here's a method that generates a list of available 
instruments:
</p>

<pre>
   public void listAvailableInstruments(){
      Instrument[] instrument =
                     synth.getAvailableInstruments();
      for (int i=0; i&lt;instrument.length; i++){
        System.out.println(i + &quot;   &quot;
                          + instrument[i].getName());
      }
   }
</pre>

<p>
If you call the method, you get a list whose first items look like this:
</p>

<pre>
0   Piano
1   Bright Piano
2   Electric Grand
3   Honky Tonk Piano
4   Electric Piano 1
5   Electric Piano 2
6   Harpsichord
7   Clavinet
8   Celesta
9   Glockenspiel
10   Music Box
11   Vibraphone
12   Marimba
13   Xylophone
14   Tubular Bell
15   Dulcimer
16   Hammond Organ
17   Perc Organ
18   Rock Organ
19   Church Organ
20   Reed Organ
21   Accordion
22   Harmonica
23   Tango Accordion
24   Nylon Str Guitar
25   Steel String Guitar
26   Jazz Electric Gtr
27   Clean Guitar
28   Muted Guitar
29   Overdrive Guitar
30   Distortion Guitar
</pre>

<p>
To illustrate the approach, let's enhance <code>SingleNoteSynthesizer</code> 
to play more than one note at a time using different instruments. 
If you examine the enhanced program, <code>SingleNoteSynthesizer</code>2, you
will notice that the program still sleeps a <code>Thread</code> to measure 
time. This is really not robust enough for creating and playing 
music. In addition, if done in the AWT Event Dispatch thread, 
this will prevent your GUI from processing events or repainting. 
These issues will be addressed later in the tip using the final
technique for generating MIDI sound.
</p>

<p>
As demonstrated in <code>SingleNoteSynthesizer</code>2, you change the 
instrument being played by calling the <code>programChange()</code> method in 
<code>MidiChannel</code>. You pass the method an int that corresponds to the 
instrument you want from the list you generated above. In this 
example, the int is 19, corresponding to a church organ.
</p>

<p>
Notice too that the code has also been refactored so that 
<code>startNote()</code> creates a message with the <code>NOTE_ON</code> command, and
<code>stopNote()</code> creates one with the <code>NOTE_OFF</code> command. The <code>playNote()</code> 
method calls <code>startNote()</code>, sleeps for the number of milliseconds 
passed in as the duration, and then calls <code>stopNote()</code>.
</p>

<pre>
   import javax.sound.midi.ShortMessage;
   import javax.sound.midi.Synthesizer;
   import javax.sound.midi.Receiver;
   import javax.sound.midi.MidiSystem;
   import javax.sound.midi.MidiUnavailableException;
   import javax.sound.midi.InvalidMidiDataException;

   public class SingleNoteSynthesizer2 {
      private ShortMessage message = new ShortMessage();
      private Synthesizer synth;
      private Receiver receiver;

      public SingleNoteSynthesizer2() {
        try {
          synth = MidiSystem.getSynthesizer();
          synth.open();
          receiver = synth.getReceiver();
        } catch (MidiUnavailableException e) {
          e.printStackTrace();
        }
      }

      public void startNote(int note) {
        setShortMessage(ShortMessage.NOTE_ON, note);
        receiver.send(message, -1);
      }

      public void stopNote(int note) {
        setShortMessage(ShortMessage.NOTE_OFF, note);
        receiver.send(message, -1);
      }

      public void playNote(int note, int duration){
        startNote(note);
        try {
          Thread.sleep(duration);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        stopNote(note);
      }

      public void setInstrument(int instrument){
        synth.getChannels()[0].programChange(
                                           instrument);
      }

      private void setShortMessage(
                               int onOrOff, int note) {
        try {
          message.setMessage(onOrOff, 0, note, 70);
        } catch (InvalidMidiDataException e) {
          e.printStackTrace();
        }
      }

      public void playMajorChord(int baseNote){
        playNote(baseNote,1000);
        playNote(baseNote+4,1000);
        playNote(baseNote+7, 1000);
        startNote(baseNote);
        startNote(baseNote+4);
        playNote(baseNote+7,2000);
        stopNote(baseNote+4);
        stopNote(baseNote);
      }

      public static void main(String[] args) {
        SingleNoteSynthesizer2 synth
                        = new SingleNoteSynthesizer2();
        synth.setInstrument(19);
        synth.playMajorChord(60);
      }
   }
</pre>

<p>
A major chord is constructed from a base note, the note four half
steps above it, and the note three half spaces above that. In 
<code>SingleNoteSynthesizer</code>2, the <code>playMajorChord()</code> method first plays 
these notes, one at a time, and then plays them all at once. The 
timing is done using <code>Thread</code>s. This is not the recommended approach 
when timing is important. In that case you should use a <code>Sequencer</code>.
</p>

<p>
A <code>Sequencer</code> object includes methods that give you more control
(as compared to the previous approach) over the timing of MIDI
events. As before, use a factory method from <code>MidiSystem</code>. This
time obtain and open a <code>Sequencer</code>. This is shown in the next 
example, <code>SequencerSound</code>. Comparing this example to the preceding
examples, you should notice some notable changes to the code. 
First the <code>startNote()</code> and <code>stopNote()</code> methods take an int that 
represents the point in time at which the message being created 
takes place. The <code>createTrack()</code> method creates a <code>Sequence</code> with four 
ticks for each quarter note. You can think of ticks as 
subdivisions. In other words, a measure of music in 4/4 time has 
four quarter notes or sixteen ticks.
</p>

<p>
The <code>setShortMessage()</code> method calls <code>setMessage()</code>, as before. 
Then the program creates a <code>MidiEvent</code> object based on this
<code>ShortMessage</code> and associated with a specific tick. Finally the
program adds a <code>MidiEvent</code> to the <code>Track</code>. To play the track, the
<code>startSequencer()</code> method assigns the newly created <code>Sequence</code> to the
<code>Sequencer</code>. The playback begins with a call to the <code>start()</code> method,
and the tempo is set to sixty beats per minute (BPM) using the
<code>setTempoInBPM()</code> method.
</p>

<pre>
   import javax.sound.midi.Track;
   import javax.sound.midi.Sequencer;
   import javax.sound.midi.Sequence;
   import javax.sound.midi.MidiSystem;
   import javax.sound.midi.MidiUnavailableException;
   import javax.sound.midi.InvalidMidiDataException;
   import javax.sound.midi.ShortMessage;
   import javax.sound.midi.MidiEvent;

   public class SequencerSound {
      private Track track;
      private Sequencer sequencer;
      private Sequence sequence;

      public SequencerSound() {
        try {
          sequencer = MidiSystem.getSequencer();
          sequencer.open();
        } catch (MidiUnavailableException e) {
          e.printStackTrace();
        }
        createTrack();
        makeScale(20);
        startSequencer();
      }

      private void startSequencer() {
        try {
          sequencer.setSequence(sequence);
        } catch (InvalidMidiDataException e) {
          e.printStackTrace();
        }
        sequencer.start();
        sequencer.setTempoInBPM(60);
      }

      private void createTrack() {
        try {
          sequence = new Sequence(Sequence.PPQ, 4);
        } catch (InvalidMidiDataException e) {
          e.printStackTrace();
        }
        track = sequence.createTrack();
      }

      public void startNote(int note, int tick) {
        setShortMessage(
                     ShortMessage.NOTE_ON, note, tick);
      }

      public void stopNote(int note, int tick) {
        setShortMessage(
                    ShortMessage.NOTE_OFF, note, tick);
      }

      private void setShortMessage(
                    int onOrOff, int note, int tick) {
        ShortMessage message = new ShortMessage();
        try {
          message.setMessage(onOrOff, 0, note, 90);
          MidiEvent event = new MidiEvent(
                                       message, tick);
          track.add(event);
        } catch (InvalidMidiDataException e) {
          e.printStackTrace();
        }
      }

      public void makeScale(int baseNote) {
        for (int i = 0; i &lt; 13; i++) {
          startNote(baseNote + i, i);
          stopNote(baseNote + i, i + 1);
          startNote(baseNote + i, 25 - i);
          stopNote(baseNote + i, 26 - i);
        }
      }

      public static void main(String[] args) {
        new SequencerSound();
      }
   }
</pre>

<p>   
This tip showed you how to produce and play back MIDI files. As a  
developer, you are able to hear the music because a soundbank is  
automatically installed along with the J2SE SDK. A soundbank 
represents a set of instruments, and is needed to synthesize 
sound. (There's a lot more to the soundbank concept, and it will
be covered in a later tip.) Non-developers might have to install 
a soundbank separately to hear what you have created. When 
installing a JRE for J2SE 1.4.1 or later, users have to opt-in to  
include the soundbank in the installation. 
</p>

<p>
For more information on saving and playing back MIDI files,
consult the <a href="http://java.sun.com/j2se/1.4.2/docs/api/javax/sound/midi/package-summary.html">documentation for <code>javax.sound.midi</code></a>.
You'll find more information on the <a href="http://java.sun.com/products/java-media/sound/">Java Sound API home page</a>.
</p>

<table width="100%" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
<td>
<div align="right">
<a href="#top">back to top<img src="http://developer.java.sun.com/images/back_to_top.gif" border="0" alt="image"></a>
</div>
</td>
</tr>

<tr>
<td bgcolor="#ffffff" height="2"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td>
</tr>

<!-- Grey Horizontal Line Begins Here -->

<tr>
<td bgcolor="#cccccc" height="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td>
</tr>
</table> 

<!-- Grey Horizontal Line ends Here -->

<a name="2"></a>

<h3>SAVING AND RECONSTITUTING SWING COMPONENTS</h3>

<p>
Before the J2SE 1.4 release, all Swing components have included
the advisory that &quot;A future release of Swing will provide support
for long term persistence.&quot; This has been replaced with the
notice that &quot;As of 1.4, support for long term storage of all
JavaBeans has been added to the java.beans package.&quot;
</p>

<p>
The <code>XMLEncoder</code> class in the <code>java.beans</code> package allows you to
persist a JavaBean in an XML file. Among other things, this
provides a way to save Swing components. After you save a Swing
component, you can reconstitute the component with the <code><code>XMLDecoder</code></code>
class in the the <code>java.beans</code> package.
</p>

<p>
The following program, <code>FrameCreator</code>, creates and customizes 
a <code>JFrame</code>. The <code>main()</code> method in the program instantiates an 
<code>XMLEncoder</code> object, and then uses it to store an XML 
representation of the <code>JFrame</code> in the file <code>Frame.xml</code>.
</p>

<pre>
   import javax.swing.*;
   import java.awt.*;
   import java.awt.event.ActionListener;
   import java.beans.XMLEncoder;
   import java.beans.EventHandler;
   import java.io.BufferedOutputStream;
   import java.io.FileOutputStream;

   public class FrameCreator {
      public static JFrame createFrame() {
        JFrame jFrame = new JFrame();
        jFrame.setTitle(&quot;My Frame&quot;);
        jFrame.setSize(200, 100);
        JPanel jPanel = new JPanel();
        jFrame.getContentPane().add(jPanel);
        Controller controller = new Controller();
        JButton button1 = new JButton(&quot;Hello, world&quot;);
        button1.addActionListener(
          (ActionListener) EventHandler.create(
            ActionListener.class, controller,
            &quot;helloWorld&quot;));
        JButton button2 = new JButton(
            &quot;Goodbye, cruel world&quot;);
        button2.setBackground(Color.RED);
        button2.addActionListener(
          (ActionListener) EventHandler.create(
            ActionListener.class, controller,
            &quot;exit&quot;));
        jPanel.add(button1);
        jPanel.add(button2);
        jFrame.setVisible(true);
        return jFrame;
      }

      public static void main(String[] args)
                                    throws Exception {
        XMLEncoder encoder = new XMLEncoder(
            new BufferedOutputStream(
                new FileOutputStream(&quot;Frame.xml&quot;)));
        encoder.writeObject(createFrame());
        encoder.close();
      }
   }
</pre>

<p>
Note that <code>FrameCreator</code> calls some methods defined in a non-GUI
class called <code>Controller.java</code>. Here's the <code>Controller</code> class:
</p>

<pre>
   public class Controller {
        public void helloWorld() {
            System.out.println(&quot;Hello, world&quot;);
        }

        public void exit() {
            System.exit(0);
        }
   }
</pre>

<p>
Here is the contents of <code>Frame.xml</code>. You can easily identify the
properties that were set in the <code>FrameCreator</code> class. Notice that
the dimensions of the <code>JFrame</code> are saved. Also saved in the file is
information about the <code>JPanel</code> stored inside the <code>JFrame</code>, and the
<code>JPanel</code>'s constituent components: two <code>JButtons</code> linked to an
instance of the <code>Controller</code> class.
</p>

<pre>
   &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
   &lt;java version=&quot;1.4.1_01&quot; class=&quot;java.beans.XMLDecoder&quot;&gt;
     &lt;object class=&quot;javax.swing.JFrame&quot;&gt;
      &lt;void property=&quot;size&quot;&gt;
       &lt;object class=&quot;java.awt.Dimension&quot;&gt;
        &lt;int&gt;200&lt;/int&gt;
        &lt;int&gt;100&lt;/int&gt;
       &lt;/object&gt;
      &lt;/void&gt;
      &lt;void property=&quot;contentPane&quot;&gt;
       &lt;void method=&quot;add&quot;&gt;
        &lt;object class=&quot;javax.swing.JPanel&quot;&gt;
         &lt;void method=&quot;add&quot;&gt;
          &lt;object class=&quot;javax.swing.JButton&quot;&gt;
           &lt;string&gt;Hello, world&lt;/string&gt;
           &lt;void method=&quot;addActionListener&quot;&gt;
            &lt;object class=&quot;java.beans.EventHandler&quot;
                    method=&quot;create&quot;&gt;
             &lt;class&gt;java.awt.event.ActionListener&lt;/class&gt;
             &lt;object id=&quot;Controller0&quot; class=&quot;Controller&quot;/&gt;
             &lt;string&gt;helloWorld&lt;/string&gt;
            &lt;/object&gt;
           &lt;/void&gt;
          &lt;/object&gt;
         &lt;/void&gt;
         &lt;void method=&quot;add&quot;&gt;
          &lt;object class=&quot;javax.swing.JButton&quot;&gt;
           &lt;string&gt;Goodbye, cruel world&lt;/string&gt;
           &lt;void property=&quot;background&quot;&gt;
            &lt;object class=&quot;java.awt.Color&quot;&gt;
             &lt;int&gt;255&lt;/int&gt;
             &lt;int&gt;0&lt;/int&gt;
             &lt;int&gt;0&lt;/int&gt;
             &lt;int&gt;255&lt;/int&gt;
            &lt;/object&gt;
           &lt;/void&gt;
           &lt;void method=&quot;addActionListener&quot;&gt;
            &lt;object class=&quot;java.beans.EventHandler&quot;
                    method=&quot;create&quot;&gt;
             &lt;class&gt;java.awt.event.ActionListener&lt;/class&gt;
             &lt;object idref=&quot;Controller0&quot;/&gt;
             &lt;string&gt;exit&lt;/string&gt;
            &lt;/object&gt;
           &lt;/void&gt;
          &lt;/object&gt;
         &lt;/void&gt;
        &lt;/object&gt;
       &lt;/void&gt;
      &lt;/void&gt;
      &lt;void property=&quot;name&quot;&gt;
       &lt;string&gt;frame0&lt;/string&gt;
      &lt;/void&gt;
      &lt;void property=&quot;title&quot;&gt;
       &lt;string&gt;My Frame&lt;/string&gt;
      &lt;/void&gt;
      &lt;void property=&quot;visible&quot;&gt;
       &lt;boolean&gt;true&lt;/boolean&gt;
      &lt;/void&gt;
     &lt;/object&gt;
   &lt;/java&gt;
</pre>

<p>
It takes surprisingly little to reconstitute the configured
<code>JFrame</code> from the file <code>Frame.xml</code>. The following program,
<code>FrameRecreator</code>, instantiates an <code>XMLDecoder</code> that reads from the 
file <code>Frame.xml</code>. When you run <code>FrameRecreator</code>, you should see the 
<code>JFrame</code> that was created and configured just as it was in 
<code>FrameCreator</code>.
</p>

<pre>
   import java.beans.XMLDecoder;
   import java.io.BufferedInputStream;
   import java.io.FileInputStream;

   public class FrameRecreator {

      public static void main(String[] args)
                                    throws Exception {
        XMLDecoder decoder = new XMLDecoder(
            new BufferedInputStream(
                new FileInputStream(&quot;Frame.xml&quot;)));
        decoder.readObject();
        decoder.close();
      }
   }
</pre>

<img src="http://developer.java.sun.com/developer/JDCTechTips/images/tt080503_FrameRecreator.gif" width="200" height="100" border="0" alt="Frame Recreator">

<p>
You've seen how to use the classes <code>XMLEncoder</code> and <code>XMLDecoder</code> to 
save and restore JavaBeans. However, <code>XMLEncoder</code> and <code>XMLDecoder</code> 
are much more general than this example implies. <code>XMLEncoder</code> is 
preconfigured to save all primitive data types, dates, strings, 
arrays, lists, hashmaps, and many other classes in the J2SE SDK 
that are not JavaBeans. You can also configure the <code>XMEncoder</code> to 
save your own Java classes, even if they aren't JavaBeans. For 
details on how to do this, and for information about other 
features of these technologies, see the following articles in
the Swing Connection:
</p>

<ul>
<li><a href="http://java.sun.com/products/jfc/tsc/articles/persistence4">Using <code>XMLEncoder</code></a></li>
<li><a href="http://java.sun.com/products/jfc/tsc/articles/persistence3">Long Term Persistence of JavaBeans Components: XML Schema</a></li>
</ul>
 
<p>
You can add <code>XMLEncoder</code> to your set of persistence techniques,
such as the Preferences API discussed in the tip titled &quot;Using
the Preferences API&quot; in the <a href="http://java.sun.com/jdc/JDCTechTips/2003/tt0715.html">July 15 issue</a> of the Core Java
Technologies Tech Tips.
</p>

<table width="100%" border="0" cellspacing="0" cellpadding="0" align="center">

<tr>
<td>
<div align="right">
<a href="#top">back to top<img src="http://developer.java.sun.com/images/back_to_top.gif" border="0" alt="image"></a>
</div>
</td></tr>

<tr><td bgcolor="#ffffff" height="2"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td></tr>

<!-- Grey Horizontal Line Begins Here -->

<tr>
<td bgcolor="#cccccc" height="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td>
</tr>
</table> 

<!-- Grey Horizontal Line ends Here -->

<form method="POST" action="http://developer.java.sun.com/servlet/jdc.survey.TabulationServlet">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr><td bgcolor="#ffffff" height="2"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td></tr>

<tr><td>
<h3>Reader Feedback</h3> 

<input type="hidden" name="survey_id" value="2600">
<input type="hidden" name="anonymous" value="True">
<input type="hidden" name="answer_count" value="3">
<input type="hidden" name="answer1" value="AugCore_tt080403">
<input name="answer2" type="radio" value="2">&nbsp; Very worth reading&nbsp; 
<input name="answer2" type="radio" value="1">&nbsp; Worth reading&nbsp; 
<input name="answer2" type="radio" value="0">&nbsp; Not worth reading&nbsp; 
 
<p>
If you have other comments or ideas for future technical tips, please type them  here: 
</p>
 
<p>
<textarea name="answer3" rows="6" cols="50"></textarea> 
</p>
 
<p>
<input type="Submit" value="Submit"> &nbsp; <input type="Reset"> 
</p>

<p> 
Have a question about Java programming? Use  
<a href="http://developer.java.sun.com/developer/support/">Java Online Support</a>. 
</p>

</td></tr></table>
</form> 

<table width="100%" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
<td>
<div align="right">
<a href="#top">back to top<img src="http://developer.java.sun.com/images/back_to_top.gif" border="0" alt="image"></a>
</div>
</td></tr>

<tr><td bgcolor="#ffffff" height="2"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td></tr>

<!-- Grey Horizontal Line Begins Here -->

<tr>
<td bgcolor="#cccccc" height="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" alt="." border="0"></td>
</tr>
</table> 

<!-- Grey Horizontal Line ends Here -->

<br>

<span class="small">IMPORTANT: Please read our Terms of Use, Privacy, and Licensing policies:<br>
<a href="http://www.sun.com/share/text/termsofuse.html"><span class="link">http://www.sun.com/share/text/termsofuse.html</span></a><br>
<a href="http://www.sun.com/privacy/"><span class="link">http://www.sun.com/privacy/</span></a><br> 
<a href="http://developer.java.sun.com/berkeley_license.html"><span class="link">http://developer.java.sun.com/berkeley_license.html</span></a>
</span><br><br>


<span class="small">
Comments? Send your feedback on the Core Java Technologies Tech Tips to: <a href="http://developers.sun.com/contact/feedback.jsp?category=sdn"><span class="link">http://developers.sun.com/contact/feedback.jsp?category=sdn</span></a>
</span><br><br>

<span class="small">
Subscribe to other Java developer Tech Tips:
</span><br><br>

<span class="small">
- Enterprise Java Technologies Tech Tips. Get tips on using enterprise Java technologies and APIs, such as those in the Java 2 Platform, Enterprise Edition (J2EE).<br>
- Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME).<br><br>
</span>
   
<span class="small">
To subscribe to these and other JDC publications:<br>
- Go to the JDC Newsletters and Publications page, choose the newsletters you want to <a href="http://developer.java.sun.com/subscription/"><span class="link">subscribe</span></a> to and click &quot;Update&quot;.<br>
  - To unsubscribe, go to the <a href="http://developer.java.sun.com/subscription/"><span class="link">subscriptions page</span></a>, uncheck the appropriate checkbox, and click &quot;Update&quot;.
</span><br><br>

<span class="small">ARCHIVES: You'll find the Core Java Technologies Tech Tips archives at:<br>
<a href="http://java.sun.com/jdc/TechTips/"><span class="link">http://java.sun.com/jdc/TechTips/index.html</span></a>
</span><br><br>

<span class="small">Copyright 2003 <a href="http://www.sun.com"><span class="link">Sun Microsystems, Inc.</span></a> All rights reserved.
<br>4150 Network Circle, Santa Clara, CA 95054 USA.
</span><br><br>

<span class="small">This document is protected by copyright. For more information, see:
<br><a href="http://java.sun.com/jdc/copyright.html"><span class="link">http://java.sun.com/jdc/copyright.html</span></a>
</span><br><br>

<span class="small">Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks or registered trademarks (<a href="http://www.sun.com/suntrademarks/"><span class="link">http://www.sun.com/suntrademarks/</span></a>) of Sun Microsystems, Inc. in the United States and other countries.</span>
<br><br>

<!-- ================ -->
<!-- End Main Content -->
<!-- ================ -->

<center>
<a href="http://www.sun.com"><img src="http://developer.java.sun.com/images/lgsun.gif" border="0" alt="Sun Microsystems, Inc."></a>
</center>

</td>
</tr>
</table>

</td></tr>
</table>
</td>

<td bgcolor="#cccccc" width="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" border="0" alt="."></td>
</tr>
<tr>
<td colspan="3" bgcolor="#cccccc" width="1"><img src="http://developer.java.sun.com/images/pixel.gif" width="1" height="1" border="0" alt="."></td>
</tr>
</table>

<br><br><table bgcolor = "#efefef"><tr><td><a href="http://subscriptions.sun.com/unsubscribe?397293321935286486">Please unsubscribe me from this newsletter.</a></td></tr></table><br><br><img src="http://bulkmail2.sun.com/OTServlet?id=397293321935286486" width=1 height=1></body>
</html>


