Bluemini.comBluemini.com

Java from Sun

Superduper Slow Jar Command

posted: 30 Sep 2009

By Xueming Shen

It's well known that creating a Jar file can be a "little" slow. How slow? On my aged SunBlad1000, it takes about 1 minute and 40 seconds to jar the whole rt.jar in cf0M mode (no compress, no manifest) -- and it costs you a little more if done in compress mode.

But then we figured we were talking about creating jars for ten of thousands of classes with a total size of over 50M. Given the number of files and the total size, it seemed a reasonable amount of time. So, until now, we assumed it really needed that time — until someone accidentally noticed that "the CPU went to 100% busy for quite some time, perhaps a minute or more on my laptop, before starting to hit the disk to create the Jar archive."

That sounds strange, as the main job the Jar is supposed to do is to copy and compress the files (into the Jar). Thus it should hit the disk from the very beginning to the end.

So I peeked into the Jar source code (after many years), and it turned out we had a very embarassing bug in the jar code: We were doing a O(n) look-up on a Hashtable (via the contains() method) for each and every file we were jarring, where it really should be a O(1) look-up operation with a HashSet. Given the number of files the command is working on, this simple mistake caused us to spend the majority of the time (that 1 min 40+ seconds) in collecting the list of files that need to jar, instead of the real "jarring" work. Sigh:-(

With that fixed (in JDK 7 build44 and later), the Jar is now much faster.

Following are the quick time-measure numbers of 10 runs of jarring/zipping the rt.jar/zip, in Store Only mode and Zip Compression mode.

  • b43: the JDK 7/build43, which does not have the fix.
  • b47: the JDK 7/build47, which does have the fix.
  • zip: the default zip installed on my Solaris, which is zip2.3/1999

jar cf0M / zip -r0q (store, no zip compression)
Build 43 Build 471 Zip
1:43.7 20.6 10.2
1:40.3 20.2 9.2
1:40.1 21.0 9.0
1:40.5 19.6 10.4
1:40.9 19.6 8.7
1:40.2 19.6 9.1
1:40.0 18.6 10.0
1:39.1 20.0 8.6
1:41.3 18.5 9.0
1:42.1 19.6 9.6
jar cfM/zip -rq (with zip compression)
Build 43 Build 471
Zip
1:47.0 25.3 15.7
1:45.9 23.4 14.2
1:44.7 23.3 14.9
1:45.4 23.7 14.3
1:45.6 23.3 14.3
1:44.9 23.6 14.0
1:45.9 23.2 14.6
1:44.0 23.0 14.2
1:44.9 23.3 14.8
1:45.8 23.5 14.2
1 The fix is in JDK 7 only, for now.

This page contains details of the fix.

We are making much progress on the Jar tool, and it is performing much better, though it is still slower compared to the Zip command. So we will continue our efforts going forward. I have to admit I do have some code that make Jar processing time much closer to Zip, but it will take time to make it into the product. Stay tuned!

Xueming Shen is an engineer at Sun Microsystems, working in the Java core technologies group.

Overhauling the Java UTF-8 charset

posted: 30 Sep 2009

By Xueming Shen

The UTF-8 charset implementation, which is available in all JDK/JRE releases from Sun, has been updated recently to reject non-shortest-form UTF-8 byte sequences. This is because the old implementation might be leveraged in security attacks. Since then I have been asked many times about what this "non-shortest-form" issue is and what the possible impact might be, so here are some answers.

The first question usually goes: "What is the non-shortest-form issue"?

The detailed and official answer is at Unicode Corrigendum #1: UTF-8 Shortest Form. Simply put, the problem is that Unicode characters can be represented in more than one way (form) in the "UTF-8 encoding" than many people think or believe. When asked what UTF-8 encoding looks like, the simplest explanation would be the following bit pattern:

# Bits Bit pattern
1 7 0xxxxxxx      
2 11 110xxxxx 10xxxxxx    
3 16 1110xxxx 10xxxxxx 10xxxxxx  
4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

The pattern is close, but it's actually wrong, based on the latest definition of UTF-8. The preceding pattern has a loophole in that you can actually have more than one form represent a Unicode character.

For ASCII characters from u+0000 to u+007f, for example, the UTF-8 encoding form maintains transparency for all of them, so they keep their ASCII code values of 0x00..0x7f (in one-byte form) in UTF-8. Based on the preceding pattern, however, these characters can also be represented in 2-bytes form as [c0, 80]..[c1, bf], the "non-shortest-form".

The following code shows all of the non-shortest-2-bytes-form for these ASCII characters, if you run code against the "old" version of the JDK and JRE (Java Runtime Environment).

byte[] bb = new byte[2]; for (int b1 = 0xc0; b1 < 0xc2; b1++) { for (int b2 = 0x80; b2 < 0xc0; b2++) { bb[0] = (byte)b1; bb[1] = (byte)b2; String cstr = new String(bb, "UTF8"); char c = cstr.toCharArray()[0]; System.out.printf("[%02x, %02x] -> U+%04x [%s]%n", b1, b2, c & 0xffff, (c>=0x20)?cstr:"ctrl"); } }

The output would be as follows:

... [c0, a0] -> U+0020 [ ] [c0, a1] -> U+0021 [!] ... [c0, b6] -> U+0036 [6] [c0, b7] -> U+0037 [7] [c0, b8] -> U+0038 [8] [c0, b9] -> U+0039 [9] ... [c1, 80] -> U+0040 [@] [c1, 81] -> U+0041 [A] [c1, 82] -> U+0042 [B] [c1, 83] -> U+0043 [C] [c1, 84] -> U+0044 [D] ...

So, for a string like "ABC", you would have two forms of UTF-8 sequences:

"0x41 0x42 0x43" and "0xc1 0x81 0xc1 0x82 0xc1 0x83"

The Unicode Corrigendum #1: UTF-8 Shortest Form specifies explicitly that "The definition of each UTF specifies the illegal code unit sequences in that UTF. For example, the definition of UTF-8 (D36) specifies that code unit sequences such as [C0, AF] are illegal."

Our old implementation accepts those non-shortest-form (while it never generates them when encoding). The new UTF_8 charset now rejects the non-shortest-form byte sequences for all BMP characters. Only the "legal byte sequences" listed below are accepted.

/* Legal UTF-8 Byte Sequences * * # Code Points Bits Bit/Byte pattern * 1 7 0xxxxxxx * U+0000..U+007F 00..7F * 2 11 110xxxxx 10xxxxxx * U+0080..U+07FF C2..DF 80..BF * 3 16 1110xxxx 10xxxxxx 10xxxxxx * U+0800..U+0FFF E0 A0..BF 80..BF * U+1000..U+FFFF E1..EF 80..BF 80..BF * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx * U+10000..U+3FFFF F0 90..BF 80..BF 80..BF * U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF * U+100000..U10FFFF F4 80..8F 80..BF 80..BF */

The next question usually is: "What would be the issue/problem if we keep using the old version of JDK/JRE?"

First, I'm not a lawyer — oops, I meant I'm not a security expert:-) — so my word does not count. We consulted with our security experts instead. Their conclusion is that while "it is not a security vulnerability in Java SE per se, it may be leveraged to attack systems running software that relies on the UTF-8 charset to reject these non-shortest form of UTF-8 sequences".

A simple scenario that might give you an idea about what the above "may be leveraged to attack..." really means:

  1. A Java application would like to filter the incoming UTF-8 input stream to reject certain key words, for example "ABC".
  2. Instead of decoding the input UTF-8 byte sequences into Java char representation and then filter out the keyword string "ABC" at Java "char" level, for example:

    String utfStr = new String(bytes, "UTF-8"); if ("ABC".equals(strUTF)) { ... }

    The application might choose to filter the raw UTF-8 byte sequences "0x41 0x42 0x43" (only) directly against the UTF-8 byte input stream and then rely on (assume) the Java UTF-8 charset to reject any other non-shortest-form of the target keyword, if there is any.

  3. The consequence is the non-shortest form input "0xc1 0x81 0xc1 0x82 0xc1 0x83" will penetrate the filter and trigger a possible security vulnerability, if the underlying JDK/JRE runtime is an OLD version.

So the recommendation is: Update to the latest JDK/JRE releases to avoid the potential risk.

Wait, there is another big bonus for updating: performance.

The UTF-8 charset implementation has not been updated or touched for years. UTF-8 encoding is very widely used as the default encoding for XML, and more and more websites use UTF-8 as their page encoding. Given that fact, we have taken the defensive position of "don't change it if it works" during the past years.

So Martin and I decided to take this opportunity to give it a speed boost as well. The following data is from one of my benchmark's run data, which compares the decoding/encoding operations of new implementation and old implementation under -server vm. (This is not an official benchmark: it is provided only to give a rough idea of the performance boost.)

The new implementation is much faster, especially when decoding or encoding single bytes (those ASCIIs). The new decoding and encoding are faster under -client vm as well, but the gap is not as big as in -server vm. I wanted to show you the best:-)

Method Millis Millis(OLD) Decoding 1b UTF-8 : 1786 12689 Decoding 2b UTF-8 : 21061 30769 Decoding 3b UTF-8 : 23412 44256 Decoding 4b UTF-8 : 30732 35909 Decoding 1b (direct)UTF-8 : 16015 22352 Decoding 2b (direct)UTF-8 : 63813 82686 Decoding 3b (direct)UTF-8 : 89999 111579 Decoding 4b (direct)UTF-8 : 73126 60366 Encoding 1b UTF-8 : 2528 12713 Encoding 2b UTF-8 : 14372 33246 Encoding 3b UTF-8 : 25734 26000 Encoding 4b UTF-8 : 23293 31629 Encoding 1b (direct)UTF-8 : 18776 19883 Encoding 2b (direct)UTF-8 : 50309 59327 Encoding 3b (direct)UTF-8 : 77006 74286 Encoding 4b (direct)UTF-8 : 61626 66517

The new UTF-8 charset implementation has been integrated in JDK7, Open JDK 6, JDK 6 update 11 and later, JDK5.0u17, and 1.4.2_19.

If you are interested in what the change looks like, you can take a peek at the webrev of the new UTF_8.java for OpenJDK7.

Xueming Shen is an engineer at Sun Microsystems, working in the Java core technologies group.

Closing a URLClassLoader

posted: 30 Sep 2009

By Michael McMahon

Complex Java programs, such as application servers, sometimes create their own class loaders using the URLClassLoader type. With URLClassLoader, applications can load classes and resources from a search path of URLs. The following URL types are supported:

  • file: (loads from file-system directories)
  • jar: (loads from JAR files)
  • http: (loads from http servers)

A frequent problem has been how to support updated implementations of the classes and resources loaded from a particular codebase, and in particular from JAR files. In principle, once the application clears all references to a loader object, the garbage collector and finalization mechanisms will eventually ensure that all resources (such as the JarFile objects) are released and closed.

The application can then replace the JAR file, and create a new URLClassLoader instance to load from the same location, but this time using the new implementation of the classes/resources.

However, since it can't be predicted exactly when finalization and garbage collection will occur, this causes problems for applications which need to be able to do this in a predictable and timely fashion. It is a particular problem on Windows, because open files cannot be deleted or replaced.

To alleviate this problem, URLClassLoader has acquired a new method called close(). It has been implemented since Build 48 of JDK7.

The close() method effectively invalidates the loader, so that no new classes can be loaded from it. It also closes any JAR files that were opened by the loader. This allows the application to delete or replace these files and, if necessary, create new loaders using new implementations.

The new method follows the familiar "Closeable" pattern, and URLClassLoader now implements the Closeable interface, which defines URLClassLoader.close(). The following sample code shows how one might use the method.

// // create a class loader loading from "foo.jar" // URL url = new URL("file:foo.jar"); URLClassLoader loader = new URLClassLoader (new URL[] {url}); Class cl = Class.forName ("Foo", true, loader); Runnable foo = (Runnable) cl.newInstance(); foo.run(); loader.close (); // foo.jar gets updated somehow loader = new URLClassLoader (new URL[] {url}); cl = Class.forName ("Foo", true, loader); foo = (Runnable) cl.newInstance(); // run the new implementation of Foo foo.run();

Michael McMahon is an engineer at Sun Microsystems. He works in the Java Security, Networking, and Libraries group.

Adding Drop Support to JTree

posted: 03 Feb 2009

by John Zukowski

Over the ages, drag and drop with the Swing component set has changed considerably. Early versions had a basic API in the java.awt.dnd package (with support from java.awt.datatransfer), but you had to define all aspects of the drag gesture, from the initial user clicking to the drop operation. J2SE 1.4 updates to the API improved upon the feature set and was described in an earlier tip: Dragging Text and Images with Swing

The earlier API changes made most of the drag and drop tasks much easier because many components had built-in support for drag and drop tasks. For instance, to enable drag operations in a JTextField, you just have to call setDragEnabled(true) on the text component. Users could then drag text out of a text component into some other application that acted as a drop zone, or even within the text field itself.

The text components offer built-in drop support, as does the JColorChooser component, but adding drop support to any of the other Swing components -- like JList, JTable, or JTree -- requires a little bit of extra work. The task might sound complicated, but thanks to the help of the new to Java SE 6 inner DropLocation class of TransferHandler, the task is relatively easy. All you have to do is create a TransferHandler for the JTree that defines what kind of data is droppable on it and what to do with it once dropped. Those operations are provided by the canImport and importData methods, respectively. The TransferSupport inner class is new to Java SE 6 and adds a simpler way to define transfer handlers.

You could create a fancy JTree that accepts images or text to put on a leaf of the tree, but the example here will just accept strings. Feel free to extend the example to images, too. To support strings, you need to define the canImport method with its TransferHandler.TransferSupport argument to check the supported data flavors (string) and operation type. TransferSupport also has a getDropLocation method to get the TransferHandler.DropLocation of the task. As long as the location is a valid spot, the canImport method should return true. Here's the method which returns true for a string-flavored, drop transfer over a non-null tree path.

public boolean canImport(TransferHandler.TransferSupport support) { if (!support.isDataFlavorSupported(DataFlavor.stringFlavor) || !support.isDrop()) { return false; } JTree.DropLocation dropLocation = (JTree.DropLocation)support.getDropLocation(); return dropLocation.getPath() != null; }

The JTree.DropLocation is the predefined implementation of TransferHandler.DropLocation for the JTree component. There is also a JList.DropLocation for working with JList, and another for JTree with JTree.DropLocation. There is a fourth implementation in JTextComponent.DropLocation if you don't like the default text component drop handling behavior.

The other half of adding drop support to a JTree is the importData method. The older version of the importData method -- importData(JComponent comp, Transferable t) -- is still supported, just not called directly. Newer handlers should really implement the importData(TransferHandler.TransferSupport support) version instead. In this method, you need to get the transferred data and place it in the right location in the TreePath.

Getting the transferred data hasn't really changed much going from the old importData method to the new. Instead of having a Transferable argument to the method, you just get it from the TransferSupport with the support.getTransferable method. Then, just get the data for the appropriate flavor:

Transferable transferable = support.getTransferable(); String transferData; try { transferData = (String)transferable.getTransferData( DataFlavor.stringFlavor); } catch (IOException e) { return false; } catch (UnsupportedFlavorException e) return false; }

For determining the location of the drop task, use the JTree.DropLocation class. Calling the getChildIndex method of DropLocation will give you the location in the tree to add the new node. A child index value of -1 means that the user dropped the node over an empty part of the tree. For this example, this will cause the node to be added to the end. Calling the getPath method of DropLocation returns the TreePath for the drop location. To then find the parent node associated with the drop location, call the path's getLastPathComponent method.

JTree.DropLocation dropLocation = (JTree.DropLocation)support.getDropLocation(); TreePath path = dropLocation.getPath(); int childIndex = dropLocation.getChildIndex(); if (childIndex == -1) { childIndex = model.getChildCount(path.getLastPathComponent()); } DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(transferData); DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode)path.getLastPathComponent(); model.insertNodeInto(newNode, parentNode, childIndex);

It is also helpful to ensure the new path element is visible. The complete importData method is here:

public boolean importData(TransferHandler.TransferSupport support) { if (!canImport(support)) { return false; } JTree.DropLocation dropLocation = (JTree.DropLocation)support.getDropLocation(); TreePath path = dropLocation.getPath(); Transferable transferable = support.getTransferable(); String transferData; try { transferData = (String)transferable.getTransferData(DataFlavor.stringFlavor); } catch (IOException e) { return false; } catch (UnsupportedFlavorException e) { return false; } int childIndex = dropLocation.getChildIndex(); if (childIndex == -1) { childIndex = model.getChildCount(path.getLastPathComponent()); } DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(transferData); DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode)path.getLastPathComponent(); model.insertNodeInto(newNode, parentNode, childIndex); TreePath newPath = path.pathByAddingChild(newNode); tree.makeVisible(newPath); tree.scrollRectToVisible(tree.getPathBounds(newPath)); return true; }

While we've shown a sufficient amount of detail to have a fully working droppable JTree, it is import to mention one more piece of information related to drop support, the DropMode. DropMode is an enumeration of modes related to how the component shows where the dropping is going to happen. Four supported modes are available for JTree:

  • DropMode.USE_SELECTION
  • DropMode.ON
  • DropMode.INSERT
  • DropMode.ON_OR_INSERT

However, it is important to point out that the enumeration is larger for modes specific to other components (like INSERT_COLS or INSERT_ROWS when working with a JTable).

What's the deal with the drop mode? By default, the mode is USE_SELECTION, which means no longer highlight the selected item in the JTree. Instead, use the selection mechanism to highlight the drop location. It is highly recommended that if your JTree is meant to support dropping, change the default. A better mode is ON, which lets you see both the current selection in the JTree and the potential drop location. INSERT mode allows you to insert new nodes between existing nodes, while still seeing the current selection. ON_OR_INSERT combines the latter two. The following four figures shows what the four options look like. The completed program offers a combo box of modes to try out the different behaviors.

The complete droppable tree program is shown next. The program includes a text area at the top for entry of text that can then be selected and dropped onto the JTree in the middle. The drop mode is settable from the combo box on the bottom. The data model for the tree comes from the default model created when one isn't specified when creating the JTree.

import java.awt.*; import java.awt.datatransfer.*; import java.awt.event.*; import java.io.*; import javax.swing.*; import javax.swing.tree.*; public class DndTree { public static void main(String args[]) { Runnable runner = new Runnable() { public void run() { JFrame f = new JFrame("D-n-D JTree"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel top = new JPanel(new BorderLayout()); JLabel dragLabel = new JLabel("Drag me:"); JTextField text = new JTextField(); text.setDragEnabled(true); top.add(dragLabel, BorderLayout.WEST); top.add(text, BorderLayout.CENTER); f.add(top, BorderLayout.NORTH); final JTree tree = new JTree(); final DefaultTreeModel model = (DefaultTreeModel)tree.getModel(); tree.setTransferHandler(new TransferHandler() { public boolean canImport(TransferHandler.TransferSupport support) { if (!support.isDataFlavorSupported(DataFlavor.stringFlavor) || !support.isDrop()) { return false; } JTree.DropLocation dropLocation = (JTree.DropLocation)support.getDropLocation(); return dropLocation.getPath() != null; } public boolean importData(TransferHandler.TransferSupport support) { if (!canImport(support)) { return false; } JTree.DropLocation dropLocation = (JTree.DropLocation)support.getDropLocation(); TreePath path = dropLocation.getPath(); Transferable transferable = support.getTransferable(); String transferData; try { transferData = (String)transferable.getTransferData( DataFlavor.stringFlavor); } catch (IOException e) { return false; } catch (UnsupportedFlavorException e) { return false; } int childIndex = dropLocation.getChildIndex(); if (childIndex == -1) { childIndex = model.getChildCount(path.getLastPathComponent()); } DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(transferData); DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode)path.getLastPathComponent(); model.insertNodeInto(newNode, parentNode, childIndex); TreePath newPath = path.pathByAddingChild(newNode); tree.makeVisible(newPath); tree.scrollRectToVisible(tree.getPathBounds(newPath)); return true; } }); JScrollPane pane = new JScrollPane(tree); f.add(pane, BorderLayout.CENTER); JPanel bottom = new JPanel(); JLabel comboLabel = new JLabel("DropMode"); String options[] = {"USE_SELECTION", "ON", "INSERT", "ON_OR_INSERT" }; final DropMode mode[] = {DropMode.USE_SELECTION, DropMode.ON, DropMode.INSERT, DropMode.ON_OR_INSERT}; final JComboBox combo = new JComboBox(options); combo.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int selectedIndex = combo.getSelectedIndex(); tree.setDropMode(mode[selectedIndex]); } }); bottom.add(comboLabel); bottom.add(combo); f.add(bottom, BorderLayout.SOUTH); f.setSize(300, 400); f.setVisible(true); } }; EventQueue.invokeLater(runner); } }

For additional information on the drag and drop support and data transfer APIs, please see the Introduction to Drag and Drop and Data Transfer trail in The Java Tutorial.

Listeners vs Adapters

posted: 03 Feb 2009

by John Zukowski

The JavaBeans component model (and thus the Swing component set) is built upon properties and events. Properties have setter and getter methods for working with their values. Events require you to use listeners and to implement interfaces in order to receive notification of their occurrence. Although working with properties is simple, listener objects require a little extra discussion to understand how they work, typically in the graphical user interface (GUI) world. Specifically, this tip describes the AWT and Swing event-related classes that offer both a listener interface and an adapter implementation.

The following classes show examples of listener and adapter pairs:

package java.awt.event - ComponentListener/ComponentAdapter - ContainerListener/ContainerAdapter - FocusListener/FocusAdapter - HierarchyBoundsListener/HierarchyBoundsAdapter - KeyListener/KeyAdapter - MouseListener/MouseAdapter - MouseMotionListener/MouseMotionAdapter - WindowListener/WindowAdapter package java.awt.dnd - DragSourceListener/DragSourceAdapter - DragTargetListener/DragTargetAdapter package javax.swing.event - InternalFrameListener/InternalFrameAdapter - MouseInputListener/MouseInputAdapter

These class pairs offer two ways to do the same thing. First, consider a simple example that doesn't offer an adapter class. The ActionListener class has a single actionPerformed method. Using an anonymous inner class, you will typically use an ActionListener class in the following manner:

ActionListener listener = new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { System.out.println("Event happened"); } };

You can also use the actionPerformed method by implementing the ActionListener interface in a high-level class:

public class MyClass extends JFrame implements ActionListener { ... public void actionPerformed(ActionEvent actionEvent) { System.out.println("Event happened"); } }

The ActionListener interface has a single method, and implementers of the interface must provide an implementation of that single method for it to do much of anything.

Other listener interfaces aren't quite so simplistic. For example, the MouseMotionListener interface has two methods: mouseDragged and mouseMoved. When you implement an interface, you must implement all the methods defined by the interface:

MouseMotionListener listener = new MouseMotionListener() { public void mouseDragged(MouseEvent mouseEvent) { System.out.println("I'm dragging: " + mouseEvent); } public void mouseMoved(MouseEvent mouseEvent) { System.out.println("I'm moving: " + mouseEvent); } };

There are situations when your application doesn't need to track all events for a particular listener interface. Maybe your code only needs to respond to one or two of the methods in a listener interface. For instance, do you really want to know when a mouse moves, or only when it moves with a mouse button depressed? You cannot implement just one of the MouseMotionListener methods and leave the others out:

MouseMotionListener badListener = new MouseMotionListener() { public void mouseDragged(MouseEvent mouseEvent) { System.out.println("I'm dragging: " + mouseEvent); } };

This listener implementation will result in a compile-time error since an interface isn't fully implemented. With an interface like MouseMotionListener, that isn't too much of a problem, you just have to provide a stub for the method you aren't interested in:

MouseMotionListener listener = new MouseMotionListener() { public void mouseDragged(MouseEvent mouseEvent) { System.out.println("I'm dragging: " + mouseEvent); } public void mouseMoved(MouseEvent mouseEvent) { // Do nothing } };

Not all listener interfaces are so small. Although MouseMotionListener has only two methods, the MouseListener interface has five:

  • void mouseClicked(MouseEvent mouseEvent)
  • void mouseEntered(MouseEvent mouseEvent)
  • void mouseExited(MouseEvent mouseEvent)
  • void mousePressed(MouseEvent mouseEvent)
  • void mouseReleased(MouseEvent mouseEvent)

If you want to add a MouseListener to a component, your interface implementation must have five methods in it:

MouseListener mouseListener = new MouseListener() { public void mouseClicked(MouseEvent mouseEvent) { System.out.println("I'm clicked: " + mouseEvent); } public void mouseEntered(MouseEvent mouseEvent) { System.out.println("I'm entered: " + mouseEvent); } public void mouseExited(MouseEvent mouseEvent) { System.out.println("I'm exited: " + mouseEvent); } public void mousePressed(MouseEvent mouseEvent) { System.out.println("I'm pressed: " + mouseEvent); } public void mouseReleased(MouseEvent mouseEvent) { System.out.println("I'm released: " + mouseEvent); } };

If your application only needs to know whether the mouse is pressed or released over a component, the other three methods will be empty and ignored. Those methods are unnecessary code. The adapter classes can help reduce the amount of code you must write when your application needs only a small subset of all interface methods. Each adapter class fully implements its associated interface (or interfaces). Then, if you want a listener for a subset of associated methods, you just have to provide that subset. No empty stubs required. Here is just such an adapter for the required MouseListener previously described.

MouseListener mouseListener = new MouseAdapter() { public void mousePressed(MouseEvent mouseEvent) { System.out.println("I'm pressed: " + mouseEvent); } public void mouseReleased(MouseEvent mouseEvent) { System.out.println("I'm released: " + mouseEvent); } };

This code still creates a MouseListener. However, instead of implementing all the interface methods that you don't care about, with the help of MouseAdapter, you only have to implement those MouseListener methods you are truly interested in.

Not every multi-method listener has an adapter. You can certainly create your own if you constantly find your self stubbing out most of an interface. Of the built-in classes, only the listeners listed at the top of this tip offer them. Also, the adapters are true classes, not interfaces. If you want your custom JButton subclass to also implement MouseListener, you cannot have that class subclass MouseAdapter, as only single inheritance is allowed. For example, the following code causes a compilation-time error because it attempts to subclass both JButton and MouseAdapter:

public class BadJButtonSubclass extends JButton, MouseAdapter { ... public void mousePressed(MouseEvent mouseEvent) { System.out.println("I'm pressed: " + mouseEvent); } }

If you truly wanted this JButton subclass to be a MouseListener, you must explicitly say so, and make sure all the methods of the interface are implemented:

public class GoodJButtonSubclass extends JButton implements MouseListener { ... public void mouseClicked(MouseEvent mouseEvent) { // Do nothing } public void mouseEntered(MouseEvent mouseEvent) { // Do nothing } public void mouseExited(MouseEvent mouseEvent) { // Do nothing } public void mousePressed(MouseEvent mouseEvent) { System.out.println("I'm pressed: " + mouseEvent); } public void mouseReleased(MouseEvent mouseEvent) { // Do nothing } ... addMouseListener(this); ... }

Of course, you don't have to have your high-level class implement the interface itself. This may be a good example of when you should create the listener as an inner or anonymous class instead.

If you use an integrated development environment (IDE) to create your user interface, the IDE will often generate the interface framework for you. You will need to code the business logic inside the necessary interface methods. An IDE can simplify the implementation of a large interface.

For more information about this topic, read the How to Write a Mouse Listener lesson of The Java Tutorial.

Adapters aren't limited to mouse listening. However, the MouseAdapter is a frequent example because the MouseListener interface has so many methods. The WindowListener interface is also another large interface, and it has an associated WindowAdapter class.

The need for BigDecimal

posted: 03 Feb 2009

by John Zukowski

Working with floating point numbers can be fun. Typically, when working with amounts, you automatically think of using a double type, unless the value is a whole number, then an int type is typically sufficient. A float or long can also work out, depending upon the size of a value. When dealing with money, though, these types are absolutely the worst thing you can use as they don't necessarily give you the right value, only the value that can be stored in a binary number format. Here is a short example that shows the perils of using a double for calculating a total, taking into account a discount, and adding in sales tax.

The Calc program starts with an amount of $100.05, then gives the user a 10% discount before adding back 5% sale tax. Your sales tax percentage may vary, but this example will use 5%. To see the results, the class uses the NumberFormat class to format the results for what should be displayed as currency.

import java.text.NumberFormat; public class Calc { public static void main(String args[]) { double amount = 100.05; double discount = amount * 0.10; double total = amount - discount; double tax = total * 0.05; double taxedTotal = tax + total; NumberFormat money = NumberFormat.getCurrencyInstance(); System.out.println("Subtotal : "+ money.format(amount)); System.out.println("Discount : " + money.format(discount)); System.out.println("Total : " + money.format(total)); System.out.println("Tax : " + money.format(tax)); System.out.println("Tax+Total: " + money.format(taxedTotal)); } }

Using a double type for all the internal calculations produces the following results:

Subtotal : $100.05 Discount : $10.00 Total : $90.04 Tax : $4.50 Tax+Total: $94.55

The Total value in the middle is what you might expect, but that Tax+Total value at the end is off. That discount should be $10.01 to give you that $90.04 amount. Add in the proper sales tax and the final total goes up a penny. The tax office won't appreciate that. The problem is rounding error. Calculations build on those rounding errors. Here are the unformatted values:

Subtotal : 100.05 Discount : 10.005 Total : 90.045 Tax : 4.50225 Tax+Total: 94.54725

Looking at the unformatted values, the first question you might ask is why does 90.045 round down to 90.04 and not up to 90.05 as you might expect? (or why does 10.005 round to 10.00?) This is controlled by what is called the RoundingMode, an enumeration introduced in Java SE 6 that you had no control over in prior releases. The acquired NumberFormat for currencies has a default rounding mode of HALF_EVEN. This means that when the remaining value is equidistant to the edges, to round towards the even side. According to the Java platform documentation for the enumeration, this will statistically minimize cumulative errors after multiple calculations.

The other available modes in the RoundingMode enumeration are:

  • CEILING which always rounds towards positive infinity
  • DOWN which always rounds towards zero
  • FLOOR which always rounds towards negative infinity
  • UP which always rounds away from zero
  • HALF_DOWN which always rounds towards nearest neighbor, unless both neighbors are equidistant, in which case it rounds down
  • HALF_UP which always rounds towards nearest neighbor, unless both neighbors are equidistant, in which case it rounds up
  • UNNECESSARY which asserts exact result, with no rounding necessary

Before looking into how to correct the problem, let us look at a slightly modified result, starting with a value of 70 cents, and offering no discount.

Total : $0.70 Tax : $0.03 Tax+Total: $0.74

In the case of the 70 cent transaction, it isn't just a rounding problem. Looking at the values without formatting, here's the output:

Total : 0.7 Tax : 0.034999999999999996 Tax+Total: 0.735

For the sales tax the value 0.035 just can't be stored as a double. It just isn't representable in binary form as a double.

The BigDecimal class helps solve some problems with doing floating-point operations with float and double. The BigDecimal class stores floating-point numbers with practically unlimited precision. To manipulate the data, you call the add(value), subtract(value), multiply(value), or divide(value, scale, roundingMode) methods.

To output BigDecimal values, set the scale and rounding mode with setScale(scale, roundingMode), or use either the toString() or toPlainString() methods. The toString() method may use scientific notation while toPlainString() never will.

Before converting the program to use BigDecimal, it is important to point out how to create one. There are 16 constructors for the class. Since you can't necessarily store the value of a BigDecimal in a primitive object like a double, it is best to create your BigDecimal objects from a String. To demonstrate this error, here's a simple example:

double dd = .35; BigDecimal d = new BigDecimal(dd); System.out.println(".35 = " + d);

The output is not what you might have expected:

.35 = 0.34999999999999997779553950749686919152736663818359375

Instead, what you should do is create the BigDecimal directly with the string ".35" as shown here:

BigDecimal d = new BigDecimal(".35");

resulting in the following output:

.35 = 0.35

After creating the value, you can explicitly set the scale of the number and its rounding mode with setScale(). Like other Number subclasses in the Java platform, BigDecimal is immutable, so if you call setScale(), you must "save" the return value:

d = d.setScale(2, RoundingMode.HALF_UP);

The modified program using BigDecimal is shown here. Each calculation requires working with another BigDecimal and setting its scale to ensure the math operations work for dollars and cents. If you want to deal with partial pennies, you can certainly go to three decimal places in the scale but it isn't necessarily.

import java.math.BigDecimal; import java.math.RoundingMode; public class Calc2 { public static void main(String args[]) { BigDecimal amount = new BigDecimal("100.05"); BigDecimal discountPercent = new BigDecimal("0.10"); BigDecimal discount = amount.multiply(discountPercent); discount = discount.setScale(2, RoundingMode.HALF_UP); BigDecimal total = amount.subtract(discount); total = total.setScale(2, RoundingMode.HALF_UP); BigDecimal taxPercent = new BigDecimal("0.05"); BigDecimal tax = total.multiply(taxPercent); tax = tax.setScale(2, RoundingMode.HALF_UP); BigDecimal taxedTotal = total.add(tax); taxedTotal = taxedTotal.setScale(2, RoundingMode.HALF_UP); System.out.println("Subtotal : " + amount); System.out.println("Discount : " + discount); System.out.println("Total : " + total); System.out.println("Tax : " + tax); System.out.println("Tax+Total: " + taxedTotal); } }

Notice that NumberFormat isn't used here, though you can add it back if you'd like to show the currency symbol.

Now, when you run the program, the calculations look a whole lot better:

Subtotal : 100.05 Discount : 10.01 Total : 90.04 Tax : 4.50 Tax+Total: 94.54

BigDecimal offers more functionality than what these examples show. There is also a BigInteger class for when you need unlimited precision using whole numbers. The Java platform documentation for the two classes offers more details for the two classes, including more details on scales, the MathContext class, sorting, and equality.

The Attach API

posted: 03 Feb 2009

by John Zukowski

When working with the Java platform, you typically program with the standard java.* and javax.* libraries. However, those aren't the only things that are provided for you with Sun's Java Development Kit (JDK). Several additional APIs are provided in a tools.jar file, found in the lib directory under your JDK installation directory. You'll find support for extending the javadoc tool and an API called the Attach API.

As the name may imply, the Attach API allows you to attach to a target virtual machine (VM). By attaching to another VM, you can monitor what's going on and potentially detect problems before they happen. The Attach API classes are found in the com.sun.tools.attach and com.sun.tools.attach.spi packages, though you'll typically never directly use the com.sun.tools.attach.spi classes.

Even including the one class in the .spi package that you won't use, the whole API includes a total of seven classes. Of that, three are exception classes and one a permission. That doesn't leave much to learn about, only VirtualMachine and its associated VirtualMachineDescriptor class.

The VirtualMachine class represents a specific Java virtual machine (JVM) instance. You connect to a JVM by providing the VirtualMachine class with the process id, and then you load a management agent to do your customized behavior:

VirtualMachine vm = VirtualMachine.attach (processid); String agent = ... vm.loadAgent(agent);

The other manner of acquiring a VirtualMachine is to ask for the list of virtual machines known to the system, and then pick the specific one you are interested in, typically by name:

String name = ... List vms = VirtualMachine.list(); for (VirtualMachineDescriptor vmd: vms) { if (vmd.displayName().equals(name)) { VirtualMachine vm = VirtualMachine.attach(vmd.id()); String agent = ... vm.loadAgent(agent); // ... } }

Before looking into what you can do with the agent, there are two other things you'll need to consider. First, the loadAgent method has an optional second argument to pass settings into the agent. As there is only a single argument here to potentially pass multiple options, multiple arguments get passed in as a comma-separated list:

vm.loadAgent (agent, "a=1,b=2,c=3");

The agent would then split them out with code similar to the following, assuming the arguments are passed into the agent's args variable.

String options[] = args.split(","); for (String option: options) System.out.println(option); }

The second thing to mention is how to detach the current virtual machine from the target virtual machine. That's done via the detach method. After you load the agent with loadAgent, you should call the detach method.

A JMX agent exists in the management-agent.jar file that comes with the JDK. Found in the same directory as tools.jar, the JMX management agent allows you to start the remote JMX agent's MBean Server and get an MBeanServerConnection to that server. And, with that, you can list things like threads in the remote virtual machine.

The following program does just that. First, it attaches to the identified virtual machine. It then looks for a running remote JMX server and starts one if not already started. The management-agent.jar file is specified by finding the java.home of the remote virtual machine, not necessarily the local one. Once connected, the MBeanServerConnection is acquired, from which you query the ManagementFactory for things like threads or ThreadMXBean as the case may be. Lastly, a list of thread names and their states are displayed.

import java.lang.management.*; import java.io.*; import java.util.*; import javax.management.*; import javax.management.remote.*; import com.sun.tools.attach.*; public class Threads { public static void main(String args[]) throws Exception { if (args.length != 1) { System.err.println("Please provide process id"); System.exit(-1); } VirtualMachine vm = VirtualMachine.attach(args[0]); String connectorAddr = vm.getAgentProperties().getProperty( "com.sun.management.jmxremote.localConnectorAddress"); if (connectorAddr == null) { String agent = vm.getSystemProperties().getProperty( "java.home")+File.separator+"lib"+File.separator+ "management-agent.jar"; vm.loadAgent(agent); connectorAddr = vm.getAgentProperties().getProperty( "com.sun.management.jmxremote.localConnectorAddress"); } JMXServiceURL serviceURL = new JMXServiceURL(connectorAddr); JMXConnector connector = JMXConnectorFactory.connect(serviceURL); MBeanServerConnection mbsc = connector.getMBeanServerConnection(); ObjectName objName = new ObjectName( ManagementFactory.THREAD_MXBEAN_NAME); Set<ObjectName> mbeans = mbsc.queryNames(objName, null); for (ObjectName name: mbeans) { ThreadMXBean threadBean; threadBean = ManagementFactory.newPlatformMXBeanProxy( mbsc, name.toString(), ThreadMXBean.class); long threadIds[] = threadBean.getAllThreadIds(); for (long threadId: threadIds) { ThreadInfo threadInfo = threadBean.getThreadInfo(threadId); System.out.println (threadInfo.getThreadName() + " / " + threadInfo.getThreadState()); } } } }

To compile this program, you need to make sure you include tools.jar in your CLASSPATH. Assuming JAVA_HOME is set to the Java SE 6 installation directory, of which the Attach API is a part, the following line will compile your program:

> javac -cp %JAVA_HOME%/lib/tools.jar Threads.java

From here, you could run the program, but there is nothing to attach to. So, here's a simple Swing program that displays a frame. Nothing fancy, just something to list some thread names you might recognize.

import java.awt.*; import javax.swing.*; public class MyFrame { public static void main(String args[]) { Runnable runner = new Runnable() { public void run() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(300, 300); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } }

Run the MyFrame program in one window and be prepared to run the Threads program in another. Make sure they share the same runtime location so the system can connect to the remote virtual machine.

Launch MyFrame with the typical startup command:

> java MyFrame

Then, you need to find out the process of the running application. That's where the jps command comes in handy. It will list the process ids for all virtual machines started for the JDK installation directory you are using. Your output, specifically the process ids, will probably be different:

> jps 5156 Jps 4276 MyFrame

Since the jps command is itself a Java program, it shows up in the list, too. Here, 4276 is what is needed to pass into the Threads program. Your id will most likely be different. Running Threads then dumps the list of running threads:

> java -cp %JAVA_HOME%/lib/tools.jar;. Threads 4276 JMX server connection timeout 18 / TIMED_WAITING RMI Scheduler(0) / TIMED_WAITING RMI TCP Connection(1)-192.168.0.101 / RUNNABLE RMI TCP Accept-0 / RUNNABLE DestroyJavaVM / RUNNABLE AWT-EventQueue-0 / WAITING AWT-Windows / RUNNABLE AWT-Shutdown / WAITING Java2D Disposer / WAITING Attach Listener / RUNNABLE Signal Dispatcher / RUNNABLE Finalizer / WAITING Reference Handler / WAITING

You can use the JMX management agent to do much more than just list threads. For instance, you can call the findDeadlockedThreads method of ThreadMXBean to find deadlocked threads.

Creating your own agent is actually rather simple. Similar to how applications require a main method, agents have an agentMain method. This isn't a part of any interface. The system just knows to look for one with the right argument set as parameters.

import java.lang.instrument.*; public class SecretAgent { public static void agentmain(String agentArgs, Instrumentation instrumentation) { // ... } }

To use the agent, you must then package the compiled class file into a JAR file and specify the Agent-Class in the manifest:

Agent-Class: SecretAgent

The main method of your program then becomes a little shorter than the original Threads program since you don't have to connect to the remote JMX connector. Just be sure to change the JAR file reference there to use your newly packaged agent. Then, when you run the SecretAgent program, it will run the agentmain method right at startup, even before the application's main method is called. Like Applet, there are other magically named methods for doing things that are not part of any interface.

Use the Threads program to monitor more virtual machines and try to get some threads to deadlock to show how you can still communicate with the blocked virtual machine.

See the Attach API documentation for more information. Consider also reading up on the Java Virtual Machine Tool Interface (JVM TI). It requires the use of the Attach API.

Logging Localized Message

posted: 03 Feb 2009

by John Zukowski

The Logging API was last covered in the October 22, 2002 tip Filtering Logged Messages. While the API hasn't changed much since being introduced with the 1.4 release of Java SE, there are two things many people don't realize when they log a message with something like the following:

logger.log(level, message);

First, the message argument doesn't have to be a hard-coded string. Second, the message can take arguments. Internally, relying on MessageFormat, the logger will take any arguments passed in after the log message string and use them to fill in any blanks in the message. The first argument after the message string argument to log() will have index 0 and is represented by the string {0}. The next argument is {1}, then {2}, and so on. You can also provide additional formatting details, like {1, time} would show only the time portion of a Date argument.

To demonstrate, here's how one formatted log message call might look:

String filename = ...; String message = "Unable to delete {0} from system."; logger.log(level, message, filename);

Now, for filename GWBASIC.EXE, the message displayed would be "Unable to delete GWBASIC.EXE from system."

On its own, this isn't too fancy a deal. Where this extra bit of formatting really comes in handy is when you treat the message argument as a lookup key into a resource bundle. When you fetch a Logger, you can either pass in only the logger name, or both the name and a resource bundle name.

By combining messages fetched from a resource bundle with local arguments, you get all the benefits of localized, parameterized messages, not just in your programs, but in your log messages as well.

To treat the message argument as a lookup key to a resource bundle, the manner of fetching the Logger needs to change slightly. If you want to use resource bundles, avoid creating a Logger object like this:

private static Logger logger = Logger.getLogger("com.example");

Instead, add an optional second argument to the getLogger() call. The argument is the resource bundle that contains localized messages. Then, when you make a call to log a message, the "message" argument is the lookup key into the resource bundle, whose name is passed to the getLogger() call.

private static final String BUNDLE_NAME = "com.example.words"; private static Logger logger = Logger.getLogger("com.example", BUNDLE_NAME);

The BUNDLE_NAME resource bundle must include the appropriate message for the key provided to the logging call:

logger.log(level, "messageKey");

If "messageKey" is a valid key in the resource bundle, you now have the associated message text logged to the Logging API. That message text can include those {0}-like arguments to get your message arguments passed into the logger.

String filename = ...; logger.log(level, "messageKey", filename);

While you don't see the {0} formatting string in "messageKey", since its value was acquired from the resource bundle, you could get your output formatted with MessageFormat again.

Let us put all the pieces together. We'll create a small application that shows localized logging.

To keep things simple, these resource bundles will be PropertyResourceBundle objects instead of ListResourceBundle objects.

Create file messages.properties in the local directory to include the messages for the default locale, assumed to be US English.

Message1=Hello, World Message2=Hello, {0}

The second language will be Spanish. Place the following in the file messages_ES.properties:

Message1=Hola, mundo Message2=Hola, {0}

Now, we have to create the application. Notice that the getAnonymousLogger() method also includes a second version that accepts a resource bundle name. If you want to use a named logger, feel free to pass in the name and use getLogger() instead.

import java.util.logging.*; public class LocalLog { private static Logger logger = Logger.getAnonymousLogger("message"); public static void main(String argv[]) { logger.log(Level.SEVERE, "Message1"); logger.log(Level.SEVERE, "Message2", "John"); } }

The LocalLog program's log messages are "Message1" and "Message2". When run with the default locale, you'll get messages similar to the following:

> java LocalLog Aug 4, 2007 12:00:35 PM LocalLog main SEVERE: Hello, World Aug 4, 2007 12:00:35 PM LocalLog main SEVERE: Hello, John

To run the program with a different locale, set the user.language system property on the command line:

>java -Duser.language=ES LocalLog ago 4, 2007 12:01:18 p.m. LocalLog main GRAVE: Hola, mundo ago 4, 2007 12:01:18 p.m. LocalLog main GRAVE: Hola, John

Notice that your log message contains the localized resource bundle message, and the logger also uses localized date strings and localized severity level text.

Keep this feature in mind to create localized log messages. You can use resource bundles to provide both localized log messages and user interface text. Those reading log files should be able to see translated text, too.

For additional information on resource bundles, see the Resource Bundle Loading tip and the Isolating Locale-Specific Data lesson in The Java Tutorial.

Using Enhanced For-Loops with Your Classes

posted: 03 Feb 2009

The author of this tip is John Zukowski, president and principal consultant of JZ Ventures, Inc.

The enhanced for-loop is a popular feature introduced with the Java SE platform in version 5.0. Its simple structure allows one to simplify code by presenting for-loops that visit each element of an array/collection without explicitly expressing how one goes from element to element.

Because the old style of coding didn't become invalid with the new for-loop syntax, you don't have to use an enhanced for-loop when visiting each element of an array/collection. However, with the new style, one's code would typically change from something like the following:

for (int i=0; i < array.length; i++) { System.out.println("Element: " + array[i]); }

to the newer form

for (String element : array) { System.out.println("Element: " + element); }

Assuming "array" is defined to be an array of String objects, each element is assigned to the element variable as it loops through the array. These basics of the enhanced for-loop were covered in an earlier Tech Tip: The Enhanced For Loop, from May 5, 2005.

If you have a class called Colony which contains a group of Penguin objects, without doing anything extra to get the enhanced for-loop to work, one way you would loop through each penguin element would be to return an Iterator and iterate through the colony. Unfortunately, the enhanced for-loop does not work with Iterator, so the following won't even compile:

// Does not compile import java.util.*; public class BadColony { static class Penguin { String name; Penguin(String name) { this.name = name; } public String toString() { return "Penguin{" + name + "}"; } } Set<Penguin> set = new HashSet<Penguin>(); public void addPenguin(Penguin p) { set.add(p); } public Iterator<Penguin> getPenguins() { return set.iterator(); } public static void main(String args[]) { Colony colony = new Colony(); Penguin opus = new Penguin("Opus"); Penguin chilly = new Penguin("Chilly Willy"); Penguin mumble = new Penguin("Mumble"); Penguin emperor = new Penguin("Emperor"); colony.addPenguin(opus); colony.addPenguin(chilly); colony.addPenguin(mumble); colony.addPenguin(emperor); Iterator<Penguin> it = colony.getPenguins(); // The bad line of code: for (Penguin p : it) { System.out.println(p); } } }

You cannot just pass an Iterator into the enhanced for-loop. The 2nd line of the following will generate a compilation error:

Iterator<Penguin> it = colony.getPenguins(); for (Penguin p : it) {

The error:

BadColony.java:36: foreach not applicable to expression type for (Penguin p : it) { ^ 1 error

In order to be able to use your class with an enhanced for-loop, it does need an Iterator, but that Iterator must be provided via the Iterable interface:

public interface java.lang.Iterable { public java.util.Iterator iterator(); }

Actually, to be more correct, you can use a generic T, allowing the enhanced for-loop to avoid casting, returning the designated generic type, instead of just a plain old Object.

public interface java.lang.Iterable<T> { public java.util.Iterator<T> iterator(); }

It is this Iterable object which is then provided to the enhanced for loop. By making the Colony class implement Iterable, and having its new iterator() method return the Iterator that getPenguins() provides, you'll be able to loop through the penguins in the colony via an enhanced for-loop.

By adding the proper implements clause:

public class Colony implements Iterable<Colony.Penguin> {

You then get your enhanced for-loop for the colony:

for (Penguin p : colony) {

Here's the updated Colony class with the corrected code:

import java.util.*; public class Colony implements Iterable<Colony.Penguin> { static class Penguin { String name; Penguin(String name) { this.name = name; } public String toString() { return "Penguin{" + name + "}"; } } Set<Penguin> set = new HashSet<Penguin>(); public void addPenguin(Penguin p) { set.add(p); } public Iterator<Penguin> getPenguins() { return set.iterator(); } public Iterator<Penguin> iterator() { return getPenguins(); } public static void main(String args[]) { Colony colony = new Colony(); Penguin opus = new Penguin("Opus"); Penguin chilly = new Penguin("Chilly Willy"); Penguin mumble = new Penguin("Mumble"); Penguin emperor = new Penguin("Emperor"); colony.addPenguin(opus); colony.addPenguin(chilly); colony.addPenguin(mumble); colony.addPenguin(emperor); for (Penguin p : colony) { System.out.println(p); } } }

Running the code produces the following output:

> java Colony Penguin{Chilly Willy} Penguin{Mumble} Penguin{Opus} Penguin{Emperor}

Keep in mind that the individual penguins are internally kept in a Set type collection so the returned order doesn't necessarily match the insertion order, which in this case it doesn't.

Remember to genericize the implements clause for the class "implements Iterable<T>" and not just say "implements Iterable". With the latter, the enhanced for-loop will only return an Object for each element.

For more information on the enhanced for-loop, please see the Java Programming Language guide from JDK 1.5

The Preferences API

posted: 03 Feb 2009

The author of this tip is John Zukowski, president and principal consultant of JZ Ventures, Inc.

The Preferences API was first covered here shortly after it was introduced with the 1.4 version of the standard platform: the July 15, 2003 article, the Preferences API.

That article described how to get and set user specific preferences. There is more to the Preferences API than just getting and setting user specific settings. There are system preferences, import and export preferences, and event notifications associated with preferences. There is even a way to provide your own custom location for storage of preferences. The first three options mentioned will be described here. Creating a custom preferences factory will be left to a later tip.

System Preferences

The Preferences API provides for two separate sets of preferences. The first set is for the individual user, allow multiple users on the same machine to have different settings defined. These are called user preferences. Each user who shares the same machine can have his or her own unique set of values associated with a group of preferences. Something like this could be like a user password or starting directory. You don't want every person on the same machine to have the same password and home directory. Well, I would hope you don't want that.

The other form of preferences is the system type. All users of a machine share the same set of system preferences. For instance, the location of an installed printer would typically be a system preference. You wouldn't necessarily have a different set of printers installed for different users. Everyone running on one machine would know about all printers known by that machine.

Another example of a system preference would be the high score of a game. There should only be one overall high score. That's what a system preference would be used for. In the previous tip you saw how userNodeForPackge() -- and subsequently userRoot() -- was used to acquire the user's preference node, the following example shows how to get the appropriate part of the system preferences tree with systemNodeForPackage() -- or systemRoot() for the root. Other than the method call to get the right preference node, the API usage is identical.

The example is a simple game, using the game term loosely here. It picks a random number from 0 to 99. If the number is higher than the previously saved number, it updates the "high score." The example also shows the current high score. The Preferences API usage is rather simple. The example just gets the saved value with getSavedHighScore(), providing a default of -1 if no high score had been saved yet, and updateHighScore(int value) to store the new high score. The HIGH_SCORE key is a constant shared by the new Preferences API accesses.

private static int getSavedHighScore() { Preferences systemNode = Preferences.systemNodeForPackage(High.class); return systemNode.getInt(HIGH_SCORE, -1); } private static void updateHighScore(int value) { Preferences systemNode = Preferences.systemNodeForPackage(High.class); systemNode.putInt(HIGH_SCORE, value); }

Here's what the whole program looks like:

import java.util.*; import java.util.prefs.*; import javax.swing.*; import java.awt.*; import java.awt.event.*; public class High { static JLabel highScore = new JLabel(); static JLabel score = new JLabel(); static Random random = new Random(new Date().getTime()); private static final String HIGH_SCORE = "High.highScore"; public static void main (String args[]) { /* -- Uncomment these lines to clear saved score Preferences systemNode = Preferences.systemNodeForPackage(High.class); systemNode.remove(HIGH_SCORE); */ EventQueue.invokeLater( new Runnable() { public void run() { JFrame frame = new JFrame("High Score"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); updateHighScoreLabel(getSavedHighScore()); frame.add(highScore, BorderLayout.NORTH); frame.add(score, BorderLayout.CENTER); JButton button = new JButton("Play"); ActionListener listener = new ActionListener() { public void actionPerformed(ActionEvent e) { int next = random.nextInt(100); score.setText(Integer.toString(next)); int old = getSavedHighScore(); if (next > old) { Toolkit.getDefaultToolkit().beep(); updateHighScore(next); updateHighScoreLabel(next); } } }; button.addActionListener(listener); frame.add(button, BorderLayout.SOUTH); frame.setSize(200, 200); frame.setVisible(true); } } ); } private static void updateHighScoreLabel(int value) { if (value == -1) { highScore.setText(""); } else { highScore.setText(Integer.toString(value)); } } private static int getSavedHighScore() { Preferences systemNode = Preferences.systemNodeForPackage(High.class); return systemNode.getInt(HIGH_SCORE, -1); } private static void updateHighScore(int value) { Preferences systemNode = Preferences.systemNodeForPackage(High.class); systemNode.putInt(HIGH_SCORE, value); } }

And, here's what the screen looks like after a few runs. The 61 score is not apt to be your high score, but it certainly could be.

You can try running the application as different users to see that they all share the same high score.

Import and Export

In the event that you wish to transfer preferences from one user to another or from one system to another, you can export the preferences from that one user/system, and then import them to the other side. When preferences are exported, they are exported into an XML formatted document whose DTD is specified by http://java.sun.com/dtd/preferences.dtd, though you don't really need to know that. You can export either a whole subtree with the exportSubtree() method or just a single node with the exportNode() method. Both methods accept an OutputStream argument to specify where to store things. The XML document will be UTF-8 character encoded. Importing of the data then happens via the importPreferences() method, which takes an InputStream argument. From an API perspective, there is no difference in importing a system node/tree or a user node.

Adding a few lines of code to the previous example will export the newly updated high score to the file high.xml. Much of the added code is responsible for launching a new thread to save the file and for handling exceptions. There are only three lines to export the single node:

Thread runner = new Thread(new Runnable() { public void run() { try { FileOutputStream fis = new FileOutputStream("high.xml"); systemNode.exportNode(fis); fis.close(); } catch (Exception e) { Toolkit.getDefaultToolkit().beep(); Toolkit.getDefaultToolkit().beep(); Toolkit.getDefaultToolkit().beep(); } } }); runner.start();

When exported, the file will look something like the following:

<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"> <preferences EXTERNAL_XML_VERSION="1.0"> <root type="system"> <map/> <node name="<unnamed>"> <map> <entry key="High.highScore" value="95"/> </map> </node> </root> </preferences>

Notice the root element has a type attribute that says "system". This states the type of node it is. The node also has a name attribute valued at "<unnamed>". Since the High class was not placed in a package, you get to work in the unnamed system node area. The entry attribute provide the current high score value, 95 in the example here, though your value could differ.

While we won't include any import code in the example here, the way to import is just a static method call on Preferences, passing in the appropriate input stream:

FileInputStream fis = new FileInputStream("high.xml"); Preferences.importPreferences(fis); fis.close();

Since the XML file includes information about whether the preferences are system or user type, the import call doesn't have to explicitly include this bit of information. Besides the typical IOExceptions that can happen, the import call will throw an InvalidPreferencesFormatException if the file format is invalid. Exporting can also throw a BackingStoreException if the data to export can't be read correctly from the backing store.

Event Notifications

The original version of the High game updated the high score preference, then explicitly made a call to update the label on the screen. A better way to perform this action would be to add a listener to the preferences node, then a value change can automatically trigger the label to update its value. That way, if the high score is ever updated from multiple places, you won't need to remember to add code to update the label after saving the updated value.

The two lines:

updateHighScore(next); updateHighScoreLabel(next);

can become one with the addition of the right listeners.

updateHighScore(next);

There is a PreferenceChangeListener and its associated PreferenceChangeEvent for just such a task. The listener will be notified for all changes to the associated node, so you need to check for which key-value pair was modified, as shown here.

PreferenceChangeListener changeListener = new PreferenceChangeListener() { public void preferenceChange(PreferenceChangeEvent e) { if (HIGH_SCORE.equals(e.getKey())) { String newValue = e.getNewValue(); int value = Integer.valueOf(newValue); updateHighScoreLabel(value); } } }; systemNode.addPreferenceChangeListener(changeListener);

The PreferenceChangeEvent has three important properties: the key, new new value, and the node itself. The new value doesn't have all the convenience methods of Preferences though. For example, you can't retrieve the value as an int. Instead you must manually convert the value yourself. Here's what the modified High class looks like:

import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import java.util.prefs.*; import javax.swing.*; public class High { static JLabel highScore = new JLabel(); static JLabel score = new JLabel(); static Random random = new Random(new Date().getTime()); private static final String HIGH_SCORE = "High.highScore"; static Preferences systemNode = Preferences.systemNodeForPackage(High.class); public static void main (String args[]) { /* -- Uncomment these lines to clear saved score systemNode.remove(HIGH_SCORE); */ PreferenceChangeListener changeListener = new PreferenceChangeListener() { public void preferenceChange(PreferenceChangeEvent e) { if (HIGH_SCORE.equals(e.getKey())) { String newValue = e.getNewValue(); int value = Integer.valueOf(newValue); updateHighScoreLabel(value); } } }; systemNode.addPreferenceChangeListener(changeListener); EventQueue.invokeLater( new Runnable() { public void run() { JFrame frame = new JFrame("High Score"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); updateHighScoreLabel(getSavedHighScore()); frame.add(highScore, BorderLayout.NORTH); frame.add(score, BorderLayout.CENTER); JButton button = new JButton("Play"); ActionListener listener = new ActionListener() { public void actionPerformed(ActionEvent e) { int next = random.nextInt(100); score.setText(Integer.toString(next)); int old = getSavedHighScore(); if (next > old) { Toolkit.getDefaultToolkit().beep(); updateHighScore(next); } } }; button.addActionListener(listener); frame.add(button, BorderLayout.SOUTH); frame.setSize(200, 200); frame.setVisible(true); } } ); } private static void updateHighScoreLabel(int value) { if (value == -1) { highScore.setText(""); } else { highScore.setText(Integer.toString(value)); } } private static int getSavedHighScore() { return systemNode.getInt(HIGH_SCORE, -1); } private static void updateHighScore(int value) { systemNode.putInt(HIGH_SCORE, value); // Save XML in separate thread Thread runner = new Thread(new Runnable() { public void run() { try { FileOutputStream fis = new FileOutputStream("high.xml"); systemNode.exportNode(fis); fis.close(); } catch (Exception e) { Toolkit.getDefaultToolkit().beep(); Toolkit.getDefaultToolkit().beep(); Toolkit.getDefaultToolkit().beep(); } } }); runner.start(); } }

In addition to the PreferenceChangeListener/Event class pair, there is a NodeChangeListener and NodeChangeEvent combo for notification of preference changes. However, these are for notification nodes additions and removals, not changing values of specific nodes. Of course, if you are writing something like a Preferences viewer, clearly you'd want to know if/when nodes appear and disappear so these classes may be of interest, too.

The whole Preferences API can be quite handy to store data beyond the life of your application without having to rely on a database system. For more information on the API, see the article Sir, What is Your Preference?

Writing toString Methods & Tech Days

posted: 03 Feb 2009

by Glen McCluskey

One of the standard methods defined in java.lang.Object is toString. This method is used to obtain a string representation of an object. You can (and normally should) override this method for classes that you write. This tip examines some of the issues around using toString.

Let's first consider some sample code:

class MyPoint { private final int x, y; public MyPoint(int x, int y) { this.x = x; this.y = y; } } public class TSDemo1 { public static void main(String args[]) { MyPoint mp = new MyPoint(37, 47); // use default Object.toString() System.out.println(mp); // same as previous, showing the // function of the default toString() System.out.println(mp.getClass().getName() + "@" + Integer.toHexString(mp.hashCode())); // implicitly call toString() on object // as part of string concatenation String s = mp + " testing"; System.out.println(s); // same as previous, except object // reference is null mp = null; s = mp + " testing"; System.out.println(s); } }

The TSDemo1 program defines a class MyPoint to represent X,Y points. It does not define a toString method for the class. The program creates an instance of the class and then prints it. When you run TSDemo1, you should see a result that looks something like this:

MyPoint@111f71 MyPoint@111f71 MyPoint@111f71 testing null testing

You might wonder how it's possible to print an arbitrary class object. The library methods such as System.out.println know nothing about the MyPoint class or its objects. So how is it possible to convert such an object to string form and then print it, as the first output statement in TSDemo1 does?

The answer is that println calls the java.io.PrintStream.print(Object) method, which then calls the String.valueOf method. The String.valueOf method is very simple:

public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); }

When println is called with a MyPoint object reference, the String.valueOf method converts the object to a string. String.valueOf first checks to make sure that the reference is not null. It then calls the toString method for the object. Since the MyPoint class has no toString method, the default one in java.lang.Object is used instead.

What does the default toString method actually return as a string value? The format is illustrated in the second print statement above. The name of the class, an "@", and the hex version of the object's hashcode are concatenated into a string and returned. The default hashCode method in Object is typically implemented by converting the memory address of the object into an integer. So your results might vary from those shown above.

The third and fourth parts of the TSDemo1 example illustrate a related idea: when you use "+" to concatenate a string to an object, toString is called to convert the object to a string form. You need to look at the bytecode expansion for TSDemo1 to see that. You can look at the bytecode for TSDemo1 (that is, in a human-readable form) by issuing the javap command as follows:

javap -c . TSDemo1

If you look at the bytecode, you'll notice that part of it involves creating a StringBuffer object, and then using StringBuffer.append(Object) to append the mp object to it. StringBuffer.append(Object) is implemented very simply:

public synchronized StringBuffer append(Object obj) { return append(String.valueOf(obj)); }

As mentioned earlier, String.valueOf calls toString on the object to get its string value.

O.K., so much for invoking the default toString method. How do you write your own toString methods? It's really very simple. Here's an example:

class MyPoint { private final int x, y; public MyPoint(int x, int y) { this.x = x; this.y = y; } public String toString() { return x + " " + y; } } public class TSDemo2 { public static void main(String args[]) { MyPoint mp = new MyPoint(37, 47); // call MyPoint.toString() System.out.println(mp); // call toString() and // extract the X value from it String s = mp.toString(); String t = s.substring(0, s.indexOf(' ')); int x = Integer.parseInt(t); System.out.println(t); } }

When you run the TSDemo2 program, the output is: 37 47 37

The toString method in this example does indeed work, but there are a couple of problems with it. One is that there is no descriptive text displayed in the toString output. All you see is a cryptic "37 47". The other problem is that the X,Y values in MyPoint objects are private. There is no other way to get at them except by picking apart the string returned from toString. The second part of the TSDemo2 example shows the code required to extract the X value from the string. Doing it this way is error-prone and inefficient.

Here's another approach to writing a toString method, one that clears up the problems in the previous example:

class MyPoint { private final int x, y; public MyPoint(int x, int y) { this.x = x; this.y = y; } public String toString() { return "X=" + x + " " + "Y=" + y; } public int getX() { return x; } public int getY() { return y; } } public class TSDemo3 { public static void main(String args[]) { MyPoint mp = new MyPoint(37, 47); // call MyPoint.toString() System.out.println(mp); // get X,Y values via accessor methods int x = mp.getX(); int y = mp.getY(); System.out.println(x); System.out.println(y); } } The output is: X=37 Y=47 37 47

This example adds some descriptive text to the output format, and defines a couple of accessor methods to get at the X,Y values. In general, when you write a toString method, the format of the string that is returned should cover all of the object contents. Your toString method should also contain descriptive labels for each field. And there should be a way to get at the object field values without having to pick apart the string. Note that using "+" within toString to build up the return value is not necessarily the most efficient approach. You might want to use StringBuffer instead.

Primitive types in the Java programming language, such as int, also have toString methods, for example Integer.toString(int). What about arrays? How can you convert an array to a string? You can assign an array reference to an Object reference, but arrays are not really classes. However, it is possible to use reflection to implement a toString method for arrays. The code looks like this:

import java.lang.reflect.*; public class TSDemo4 { public static String toString(Object arr) { // if object reference is null or not // an array, call String.valueOf() if (arr == null || !arr.getClass().isArray()) { return String.valueOf(arr); } // set up a string buffer and // get length of array StringBuffer sb = new StringBuffer(); int len = Array.getLength(arr); sb.append('['); // iterate across array elements for (int i = 0; i < len; i++) { if (i > 0) { sb.append(','); } // get the i-th element Object obj = Array.get(arr, i); // convert it to a string by // recursive toString() call sb.append(toString(obj)); } sb.append(']'); return sb.toString(); } public static void main(String args[]) { // example #1 System.out.println(toString("testing")); // example #2 System.out.println(toString(null)); // example #3 int arr3[] = new int[]{ 1, 2, 3 }; System.out.println(toString(arr3)); // example #4 long arr4[][] = new long[][]{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; System.out.println(toString(arr4)); // example #5 double arr5[] = new double[0]; System.out.println(toString(arr5)); // example #6 String arr6[] = new String[]{ "testing", null, "123" }; System.out.println(toString(arr6)); // example #7 Object arr7[] = new Object[]{ new Object[]{null, new Object(), null}, new int[]{1, 2, 3}, null }; System.out.println(toString(arr7)); } }

The TSDemo4 program creates a toString method, and then passes the toString method an arbitrary Object reference. If the reference is null or does not refer to an array, the program calls the String.valueOf method. Otherwise, the Object refers to an array. In that case, TSDemo4 uses reflection to access the array elements. Array.getLength and Array.get are the key methods that operate on the array. After an element is retrieved, the program calls toString recursively to obtain the string for the element. Doing it this way ensures that multidimensional arrays are handled properly.

The output of the TSDemo4 program is:

testing null [1,2,3] [[1,2,3],[4,5,6],[7,8,9]] [] [testing,null,123] [[null,java.lang.Object@111f71,null],[1,2,3],null]

Obviously, if you have a huge array, and you call toString, it will use a lot of memory, and the resulting string might not be particularly useful or readable by a human.

For more information about using toString methods, see Section 2.6.2, Method Invocations, in "The Java(tm) Programming Language Third Edition" by Arnold, Gosling, and Holmes http://java.sun.com/docs/books/javaprog/thirdedition/. Also see item 9, Always override toString, in "Effective Java Programming Language Guide" by Joshua Bloch (http://java.sun.com/docs/books/effective/).

**********

Sun Tech Days

The Sun Tech Days program educates developers on many technologies in a two-day format, and includes hands-on labs, university training, community programs and technical sessions. Attend an upcoming free session:

Taipei, Taiwan Oct. 19
Shanghai, China Oct. 23-25
Beijing, China Nov. 1-3
Tokyo, Japan Nov. 6-8
Frankfurt, Germany Dec. 3-5

See the Sun Tech Days website for more information about a Tech Days near you.

Socket Logging

posted: 03 Feb 2009

by John Zukowski

A couple of recent Core Java Technologies Tech Tips were related to the Java Logging API, found in the java.util.logging package. You may have read the recent tip titled Logging Localized Messages or the 2002 tip titled Filtering Logged Messages. The logging examples are always to the local file system or console. However, you can install any logging Handler, including the default SocketHandler, which allows you to send your log messages to a centralized server. Setting up the handler is easy. Configuring the server requires a little work.

Here's what a typical logging program looks like. Basically, just get the Logger and log a message to it. You control the severity of the message and and the content of the message itself.

import java.io.*; import java.util.logging.*; public class LogTest { private static Logger logger = Logger.getAnonymousLogger(); public static void main(String argv[]) throws IOException { logger.log(Level.SEVERE, "Hello, World"); logger.log(Level.INFO, "Welcome Home"); } }

By default, the messages go to whatever is configured in the logging.properties file found for the Java Runtime Environment (JRE). The handlers line of that file specifies which Handler to use for logging messages. That default is the ConsoleHandler:

handlers= java.util.logging.ConsoleHandler

But the file also includes a default FileHandler definition:

java.util.logging.FileHandler.pattern = %h/java%u.log java.util.logging.FileHandler.limit = 50000 java.util.logging.FileHandler.count = 1 java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

And changing the handlers line to the following will cause messages to go the FileHandler in addition to the ConsoleHandler.

handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler

The system comes with two additional system-defined Handler types. The MemoryHandler class represents an in-memory circular buffer and SocketHandler for logging messages to the network. The constructor public SocketHandler(String host, int port) then allows you to define where to send network log messages, or you can place the necessary settings in the logging.properties file and use the no-argument version of the constructor.

If you would prefer not to use the logging.properties file, you can add the SocketHandler to the LogTest program. Simply create the Handler and add it to the Logger with the addHandler() call. Assuming that something is listening at the other end, you'll get messages sent using the default XMLFormatter.

import java.io.*; import java.util.logging.*; public class LogTest { private static Logger logger = Logger.getAnonymousLogger(); public static void main(String argv[]) throws IOException { Handler handler = new SocketHandler("localhost", 5000); logger.addHandler(handler); logger.log(Level.SEVERE, "Hello, World"); logger.log(Level.INFO, "Welcome Home"); } }

This sends messages to port 5000 on localhost (the user's computer). Of course, by default, nothing is listening on the other end. You must create a server.

The following is a very simple version of a server that listens on port 5000 for connections and dumps data to its console. You could also use this to send data.

import javax.net.ssl.*; import javax.net.*; import java.io.*; import java.net.*; public class LogServer { private static final int PORT_NUM = 5000; public static void main(String args[]) { ServerSocketFactory serverSocketFactory = ServerSocketFactory.getDefault(); ServerSocket serverSocket = null; try { serverSocket = serverSocketFactory.createServerSocket(PORT_NUM); } catch (IOException ignored) { System.err.println("Unable to create server"); System.exit(-1); } System.out.printf("LogServer running on port: %s%n", PORT_NUM); while (true) { Socket socket = null; try { socket = serverSocket.accept(); InputStream is = socket.getInputStream(); BufferedReader br = new BufferedReader( new InputStreamReader(is, "US-ASCII")); String line = null; while ((line = br.readLine()) != null) { System.out.println(line); } } catch (IOException exception) { // Just handle next request. } finally { if (socket != null) { try { socket.close(); } catch (IOException ignored) { } } } } } }

The program here comes from the SSL Servers tip, but the author has modified that code by removing the SSL piece and using a regular ServerSocketFactory. Also, instead of echoing the message back, that message goes to the console.

Now, if you compile and run the LogServer in one window and run the LogTest program in another window, you'll notice the messages received by the LogServer. You can run LogTest multiple times to repeatedly send the same two messages. The server simply continues to accept messages.

As you can see, there isn't much to sending logging messages to a centralized network server. A configuration like this can help considerably when logging infrequent problems, as users will not have to log exactly what they were doing when a rare error situation occurred.

For more information on creating a server, see the All About Sockets lesson of the Java SE Tutorial's Custom Networking trail.

********

Ask the Experts Transcript: NetBeans IDE 6.0. Is there an obfuscator function in NetBeans? How can I run/invoke JavaFx code from a NetBeans module? What is the current status of PHP support in NetBeans? Get answers to these and a wide variety of other questions about NetBeans in this transcript.

********

Java SE 6 Update N introduces new features and enhancements aimed at providing an optimized consumer end user experience. We would like to invite you to try the Early Access versions and provide us your feedback. For a list of features: https://jdk6.dev.java.net/testing.html You can find more information at: http://jdk6.dev.java.net/ProjectHamburg.html

Using the MBeanRegistration Interface to Manage MB

posted: 03 Feb 2009

By Daniel Fuchs

The Java Management Extensions (JMX) have been present in the JDK since J2SE 5.0. In JDK 6, Sun has added a new advanced JMX sample to the JDK sample directory. The new sample -- $JDK_HOME/sample/jmx/jmx-scandir -- builds on the JMX best practices and discusses some usual JMX patterns, and some pitfalls to avoid, when you design a management interface with JMX.

In particular, the jmx-scandir sample shows all the different use cases that can be solved by implementing the MBeanRegistration interface.

This technical tip will focus on one such use case: using the MBeanRegistration interface to manage Management Bean (MBean) life cycles. Readers who are not familiar with JMX and the notion of MBeans should follow the JMX trail from the Java Tutorials before going any further.

The MBeanRegistration Interface

The MBeanRegistration interface is a callback interface. It can be implemented by MBeans, which need to perform specific actions when being registered to or unregistered from their MBeanServer. When an MBean implements MBeanRegistration, methods defined in that interface will be called by the MBeanServer before and after the MBean is registered and unregistered from the MBeanServer.

An MBean that implements the MBeanRegistration interface will obtain a reference to the MBeanServer in which it is being registered, will have a chance to obtain -- and possibly change -- its own ObjectName before being registered, and will get an opportunity to accept or reject its own registration or unregistration.

In the following code extract, the Rack MBean implements the MBeanRegistration interface:

------------------------------------------------------------------ package jmxtechtip; public interface RackMBean { /... defines methods exposed for management .../ } ------------------------------------------------------------------ package jmxtechtip; public class Rack implements RackMBean, MBeanRegistration { /... implements RackMBean methods .../ /... implements MBeanRegistration methods .../ } ------------------------------------------------------------------

The MBean interface does not extend MBeanRegistration. Only the concrete MBean class implements it. Indeed, it would be a violation to expose the methods defined in MBeanRegistration through the management interface, since the MBeanRegistration methods are designed to be called only by the MBeanServer at registration time.

This technical tip will show you how to implement the methods of MBeanRegistration to register and unregister dependent MBeans.

The Racks and Cards Use Case

Let's assume you are designing a JMX application that will expose a piece of equipment hardware. Your equipment has a rack -- represented by a RackMBean. The rack itself has a number of slots where cards can be plugged in. When plugged in, each card is represented by a CardMBean or by a subclass of CardMBean.

In this example, assume that the RackMBean is completely responsible for creating and registering CardMBean instances. The class Card can be extended to provide Card-specific implementation. The class Rack can be extended to override its createCard(...) method.

Here are the aims you are trying to achieve:

* In the class Rack, when a RackMBean is being registered, discover which slots already contain cards, and create and register a CardMBean for each of them.

* In the class Rack, when a RackMBean is being unregistered, unregister all its corresponding instances of Card.

* In the class Card, ensure that a CardMBean can only be registered by a Rack, and ensure that a CardMBean can only be unregistered by a Rack, and not by, for example, a client JMX application calling MBeanServer.unregisterMBean(...) directly for a CardMBean.

Note: The intent here is not to show how to model a rack or a card. The author chose racks and cards only for the sake of the example, because their relationship is easy to understand.

Implementing the MBeanRegistration Interface

The following code snippet shows how the Rack MBean could implement the MBeanRegistration interface:

/** * A Rack contains Cards. */ public class Rack implements RackMBean, MBeanRegistration { // MBeans must be multi-thread safe. // 'rackName' and 'server' will be set at registration time, so you // must therefore take care of synchronization issues. // Use 'volatile' to make sure that you always use an accurate value. // // Name of this rack, extracted from the ObjectName. private volatile String rackName; // The MBeanServer in which this MBean is registered private volatile MBeanServer server; // An array of slots that may be occupied (and contain a card) or may be // unoccupied. slots[i]==null means that slot 'i' is unoccupied. private final Card[] slots; /** * Creates a new rack. * @param slotCount Total number of slots. **/ public Rack(int slotCount) { if (slotCount < 0) throw new IllegalArgumentException(Integer.toString(slotCount)); this.slots = new Card[slotCount]; ... } // -------------------------------------------------------------------- // *Implementation of the MBeanRegistration Interface* // -------------------------------------------------------------------- /** * Allows the MBean to perform any operations it needs before * being registered in the MBean server. * If any exception is raised, the MBean will not be registered * in the MBean server. * * RackMBean uses this method to check the validity of the supplied * ObjectName, and in particular that it corresponds to a valid rack. * * It also uses this method to obtain a reference to the MBeanServer in * which it is registered. * * @param server -- The MBean server in which the MBean will be registered. * @param name -- The object name of the MBean. In this implementation, the * supplied name must not be null. * * @return -- The name under which the MBean is to be registered. If null, * the registration will fail. * * @throws Exception -- This exception will be caught by the MBean * server and rethrown as an MBeanRegistrationException. */ *public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {* // A null ObjectName is not allowed. Let MBeanServer throw // the exception. if (name == null) return null; // Get rackname. rackName = name.getKeyProperty(RACK_KEY); if (rackName == null) throw new MalformedObjectNameException("missing rack name: " + name); if (!isValidRack(rackName)) throw new IllegalArgumentException(rackName + ": not a valid rack"); this.server = server; return name; } /** * Allows the MBean to perform any operations needed after having * been registered in the MBean server or after the registration * has failed. * * If the registration is successful, the Rack MBean will * register all its related CardMBeans. * * @param registrationDone -- Indicates whether or not the MBean * has been successfully registered in the MBean server. * The value false means that the registration has failed. */ *public void postRegister(Boolean registrationDone) {* if (!registrationDone) return; for (int i = 0; i < slots.length; i++) { final String cardType = discoverCardType(i); if (cardType != null) { try { final Card card = createCard(i, cardType); final ObjectName cardName = createCardName(rackName, i); // Avoid calling an MBeanServer operation // from within a synchronized block. card.registerSelf(server, cardName); synchronized (slots) { slots[i] = card; } } catch (Exception x) { LOG.warning("Couldn't create CardMBean for " + cardType + "[" + i + "]"); } } } } /** * Allows the MBean to perform any operations it needs before being * unregistered by the MBean server * * The RackMBean uses this method to unregister all its related * CardMBeans. If for some reason, unregistration of one of these * MBeans fails, the RackMBean will no be unregistered either. * * @throws Exception -- This exception will be caught by the MBean * server and rethrown as an MBeanRegistrationException. */ *public void preDeregister() throws Exception {* for (int i=0; i

In preRegister <#preRegisterRack>, the Rack MBean obtains a reference to the MBeanServer in which it is registered, and a reference to its own ObjectName. It can check that the supplied ObjectName is valid -- in this example, it is considered valid if it contains the rack key -- and that it can indeed model the rack that it is asked to model. Here, this is supposed to be done by the isValidRack(...) method, whose implementation is not shown. If these conditions are not met, the Rack MBean will throw an exception, thus refusing to be registered.

In postRegister <#postRegisterRack>, the Rack MBean checks whether registration was successful. If it wasn't, it stops here. Otherwise, it will discover which slots already have a card plugged in, and for each of those, it will create and register a CardMBean. However, instead of using plain MBeanServer.registerMBean(...), it calls a package-protected method on the created Card instance -- in this example, Card.registerSelf() <#registerSelf>.

This method has been created with logic such that a CardMBean can only be registered in the MBeanServer by invoking its registerSelf(...) method.

Note that postRegister isn't supposed to throw an exception. You have thus protected all Card MBean registrations by a try { } catch { } block. This is OK because according to the application logic, Card MBean registration is not expected to fail at this point. If it does, you will simply log a warning message. Had you wanted the Rack MBean registration to fail whenever a Card MBean couldn't be registered, you would have implemented the Card registration logic in preRegister <#preRegisterRack> instead. However, that logic would have been more complex, since you would have had to implement code to rollback registration of already registered Card MBeans in case one of them failed to be registered.

In preDeregister <#preDeregisterRack>, the Rack MBean unregisters all Card MBeans it has previously registered. Here again, instead of using plain MBeanServer.unregisterMBean(...), it calls a package-protected method on the registered Card instance -- in this example, Card.unregisterSelf() <#unregisterSelf>.

This method has been created with logic such that a Card MBean can only be unregistered from the MBeanServer by invoking its unregisterSelf(...) method. Simply calling MBeanServer.unregisterMBean(...) would fail. This ensures that only the creator of the Card MBean will be able to register and unregister it. If a JMX client attempts to directly call MBeanServer.unregisterMBean(...) on a Card MBean, it will fail.

Now let's see how the Card MBean uses its MBeanRegistration interface. The following code snippet show how the Card MBean was implemented:

/** * A Card is plugged in a Rack slot. */ public class Card implements CardMBean, MBeanRegistration { // This MBean will accept to be registered or unregistered // only if 'isRegistrationAllowed' is 'true'. private volatile boolean isRegistrationAllowed = false; private volatile ObjectName name; private volatile MBeanServer server; public Card(int occupiedSlot, String cardType) { ... } // This method sets 'isRegistrationAllowed' to 'true', calls // server.registerMBean(this, name), and finally sets // 'isRegistrationAllowed' back to false. // *void registerSelf(MBeanServer server, ObjectName name) throws JMException {* *isRegistrationAllowed = true;* try { this.server = server; this.name = name; this.name = server.registerMBean(this, name).getObjectName(); } finally { *isRegistrationAllowed = false;* } } // This method sets 'isRegistrationAllowed' to 'true', // calls server.unregisterMBean(name), and finally sets // 'isRegistrationAllowed' back to false. // *void unregisterSelf() throws JMException {* *isRegistrationAllowed = true;* try { if (server != null && server.isRegistered(name)) server.unregisterMBean(name); this.server = null; this.name = null; } finally { *isRegistrationAllowed = false;* } } /** * Allows the MBean to perform any operations it needs before being * registered in the MBean server. * * *In this example, the CardMBean will refuse to be registered if * 'isRegistrationAllowed' is false -- which means that Card MBeans * can only be registered by {@link #registerSelf registerSelf()}.* * * {@code registerSelf()} is a package method which is only called by * {@link Rack}. */ *public final ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {* if (*!isRegistrationAllowed*) throw new IllegalStateException("Illegal registration attempt"); assert server == this.server && name == this.name; return name; } /** * Allows the MBean to perform any operations needed after having * been registered in the MBean server or after the registration has * failed. * *The CardMBean does not need to do anything here.* * @param registrationDone -- Indicates whether or not the MBean * has been successfully registered in the MBean server. The value * 'false' means that the registration has failed. */ *public void postRegister(Boolean registrationDone) {}* /** * Allows the MBean to perform any operations it needs before * being unregistered by the MBean server. * *The CardMBean will refuse to let itself be unregistered if * 'isRegistrationAllowed' is false, which means that Card MBeans * can only be unregistered by {@link #unregisterSelf unregisterSelf()}.* * * {@code unregisterSelf()} is a package method which is only called by * {@link Rack}. * * @throws Exception -- This exception will be caught by the MBean * server and rethrown as an MBeanRegistrationException. */ *public final void preDeregister() throws Exception {* if (*!isRegistrationAllowed*) throw new IllegalStateException("Illegal unregistration attempt"); } /** * Allows the MBean to perform any operations needed after having been * unregistered in the MBean server. * *The CardMBean does not need to do anything here.* */ *public void postDeregister() { }* }

The logic in preRegister <#preRegisterCard>, preDeregister <#preDeregisterCard>, registerSelf <#registerSelf>, and unregisterSelf <#unregisterSelf> prevents the Card MBean from being registered or unregistered by anybody but the Rack MBean that created it. The registerSelf and unregisterSelf methods are kept package-protected so that only the Rack MBean can call them. The preRegister and preDeregister methods are declared final, so that subclasses cannot change that logic.

Note: If you expected that subclasses of Card would need to perform some additional specific actions when being registered or unregistered, you would introduce a new protected hook specific to that purpose, as described in Martin Fowler's note.

However, using a simple volatile boolean isRegistrationAllowed flag still leaves some room for thread contention. Indeed, when the Rack MBean calls unregisterSelf <#unregisterSelf>, another thread could sneak in and call MBeanServer.unregisterMBean right after the isRegistrationAllowed flag is set, but before unregisterSelf completes.

A better solution would thus be to store the isRegistrationAllowed flag in a ThreadLocal.

The following code snippet shows what would need to change in the Card MBean implementation:

// initialization of isRegistrationAllowed flag: // private volatile boolean isRegistrationAllowed = false; // => replaced by: private final static ThreadLocal isRegistrationAllowed = new ThreadLocal() { @Override protected Boolean initialValue() {return false;} }; // in registerSelf / unregisterSelf .... // isRegistrationAllowed = true; // => replaced by: *isRegistrationAllowed.set(true);* try { ... } finally { // isRegistrationAllowed = false; // => replaced by: *isRegistrationAllowed.set(false);* } .... // in preRegister / preDeregister ... // if (!isRegistrationAllowed) // => replaced by: if (*!isRegistrationAllowed.get()*) throw new IllegalStateException(...); ...

Conclusion

This technical tip has shown how an MBean can make use of the MBeanRegistration interface in order to register and unregister dependent MBeans. You have also seen how such dependent MBeans could be protected in such a way that only their creator could register and unregister them.

The advanced JMX sample for JDK 6 -- $JDK_HOME/sample/jmx/jmx-scandir -- shows many other possibilities for using the MBeanRegistration interface.

About the Author

Daniel Fuchs works on the Sun Microsystems Java DMK, JMX, Java SE Team in Grenoble, France.

*****************************


Sun Microsystems Developer Playground
Join Dana Nourie November 29 at 9-10 AM PDT in Second Life at the Sun Microsystems Developer Playground to chat about how you can learn the Java platform.

Using Callable to Return Results From Runnables

posted: 03 Feb 2009

by John Zukowski

The Runnable interface has been around since the beginning of time for the Java platform. It allows you to define a task to be completed by a thread. As most people probably know already, it offers a single method run() that accepts no arguments and returns no values, nor can it throw any checked exceptions. If you need to get a value back from the now-completed task, you must use a method outside the interface and wait for some kind of notification message that the task completed. For example, the following demonstrates what you might do for just such a scenario:

Runnable runnable = ...; Thread t = new Thread(runnable); t.start(); t.join(); String value = someMethodtoGetSavedValue()

Nothing is inherently wrong with this code, but it can be done differently now, thanks to the Callable interface introduced in J2SE 5.0. Instead of having a run() method, the Callable interface offers a call() method, which can return an Object or, more specifically, any type that is introduced in the genericized form:

public interface Callable<V> { V call() throws Exception; }

Because you cannot pass a Callable into a Thread to execute, you instead use the ExecutorService to execute the Callable object. The service accepts Callable objects to run by way of the submit() method:

<T> Future<T> submit(Callable<T> task)

As the method definition shows, submitting a Callable object to the ExecutorService returns a Future object. The get() method of Future will then block until the task is completed. This is the equivalent of the join() call in the first example. Actually, it is the equivalent of both the join() call and the get value call as get() returns the value calculated by the Callable instance.

To demonstrate, the following example creates separate Callable instances for each word passed in on the command line and sums up their length. Each Callable will just calculate the sum of its individual word. The set of Future objects are saved to acquire the calculated value from each. If the order of the returned values needed to be preserved, a List could be used instead.

import java.util.*; import java.util.concurrent.*; public class CallableExample { public static class WordLengthCallable implements Callable { private String word; public WordLengthCallable(String word) { this.word = word; } public Integer call() { return Integer.valueOf(word.length()); } } public static void main(String args[]) throws Exception { ExecutorService pool = Executors.newFixedThreadPool(3); Set<Future<Integer>> set = new HashSet<Future≶Integer>>(); for (String word: args) { Callable<Integer> callable = new WordLengthCallable(word); Future<Integer> future = pool.submit(callable); set.add(future); } int sum = 0; for (Future<Integer> future : set) { sum += future.get(); } System.out.printf("The sum of lengths is %s%n", sum); System.exit(sum); } }

The WordLengthCallable saves each word and uses the word's length as the value returned by the call() method. This value could take some time to generate but in this case is known immediately. The only requirement of call() is the value is returned at the end of the call. When the get() method of Future is later called, the Future will either have the value immediately if the task runs quickly, as in this case, or will wait until the value is done generating. Multiple calls to get() will not cause the task to be rerun in the thread.

Because the goal of the program is to calculate the sum of all word lengths, it doesn't matter in which order the Callable tasks finish. It is perfectly OK if the last task completes before the first three. The first get() call to Future will just wait for the first task in the Set to complete. This does not block other tasks from running to completion separately. It is just waiting for that one thread or task to complete.

This particular example uses a fixed-size thread pool for the ExecutorService, but any available service will do.

For more information on the use of executors and thread pools, see the Executors lesson in the Java Tutorial. The SwingWorker class is another example of a Runnable object that works with a Future, though in a slightly different way. See the Worker Threads and SwingWorker lesson for more information on that.

************

NetBeans 6.0 Integrated Development Environment (IDE) increases developer productivity with a smarter, faster editor, Ruby/JRuby/Ruby on Rails support, enhancements for improved Swing development, a new Visual Game Designer, updated Data Binding support, integrated Profiling, and more. Download NetBeans 6.0

New Certification Courses

posted: 03 Feb 2009

New Certification Courses Sun Certified Programmer for the Java Platform, Standard Edition 6-- The Sun Certified Programmer for Java Platform, Standard Edition 6 certification exam is for programmers experienced using the Java programming language. Achieving this certification provides clear evidence that a programmer understands the basic syntax and structure of the Java programming language and can create Java technology applications that run on server and desktop systems using Java SE 6.

Sun Certified Programmer for the Java Platform, Standard Edition 6 Upgrade Exam (CX-310-066)-- The Sun Certified Programmer for Java Platform, Standard Edition 6 certification exam is for programmers experienced using the Java programming language. Achieving this certification provides evidence that a programmer understands the basic syntax and structure of the Java programming language and can create Java technology applications that run on server and desktop systems using Java Platform, Standard Edition 6 Sun strongly recommends that all new candidates interested in becoming a Sun Certified Java Programmer (SCJP) take this new version of the certification exam, rather than a previous version. In this way, candidates can demonstrate that they are knowledgeable in the latest technology. Sun also recommends that those certified on a previous version of SCJP update their credentials by taking the SCJP 6 Upgrade exam.

******

Sun Microsystems Developer Playground
Join Dana Nourie December 18th, Tues 9 AM PST and 7 PM PST, in Second Life at the Sun Microsystems Developer Playground to discover how easy it is to learn the Java platform through web programming. Watch a demo and have your questions answered.

******

Tech Days Comes Back to the USA

posted: 03 Feb 2009

On January 9 & 10, the Tech Days Conference will be in Atlanta Georgia. (FREE!)

Advance your development ability and shape your future with cutting-edge technical education; Sun Tech Days are loaded with practical information, examples of real-world solutions, and hands-on training. Whatever your focus, you'll find sessions to take your skills to the next level and advance your career.

In addition, Tech Days has hands-on labs to ensure you can use specific technologies, and so you can ask the questions you need to. There will also be a Q & A session with James Gosling, and a Featured Keynote by Jeet Kaul.

The sessions that will be covered:

Java Development Tracks

  • Java EE 6 and GlassFish
  • Java SE 6 Top 10 features, Java SE 7 and OpenJDK
  • Next Generation Web and Java Server Faces
  • Consumer JRE: Java on Every Desktop OR Java Performance Myths
  • Java ME: Extreme GUI Makeover for Mobile Phones
  • SOA using OpenESB and JCAP
  • Java Scripting: JavaFX Script and JRuby

Solaris Development Track

  • What Makes Solaris Interesting?
  • Sun Studio Tools
  • OpenSolaris Security Frameworks
  • Integrating ZFS Into Your Applications
  • Security Features in Solaris
  • How To Develop Solaris Parallel Applications
  • Solaris Networking For Developers

    If you're not familiar with Tech Days and want to learn more, check out the website. In addition to information on this particular event in Georgia, there is also information and resources from previous Tech Days events, including videos, presentations, and more.

    Sun Tech Days

    Placing Components on Tabs

    posted: 03 Feb 2009


    by John Zukowski

    The JTabbedPane component is a special Swing container that accepts components to be placed within a panel for each tab. Select a tab to see that tab's components. Technically speaking, one component is associated with each tab. However, that component is typically a panel that contains others. Identification for each tab is done with title text and an icon, settable per tab. The first Core Java Technologies Tech Tip to discuss the JTabbedPane component was published in July 2001, with a simple introductory tip.

    The methods of JTabbedPane to support the use of components on tabs are as follows:

    • public void setTabComponentAt(int index, Component component)
    • public Component getTabComponentAt(int index)
    • public int indexOfTabComponent(Component tabComponent)

    The first method associates the component with a tab; the second gets it back; and the last one tells you which tab is associated with the component, if any. Typically, you use only the first method, but the others are available.

    To get started with the task at hand, it is helpful to have an Icon implementation that draws a little x on it. You could just use the letter x as the button label, but you should avoid that option. Typically, when a user sees an x in a box, this signals a command to close a window, so it serves as a good indicator for closing a tab.

    class CloseIcon implements Icon { public void paintIcon(Component c, Graphics g, int x, int y) { g.setColor(Color.RED); g.drawLine(6, 6, getIconWidth() - 7, getIconHeight() - 7); g.drawLine(getIconWidth() - 7, 6, 6, getIconHeight() - 7); } public int getIconWidth() { return 17; } public int getIconHeight() { return 17; } }

    Before creating the special tab component, let's put together the program's framework, one that creates a frame with a JTabbedPane on it and adds a number of tabs:

    import java.awt.*; import java.awt.event.*; import javax.swing.*; public class CloseTabs { public static void main(String args[]) { Runnable runner = new Runnable() { public void run() { JFrame frame = new JFrame("JTabbedPane"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JTabbedPane jtp = new JTabbedPane(); frame.add(jtp, BorderLayout.CENTER); for (int i=0; i<5; i++) { JButton button = new JButton("Card " + i); jtp.add("Btn " + i, button); // new CloseTabButton(jtp, i); } frame.setSize(400, 200); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } }

    This program creates five tabs, each with a JButton on it. The tab title is "Btn" followed by the tab position.

    The program will run fine without doing anything else, but it doesn't have the close button on each tab. To add a close button to the tab, you need to retain the tab's title text and icon, while adding a button with the CloseIcon previously shown. (The example does not use the tab's icon, but the class definition includes one so that it can be reused beyond this example.) As the final part of the definition, you need an ActionListener for the button to handle when it is pressed, so that when the user presses the button, the tab is removed from the pane.

    class CloseTabButton extends JPanel implements ActionListener { private JTabbedPane pane; public CloseTabButton(JTabbedPane pane, int index) { this.pane = pane; setOpaque(false); add(new JLabel( pane.getTitleAt(index), pane.getIconAt(index), JLabel.LEFT)); Icon closeIcon = new CloseIcon(); JButton btClose = new JButton(closeIcon); btClose.setPreferredSize(new Dimension( closeIcon.getIconWidth(), closeIcon.getIconHeight())); add(btClose); btClose.addActionListener(this); pane.setTabComponentAt(index, this); } public void actionPerformed(ActionEvent e) { int i = pane.indexOfTabComponent(this); if (i != -1) { pane.remove(i); } } }

    You can now reuse the CloseTabButton in your own tabbed panes.

    For more information on the use of tabbed panes, see the How to Use Tabbed Panes lesson in The Java Tutorial.

    January 2008 Core Java Tech Tips Quiz

    posted: 03 Feb 2009

    by John Zukowski

    We've made it through another year, and it's time for another tech tips quiz. The last two -- June 2006 and September 2005 -- were popular refreshers of past tips and tricks. Here are another five questions for you. Don't look too far ahead, as the answers are at the end of this entry.

    1. Given two objects one and two of BigDecimal type, how do you multiply the two factors to calculate a product in object three?

    a. BigDecimal three = one * two;
    b. BigDecimal three = one.*(two);
    c. BigDecimal three = one.times(two);
    d. BigDecimal three = one.multiply(two);

    2. In order to write the line System.out.println("Pi = " + PI), what must the import statement be so that the compiler will locate PI in the Math class?

    a. import java.lang.Math;
    b. import java.lang.Math.PI;
    c. import static java.lang.Math.PI;
    d. import final java.lang.Math.PI;

    3. When using an enhanced for loop (also known as a foreach statement), what interface must the element following the colon (:) implement in order for the construct to compile and execute appropriately?

    a. Enumeration
    b. Iterable
    c. Iterator
    d. Collection

    4. When two Swing components overlap in their display area, how do you control which component is drawn on top?

    a. The component added to the container first is drawn on top.
    b. The component added to the container last is drawn on top.
    c. You call the setComponentZOrder() method of the container.
    d. You call the setComponentZOrder() method for each component.

    5. What is the best way to monitor the progress of image reading when using the Java Image I/O API?

    a. Create a FilteredReader subclass to count the bytes.
    b. Attach a ProgressMonitorListener to the ImageReader to report progress.
    c. Attach an IIOReadProgressListener to the ImageReader for progress reporting.
    d. Register a Runnable with the ImageReader and tell it how frequently to execute.

    Check your answers here.

    Quiz Answers

    posted: 03 Feb 2009

    Answers

    1. Given two objects one and two of BigDecimal type, how do you multiply the two factors to calculate a product in object three? Answer: D
    To generate the product of two variables of BigDecimal type, use the multiply() method of BigDecimal. This was shown in "The Need for BigDecimal" (July 2007), which also discussed formatting and rounding issues.

    2. In order to write the line System.out.println("Pi = " + PI), what must the import statement be so that the compiler will locate PI in the Math class? Answer: C
    Use static import statements to tell the compiler about constants and methods you wish to use without explicitly specifying the class they come from. See "Using Static Imports for Constants and Methods" (October 2004) for more information on working with static imports.

    3. When using an enhanced for loop (also known as a foreach statement), what interface must the element following the colon (:) implement in order for the construct to compile and execute appropriately? Answer: B
    The argument must implement the Iterable interface, which consists of a single method, to get an iterator:

    Iterator iterator()

    "The Enhanced For Loop" (May 2005) started the discussion of using the enhanced for loop. The use of Iterable was discussed later in "Using Enhanced For-Loops With Your Classes" (September 2007).

    4. When two Swing components overlap in their display area, how do you control which component is drawn on top? Answer: C
    Z-order represents the layering of the components on the screen, where x and y coordinates are for horizontal and vertical positioning, respectively. By calling the setComponentZOrder() method of the Container for each component that overlaps, you can explicitly control which components are drawn on top of which other components. See "Let There Be Z-Order" tip (January 2005) for additional information on controlling the z-order layering.

    5. What is the best way to monitor the progress of image reading when using the Java Image I/O API? Answer: C
    Although you can certainly display progress with a Progress Monitor, the best way to set up notification of such progress is by using a IIOReadProgressListener. "Monitoring Image I/O Events" (February 2007) covered this notification as well as region-update notifications.

    Sorting Strings

    posted: 03 Feb 2009

    by John Zukowski

    Sorting strings with the Java platform can be thought of as an easy task, but there is much more thought that should be put into it when developing programs for an international market. If you're stuck in the English-only mindset, and you think your program works fine because it shows that the string tomorrow comes after today, you might think all is great. But, once you have a Spanish user who wants ma�ana to be sorted properly, if all you use is the default compare() method of String for sorting, the � character will come after the z character and will not be the natural Spanish ordering, between the n character and o character. That's where the Collator class of the java.text package comes into play.

    Imagine a list of words

    • first
    • ma�ana
    • man
    • many
    • maxi
    • next

    Using the default sorting mechanism of String, its compare() method, this will result in a sorted list of:

    • first
    • man
    • many
    • maxi
    • ma�ana
    • next

    Here, ma�ana comes between maxi and next. In the Spanish world, what should happen is ma�ana should come between many and maxi as the � character (pronounced e�e) comes after the n in that alphabet. While you could write your own custom sort routine to handle the �, what happens to your program when a German user comes around and wants to use their own diacritical marks, or what about just a list of design patterns with fa�ade? Do you want fa�ade before or after factory? (Essentially treating the � with the little cedilla hook the same as c or different.)

    That's where the Collator class comes in handy. The Collator class takes into account language-sensitive sorting issues and doesn't just try to sort words based upon their ASCII/Unicode character values. Using Collator requires understanding one additional property before you can fully utilize its features, and that is something called strength. The strength setting of the Collator determines how strong (or weak) a match is used for ordering. There are four possible values for the property: PRIMARY, SECONDARY, TERTIARY, and IDENTICAL. What actually happens with each is dependent on the locale. Typically what happens is as follows. In reverse order, IDENTICAL strength means just that, the characters must be identical for them to be treated the same. TERTIARY typically is for ignoring case differences. SECONDARY is for ignoring diacritical marks, like n vs. �. PRIMARY is like IDENTICAL for base letter differences, but has some differences when handling control characters and accents. See the Collator javadoc for more information on these differences and decomposition mode rules.

    To work with Collator, you need to start by getting one. You can either call getInstance() to get one for the default locale, or pass the specific Locale to the getInstance() method to get a locale for the one provided. For instance, to get one for the Spanish language, you would create a Spanish Locale with new Locale("es") and then pass that into getInstance():

    Collator esCollator = Collator.getInstance(new Locale("es"));

    Assuming the default Collator strength for the locale is sufficient, which happens to be SECONDARY for Spanish, you would then pass the Collator like any Comparator into the sort() routine of Collections to get your sorted List:

    Collections.sort(list, esCollator);

    Working with the earlier list, that now gives you a proper sorting with the Spanish alphabet:

    • first
    • man
    • many
    • ma�ana
    • maxi
    • next

    Had you instead used the US Locale for the Collator, ma�ana would appear between man and many since the � is not its own letter.

    Here's a quick example that shows off the differences.

    import java.awt.*; import java.text.*; import java.util.*; import java.util.List; // Explicit import required import javax.swing.*; public class Sort { public static void main(String args[]) { Runnable runner = new Runnable() { public void run() { String words[] = {"first", "ma�ana", "man", "many", "maxi", "next"}; List list = Arrays.asList(words); JFrame frame = new JFrame("Sorting"); frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); Box box = Box.createVerticalBox(); frame.setContentPane(box); JLabel label = new JLabel("Word List:"); box.add(label); JTextArea textArea = new JTextArea( list.toString()); box.add(textArea); Collections.sort(list); label = new JLabel("Sorted Word List:"); box.add(label); textArea = new JTextArea(list.toString ()); box.add(textArea); Collator esCollator = Collator.getInstance(new Locale("es")); Collections.sort(list, esCollator); label = new JLabel("Collated Word List:"); box.add(label); textArea = new JTextArea(list.toString()); box.add(textArea); frame.setSize(400, 200); frame.setVisible(true); } }; EventQueue.invokeLater (runner); } }

    One last little bit of information about collation. The Collator returned by the getInstance() call is typically an instance of RuleBasedCollator for the supported languages. You can use RuleBasedCollator to define your own collation sequences. The javadoc for the class describes the rule syntax more completely, but lets say you had a four character alphabet and wanted the order of the letters to be CAFE instead of ACEF, your rule would look something like:

    String rule = "< c, C < a, A < f, F < e, E"; RuleBasedCollator collator = new RuleBasedCollator(rule);

    This defines the explicit order as cafe, with the different letter cases shown. Now, for a list of words of ace, cafe, ef, and face, the resultant sort order is cafe, ace, face, and ef with the new rules:

    import java.text.*; import java.util.*; public class Rule { public static void main(String args[]) throws ParseException { String words[] = {"ace", "cafe", "ef", "face"}; String rule ="< c, C < a, A < f, F < e, E"; RuleBasedCollator collator = new RuleBasedCollator(rule); List list = Arrays.asList(words); Collections.sort(list, collator); System.out.println(list); } }

    After compiling and running, you see the words sorted with the new rules:

    > javac Rule.java > java Rule [cafe, ace, face, ef]

    After reading the rule syntax some more in the javadocs, try to expand the alphabet and work with the different diacritical marks, too.

    Now, when developing programs for the global world, your programs can be better prepared to suit the local user. Be sure to keep strings in resource bundles, too, as shown in an earlier tip:

    Earlier tip

    *********

    Connect and Participate With GlassFish
    Try GlassFish for a chance to win an iPhone. This sweepstakes ends on March 23, 2008. Submit your entry today.

    Foote on Blu-ray Disc Java In this video interview, Sun's Blu-ray Disc Java (BDJ) architect Bill Foote talks about this powerful technology and shows some examples of BDJ code and applications. Download the code

    Getting to Know BoxLayout

    posted: 03 Feb 2009

    One of the standard layout managers that come with the Java platform is BoxLayout. This allows you to layout a single row or column of components in a container. This may sound like a not-so-complicated layout manager, but with the help of Box and its glue and struts, you'd think that would be enough, but there is even more. The vertical and horizontal alignment of the underlying components allows even more control of the positioning of components within the container. Here, we'll look at all these aspects.

    Typical Usage

    BoxLayout is unlike most layout managers which just require you to create the layout manager, and associate the layout manager with the Container. Instead, the BoxLayout constructor requires you to pass the Container into the constructor of the layout manager, thus having a reference to the other component in each of them. This can be awkward sometimes, and makes the use of the Box container more popular, since all you have to do is ask for a horizontally or vertically laid out Box through one of its static methods:

    Box vertical = Box.createVerticalBox(); Box horizontal = Box.createHorizontalBox();

    Both are using BoxLayout under the covers, placing added components on the proper access, depending upon direction. A vertical box places everything into a single column, whereas a horizontal box places everything in a row. Comparing BoxLayout (and thus Box) to GridLayout requires a quick comment. When placing a bunch of components in a GridLayout controlled container, all the components are expected to be the same size. With BoxLayout, that isn't the case, and the maximum preferred size of the component is honored.

    Struts and Glue

    The Box class offers the creation of two supporting components, one a strut, or fixed-size filler area, the other glue for an expandable area. The use of these allows you to place components within a container, either a fixed-distance apart with a strut, or a growing/shrinking area based upon available space, with glue. The same task can be done with GridBagConstraints and GridBagLayout, though not as easily.

    To demonstrate, this first program creates a 25 pixel strut between the top two components and a 10 pixel strut between the bottom two.

    import java.awt.*; import javax.swing.*; public class VerticalBoxTest { public static void main(String args[]) { JFrame frame = new JFrame("Vertical Box"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Box box = Box.createVerticalBox(); box.add(new Button("Top")); box.add(Box.createVerticalStrut(25)); box.add(new Button("Middle")); box.add(Box.createVerticalStrut(10)); box.add(new Button("Bottom")); frame.add(box, BorderLayout.CENTER); frame.setSize(300, 200); frame.setVisible(true); } }

    Once you compile and run it, notice how the components change in size when the window size increased or decreased. The distance between the components remain unchanged, to match the reserved strut space. This example uses a <code>Button</code> instead of a JButton to avoid an explanation of component alignment until a little later.


    Working with a horizontal box and glue produces similar results, though this time the glue grows in size to take up added space, instead of staying a fixed size, with the strut.

    import java.awt.*; import javax.swing.*; public class HorizontalBoxTest { public static void main(String args[]) { JFrame frame = new JFrame("Horizontal Box"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Box box = Box.createHorizontalBox(); box.add(Box.createHorizontalGlue()); box.add(new JButton("Left")); box.add(new JButton("Right")); frame.add(box, BorderLayout.NORTH); box = Box.createHorizontalBox(); box.add(new JButton("Left")); box.add(Box.createHorizontalGlue()); box.add(new JButton("Right")); frame.add(box, BorderLayout.CENTER); box = Box.createHorizontalBox(); box.add(new JButton("Left")); box.add(new JButton("Right")); box.add(Box.createHorizontalGlue()); frame.add(box, BorderLayout.SOUTH); frame.setSize(300, 200); frame.setVisible(true); } }


    Trying not to confuse you too much, but the example is back to using JButton components.

    Alignment

    Life gets interesting with Box/BoxLayout when the components within the container are a different size or the height/width of the container is wider than necessary for a vertical box or taller than necessary with a horizontal one. In other words, if you have a tall column, where do components of a different width end up? And, if you have a wide row with components of a different height, how about them?

    This is where the different alignments of a component come into play. Each Swing component has an X-alignment setting and a Y-alignment setting thanks to its get/setAlignmentX() and get/setAlignmentY() methods. The range of each setting is from 0 to 1.0, inclusive, where 0 represents left or top alignment and 1 represents right or bottom alignment, depending upon direction of BoxLayout. There are constants available in the Component class so you don't really need to know what the values are for right and left alignment. However, it does help to know if you might want something in between.

    To demonstrate the right, left, center nature of different size buttons in a vertical box, the following program creates three boxes, one each filled with left, center, and right aligned buttons.

    import java.awt.*; import javax.swing.*; public class AlignX { private static Container makeIt(String labelChar, float alignment) { Box box = Box.createVerticalBox(); for (int i=1; i<6; i++) { String label = makeLabel(labelChar, i*2); JButton button = new JButton(label); button.setAlignmentX(alignment); box.add(button); } return box; } private static String makeLabel(String s, int length) { StringBuffer buff = new StringBuffer(length); for (int i=0; i<length; i++) { buff.append(s); } return buff.toString(); } public static void main(String args[]) { JFrame frame = new JFrame("X Alignment"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container panel1 = makeIt("L", Component.LEFT_ALIGNMENT); Container panel2 = makeIt("C", Component.CENTER_ALIGNMENT); Container panel3 = makeIt("R", Component.RIGHT_ALIGNMENT); frame.setLayout(new FlowLayout()); frame.add(panel1); frame.add(panel2); frame.add(panel3); frame.pack(); frame.setVisible(true); } }

    Now, let us mix things up a little and have one vertical box with three buttons, one for each alignment. The screen width will be wide, to make sure there is extra space available. Conceptually thinking, one would expect the component with left alignment to be aligned to the left of the container and the one with right alignment to be aligned to the right of the container. That would be wrong though. When there are different component alignments, they are aligned to the center of the container. So, for left alignment, that component will have its left edge on the invisible center line of the container. For right alignment, it is the right edge.

    Here's the program to demonstrate:

    import java.awt.*; import javax.swing.*; public class AlignX2 { public static void main(String args[]) { JFrame frame = new JFrame("X Alignment"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Box box = Box.createVerticalBox(); JButton button = new JButton("LL"); button.setAlignmentX(Component.LEFT_ALIGNMENT); box.add(button); button = new JButton("CC"); button.setAlignmentX(Component.CENTER_ALIGNMENT); box.add(button); button = new JButton("RR"); button.setAlignmentX(Component.RIGHT_ALIGNMENT); box.add(button); frame.add(box, BorderLayout.CENTER); frame.setSize(300, 200); frame.setVisible(true);} }

    And, the corroborating screen:

    Working in the other direction has top alignment aligning the top of the component to the imaginary center line, or in other words below the center.

    Mixing up alignments in this fashion works fine, but just takes some getting used to, as the alignment isn't necessarily where you would expect it to be, unless all the alignments are the same, and then it does align to the container border, as opposed to the container center line.

    If you are still confused, feel free to modify the earlier programs and try even more combinations of x and y alignment. Of course, if all this baffles you, there is always GridBagLayout.

    ******

    SDN Chat: Meet the Writers of java.sun.com
    Please join us in Sun's Developer Playground in Second Life on Thursday, February 14 at 10am PST to meet the writers of java.sun.com. Ed Ort, Dana Nourie, Janice Heiss, and Laureen Hudson will be inworld to discuss their adventures in writing for one of the industry's most popular websites and to share the technologies and trends they'll be keeping their eyes on in 2008. And, for the first time, SMI Press is pleased to offer attendees one of three new SMI Press books for free!

    Using Generics With Wildcards and Extends

    posted: 03 Feb 2009

    By John Zukowski

    Java 2 Platform, Standard Edition 5.0 (J2SE 5.0) introduced generics to the Java programming language and platform. In the simplest case and typical usage, generics allow for the identification of what you want to store in a collection. So instead of saying that your program has a List of Objects, you can now specify that you have a List of String objects or some other class type. Then, if you accidentally try to add something to the List that is of the wrong type, the compiler notifies you of the error and it can be fixed at compile time, rather than having to wait until you run the program and the program reaches the point in code where the fetch operation produces a runtime casting exception.

    This brings up a second benefit of generics. Iterators now become typesafe. The next() method of the Iterator interface returns the typesafe version of the next element of the collection.

    But this is not a tip about the use of generics, which a 2005 Core Java Technologies Tip explained. Most people don't fully understand the use of the extends keyword when using generics. A typical example shown to explain the use of extends has to do with drawing shapes. Instead, this tech tip will use an example that uses Swing components so that you do not have to create extra new classes. In a very limited case, the class hierarchy for Swing button components is shown here, with Object as the real root:

    Component |- Container |- JComponent |- AbstractButton |- JButton |- JMenuItem |- JCheckBoxMenuItem |- JMenu |- JRadioButtonMenuItem |- JToggleButton |- JCheckBox |- JRadioButton

    One thing that all AbstractButton subclasses share in common is a getText() method. So in the spirit of generics, you can define a method that takes a List of AbstractButton items and return a List of the String labels of those buttons. Here's the first version of such a method:

    public static List<String> getLabels(List<AbstractButton> list) { List<String> labelList = new ArrayList<String>(list.size()); for (AbstractButton button: list) { labelList.add(button.getText()); } return labelList; }

    And here is how you might use the method. First, define a List of AbstractButton types, fill it up, and call the method:

    List<AbstractButton> buttonList = new ArrayList<AbstractButton>(); buttonList.add(new JButton("Hello")); buttonList.add(new JCheckBox("World")); buttonList.add(new JRadioButton("Hola")); buttonList.add(new JMenuItem("Mundo")); List labels = getLabels(buttonList); System.out.println(labels);

    "Hola, Mundo" is the Spanish translation of "Hello, World," according to Google. The results of the println() call is as follows:

    [Hello, World, Hola, Mundo]

    With a List of AbstractButtons, everything functions fine, but this breaks down when the List is of something else, specifically a subclass. One would logically think that with a List of JButton items, everything would work fine, because JButton is a subclass of AbstractButton. Shouldn't you be able to call getLabels(List<AbstractButton>) with a List of a subclass of AbstractButton?

    List<JButton> buttonList = ... // ... Fill list ... List<String> labels = getLabels(buttonList);

    Well, that isn't the case. Because this is a compile-time check, and because the definition of getLabels() is defined to accept only an AbstractButton List, you cannot pass anything else to it. The compilation-time error message follows:

    GetList.java:13: getLabels(java.util.List<javax.swing.AbstractButton>) in GetList cannot be applied to (java.util.List<javax.swing.JButton>) List<String> labels = getLabels(buttonList); ^ 1 error

    And this is where the extends keyword comes in handy. Instead of defining the getLabels() method to accept only an AbstractButton list, define it to accept any List of AbstractButton subclasses:

    public static List<String> getLabels( List<? extends AbstractButton> list) {

    The wildcard ? here says that the method doesn't care what the exact class type is, as long as it is a subclass of AbstractButton. Here's a complete example that puts all the pieces together:

    import java.util.*; import javax.swing.*; public class GetList { public static void main(String args[]) { List<JButton> buttonList = new ArrayList<JButton>(); buttonList.add(new JButton("Hello")); buttonList.add(new JButton("World")); buttonList.add(new JButton("Hola")); buttonList.add(new JButton("Mundo")); List labels = getLabels(buttonList); System.out.println(labels); } public static List<String> getLabels( List<? extends AbstractButton> list) { List<String> labelList = new ArrayList<String>(list.size()); for (AbstractButton button: list) { labelList.add(button.getText()); } return labelList; } }

    Now, when you are defining your own classes and methods with generics and are thinking of accepting an abstract class as the generic argument, or any superclass, remember to use wildcards so that the same method works best with subclasses too.

    For more information on generics, see two earlier tutorials by Gilad Bracha: a 2004 tutorial (PDF) and the generics lesson in the online Java Tutorial.

    JSlider Appearance Improvements

    posted: 03 Feb 2009

    by John Zukowsi

    The JSlider component is a popular component for selecting a value from a numerical range. While some people might think of a JScrollBar for numerical input, that really serves the purpose of scrolling around a viewport, not data input. By default, the input range of a JSlider is 0 to 100, with an initial value of 50. You can change any of the three defaults, the direction of the scrollbar, and whether tick marks should appear and what to show next to them. You'll look at all of these possible features.

    First off is the basic JSlider, just creating it and adding it to a screen, preserving all the defaults. Nothing fancy here, but builds the basics for what to build on for the remaining examples. The SliderSample program actually has four sliders; two horizontal and two vertical.

    import javax.swing.*; import java.awt.*; public class SliderSample { public static void main(final String args[]) { Runnable runner = new Runnable() { public void run() { JFrame frame = new JFrame("Sample Sliders"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JSlider js1 = new JSlider(); JSlider js2 = new JSlider(); js2.setInverted(true); JSlider js3 = new JSlider(JSlider.VERTICAL); js3.setPaintLabels(true); JSlider js4 = new JSlider(JSlider.VERTICAL); js4.setInverted(true); frame.add(js1, BorderLayout.NORTH); frame.add(js2, BorderLayout.SOUTH); frame.add(js3, BorderLayout.WEST); frame.add(js4, BorderLayout.EAST); frame.setSize(300, 200); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } }

    Compiling and running the program shows a screen with four sliders on it. While you cannot tell from the screen, the ones in the EAST and SOUTH regions of the BorderLayout are actually inverted. So, for the horizontal sliders, 0 is on the left for the top one and 100 is on the left for the inverted bottom one. For the vertical sliders, 0 is on the bottom for the left one and on the top for the inverted right one.

    By default, there is no indicator on the slider for what its value is. Instead, you would just ask the slider its value at some later time, or attach a listener to the control to be told the value upon movement. Ignoring the import lines, here is a listener that reports the value when the user stops dragging the slider knob. The getValueIsAdjusting() method reports true while the user still has the knob selected with the mouse.

    public class SliderChangeListener implements ChangeListener { public void stateChanged(ChangeEvent changeEvent) { Object source = changeEvent.getSource(); JSlider theJSlider = (JSlider)source; if (!theJSlider.getValueIsAdjusting()) { System.out.println ("Slider changed: " + theJSlider.getValue()); } } }

    Not showing a value on the slider makes it somewhat useless, though you can certainly work with it where the exact value doesn't matter and the user can see results based upon relative position. To help in making the slider more useful, you can show tick marks with labels. There are two classes of tick marks, major and minor ones. Major and minor are just classifications for the length and positioning of the lines drawn for the tick mark, where major tick marks are longer than minor ones. To enable tick marks, you have to call setPaintTicks(true), and, say how far apart the tick marks should appear. By default, the spacing is zero, resulting in no tick marks drawn, even when paint ticks is true. Adding the appropriate lines to the earlier program will show tick marks on the top and left sliders, with major ticks every 10 and minor ones at the midpoint between them.

    js1.setPaintTicks(true); js1.setMajorTickSpacing(10); js1.setMinorTickSpacing(5); js3.setPaintTicks(true); js3.setMajorTickSpacing(10); js3.setMinorTickSpacing(5);

    Showing tick marks certainly helps, but showing labels makes things most useful. Adding setPaintLabels(true) calls will show labels at the major tick marks. By default, the labels are their respective cardinal numbers, so with a major tick mark at value 0, the associated label is string "0," and so on for "10,", "20,", all the way to "100."

    Instead of showing the cardinal numbers, you can provide a label table: setLabelTable(Dictionary labels). In the table, you would map integer values to different components to show as the label. In generic-speak, that would be Dictionary<Integer, JComponent>, though the method isn't explicitly defined to require the key-value pair to be such. Typically, the mapping is Integer object to JLabel. As the Swing component set predates the Collections framework, the method takes a Dictionary, not a Map, so remember to use a Hashtable for the label table. Here's one label table setup that shows text roughly every eleven positions. At the ends are colored diamond icons, instead of text, to help show that that are labels not text strings that can go at each position.

    Hashtable<Integer, JComponent> table = new Hashtable<Integer, JComponent>(); table.put(new Integer(0), new JLabel( new DiamondIcon(Color.RED))); table.put(new Integer(11), new JLabel("Eleven")); table.put(new Integer(22), new JLabel("Twenty-Two")); table.put(new Integer(33), new JLabel("Thirty-Three")); table.put(new Integer(44), new JLabel("Fourty-Four")); table.put(new Integer(55), new JLabel("Fifty-Five")); table.put(new Integer(66), new JLabel("Sixty-Six")); table.put(new Integer(77), new JLabel("Seventy-Seven")); table.put(new Integer(88), new JLabel("Eighty-Eight")); table.put(new Integer(100), new JLabel( new DiamondIcon(Color.BLACK))); js4.setLabelTable(table); js4.setPaintLabels(true);

    The definition for the DiamondIcon will be shown at end with the full class definition.

    Notice that the slider in the eastern area of the screen was used for the label table. Had the southern slider been used instead, the label text would overlap. Keep that in mind when you work with slider labels.

    One last thing you can do to affect the display. You can hide the track that the knob slides on. Just call the setPaintTrack() method, with a setting of false. Here, the bottom slider has its track hidden.

    There isn't that much more you can do to customize the look of the JSlider, but as you've seen there is certainly quite a bit you can do. From adding tick marks and labels to hiding the track, you've seen the most significant features. If you want to get really fancy, consider creating a custom UI for the component and show it as a dial instead of a line. Sounds like an idea for a future tip.

    Here's the final and complete source used to generate the samples. You will need to work backwards to remove code sections to return back to the first screen shot.

    import javax.swing.*; import javax.swing.event.*; import java.awt.*; import java.util.Hashtable; public class SliderSample { public static void main(final String args[]) { Runnable runner = new Runnable() { public void run() { JFrame frame = new JFrame("Sample Sliders"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ChangeListener listener = new SliderChangeListener(); JSlider js1 = new JSlider(); js1.setPaintLabels(true); js1.setPaintTicks(true); js1.setMajorTickSpacing(10); js1.setMinorTickSpacing(5); js1.addChangeListener(listener); JSlider js2 = new JSlider(); js2.setInverted(true); js2.setPaintTrack(false); js2.addChangeListener(listener); JSlider js3 = new JSlider(JSlider.VERTICAL); js3.setPaintLabels(true); js3.setPaintTicks(true); js3.setMajorTickSpacing(10); js3.setMinorTickSpacing(5); js3.addChangeListener(listener); JSlider js4 = new JSlider(JSlider.VERTICAL); js4.setInverted(true); Hashtable<Integer, JComponent> table = new Hashtable<Integer, JComponent>(); table.put(new Integer(0), new JLabel( new DiamondIcon(Color.RED))); table.put(new Integer(11), new JLabel("Eleven")); table.put(new Integer(22), new JLabel("Twenty-Two")); table.put(new Integer(33), new JLabel("Thirty-Three")); table.put(new Integer(44), new JLabel("Fourty-Four")); table.put(new Integer(55), new JLabel("Fifty-Five")); table.put(new Integer(66), new JLabel("Sixty-Six")); table.put(new Integer(77), new JLabel("Seventy-Seven")); table.put(new Integer(88), new JLabel("Eighty-Eight")); table.put(new Integer(100), new JLabel( new DiamondIcon(Color.BLACK))); js4.setLabelTable(table); js4.setPaintLabels(true); js4.addChangeListener(listener); frame.add(js1, BorderLayout.NORTH); frame.add(js2, BorderLayout.SOUTH); frame.add(js3, BorderLayout.WEST); frame.add(js4, BorderLayout.EAST); frame.setSize(400, 300); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } public static class SliderChangeListener implements ChangeListener { public void stateChanged(ChangeEvent changeEvent) { Object source = changeEvent.getSource(); JSlider theJSlider = (JSlider)source; if (!theJSlider.getValueIsAdjusting()) { System.out.println ("Slider changed: " + theJSlider.getValue()); } } } public static class DiamondIcon implements Icon { private Color color; private boolean selected; private int width; private int height; private Polygon poly; private static final int DEFAULT_WIDTH = 10; private static final int DEFAULT_HEIGHT = 10; public DiamondIcon(Color color) { this(color, true, DEFAULT_WIDTH, DEFAULT_HEIGHT); } public DiamondIcon(Color color, boolean selected) { this(color, selected, DEFAULT_WIDTH, DEFAULT_HEIGHT); } public DiamondIcon(Color color, boolean selected, int width, int height) { this.color = color; this.selected = selected; this.width = width; this.height = height; initPolygon(); } private void initPolygon() { poly = new Polygon(); int halfWidth = width / 2; int halfHeight = height / 2; poly.addPoint(0, halfHeight); poly.addPoint(halfWidth, 0); poly.addPoint(width, halfHeight); poly.addPoint(halfWidth, height); } public int getIconHeight() { return height; } public int getIconWidth() { return width; } public void paintIcon(Component c, Graphics g, int x, int y) { g.setColor(color); g.translate(x, y); if (selected) { g.fillPolygon(poly); } else { g.drawPolygon(poly); } g.translate(-x, -y); } } }

    Nimbus Look and Feel in Java SE 6 Update 10 Beta

    posted: 03 Feb 2009

    By Ethan Nicholas


    When the venerable Metal look and feel for Swing first debuted, its main aesthetic competition was the Windows 95 interface. Given the state of graphical user interfaces a decade ago, Metal was an attractive and elegant alternative to the other common interfaces of the time.

    The updated Ocean theme in Java SE 5 helped to keep Metal a viable choice up to the present day, but it's time for Swing's cross-platform look and feel to get an overhaul.

    Enter the Nimbus Look and Feel. A brand new, modern look and feel based on Synth, Nimbus provides a polished look to applications which choose to use it. And because Nimbus is drawn entirely using Java 2D vector graphics, rather than static bitmaps, it's tiny (only 56KB!) and can be rendered at arbitrary resolutions.

    Figure 3: SwingSet3 in Metal

     

    Figure 4: SwingSet3 in Nimbus

     

    For compatibility reasons, Metal is still the default Swing look and feel, but updating applications to use Nimbus couldn't be simpler. It only takes a single line of code:

    UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");

    You can also force Nimbus to be the default look and feel by specifying -Dswing.defaultlaf=com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel. on the command line. A more permanent way to set the property is to add

    swing.defaultlaf=com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel

    to the file <JAVA_HOME>/lib/swing.properties. You will have to create the swing.properties file if it does not already exist.

    For further reading about Nimbus, take a look at the Nimbus early access page.

    Source Code Analysis Using Java 6 APIs

    posted: 03 Feb 2009

    by Seema Richard, Deepa Sobhana

    Have you ever thought of how tools like Checkstyle or FindBugs perform a static code analysis, or how Integrated Development Environments (IDEs) like NetBeans or Eclipse execute quick code fixes or find the exact references of a field declared in your code? In many cases, IDEs have their own APIs to parse the source code and generate a standard tree structure, called an Abstract Syntax Tree (AST) or "parse tree," which can be used for deeper analysis of the source elements. The good news is that it is now possible to accomplish the said tasks plus a lot more with the help of three new APIs introduced in Java as part of the Java Standard Edition 6 release. The APIs that might be of interest to developers of Java applications that need to perform source code analysis are the Java Compiler API (JSR 199), the Pluggable Annotation Processing API (JSR 269), and the Compiler Tree API.

    In this article, we explore the features of each of these APIs and go on to develop a simple demo application that verifies certain Java coding rules on a set of source code files supplied as input. This utility also shows the coding violation messages as well as the location of violated source code as output. Consider a simple Java class that overrides the equals() method of the Object class. The coding rule to be verified is that every class that implements the equals() method should also override the hashcode() method with the proper signature. You can see that the TestClass class below does not define the hashcode() method, even though it has the equals() method.

     

    public class TestClass implements Serializable {
    int num;

    @Override
    public boolean equals(Object obj) {
    if (this == obj)
    return true;
    if ((obj == null) || (obj.getClass() != this.getClass()))
    return false;
    TestClass test = (TestClass) obj;
    return num == test.num;
    }
    }

    Let us go on and analyze this class as part of the build process with the help of these three APIs.

    Invoking the Compiler from Code: The Java Compiler API

    We all use the javac command-line tool for compiling Java source files to class files. Then why do we need an API to compile Java files? Well, the answer is quite simple: as the name describes, this new standard API lets us invoke the compiler from our own Java applications; i.e., you can programmatically interact with the compiler and thereby make compilation part of application-level services. Some typical uses of this API are listed below.

    • The compiler API helps application servers to minimize the time taken to deploy applications, for example, by avoiding the overhead of using an external compiler for compiling the servlet sources generated from the JSP pages.

    • Developer tools like IDEs and code analyzers can invoke the compiler from within the editor or build tools that significantly reduce the compile time.

    The Java compiler classes are packaged under the javax.tools package. The ToolProvider class of this package provides a method called getSystemJavaCompiler() that returns an instance of some class that implements the JavaCompiler interface. This compiler instance can be used to create a compilation task that will perform the actual compilation. The Java source files to be compiled will be then passed to the compilation task. For this, the compiler API provides a file manager abstraction called JavaFileManager, which allows Java files to be retrieved from various sources, such as the file system, databases, memory, and so on. In this sample, we use StandardFileManager, a file manager based on java.io.File. The standard file manager can be acquired by calling the getStandardFileManager() method of the JavaCompiler instance. The code snippet for the above-mentioned steps is shown below:

    //Get an instance of java compiler
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

    //Get a new instance of the standard file manager implementation
    StandardJavaFileManager fileManager = compiler.
    getStandardFileManager(null, null, null);

    // Get the list of java file objects, in this case we have only
    // one file, TestClass.java
    Iterable compilationUnits1 =
    fileManager.getJavaFileObjectsFromFiles("TestClass.java");


    A diagnostic listener can be optionally passed to the getStandardFileManager() method to produce diagnostic reports of any non-fatal problems. In this code snippet, we pass null values, since we are not collecting the diagnostics from the tool. For details of the other parameters passed to these methods, please refer to the Java 6 API. The getJavaFileObjectsfromFiles() method of the StandardJavaFileManager returns all the JavaFileObject instances that correspond to the supplied Java source files.

    Read the rest of this article

    Add Logging at Class Load Time with Java Instrumen

    posted: 03 Feb 2009

    by Thorbj�rn Ravn Andersen

    When you're trying to analyze why a program failed, a very valuable piece of information is what the program was actually doing when it failed. In many cases, this can be determined with a stack trace, but frequently that information is not available, or perhaps what you need is information about the data that was being processed at the time of failure.

    Traditionally this means using a logging framework like log4j or the Java Logging API, and then writing and maintaining all necessary log statements manually. This is very tedious and error-prone, and well-suited for automation. Java 5 added the Java Instrumentation mechanism, which allows you to provide "Java agents" that can inspect and modify the byte code of the classes as they are loaded.

    This article will show how to implement such a Java agent, which transparently will add entry and exit logging to all methods in all your classes with the standard Java Logging API. The example used is Hello World:

    public class HelloWorld { public static void main(String args[]) { System.out.println("Hello World"); } }

    And here is the same program with entry and exit log statements added:

    import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; public class LoggingHelloWorld { final static Logger _log = Logger.getLogger(LoggingHelloWorld.class.getName()); public static void main(String args[]) { if (_log.isLoggable(Level.INFO)) { _log.info("> main(args=" + Arrays.asList(args) + ")"); } System.out.println("Hello World"); if (_log.isLoggable(Level.INFO)) { _log.info("< main()"); } } }

    The default logger format generates output similar to:

    2007-12-22 22:08:52 LoggingHelloWorld main INFO: > main(args=[]) Hello World 2007-12-22 22:08:52 LoggingHelloWorld main INFO: < main()

    Note that each log statement is printed on two lines. First, a line with a time stamp, the provided log name, and the method in which the call was made, and then a line with the provided log text.

    The rest of the article will demonstrate how to make the original Hello World program behave like the logging Hello World by manipulating the byte code when it is loaded. The manipulation mechanism is the Java Instrumentation API added in Java 5.

    Read the rest of this article.

    Launch Java Applications from Assembly Language Pr

    posted: 03 Feb 2009

    by Biswajit Sarkar

    Java Native Interface (JNI) is a mechanism that can be used to establish communication between native language programs and the Java virtual machine. The documentation for JNI and the technical literature on JNI deal extensively with interactions between the JVM and C/C++ code. The Java SDK even provides a utility to generate a header file to facilitate calling C/C++ programs from Java code. However, there is hardly any mention of Java and assembly language code working together. In an earlier article I showed how assembly language programs can be called from Java applications. Here I deal with the technique for invoking Java programs from an ASM process through a demo application that calls a Java method from assembly language code. The Java method brings up a Swing JDialog to show that it has, indeed, been launched.

    Why Java with ASM?

    JNI is essential to the implementation of Java, since the JVM needs to interact with the native platform to implement some of its functionality. Apart from that, however, use of Java classes can often be an attractive supplement to applications written in other languages, as Java offers a wide selection of APIs that makes implementation of advanced functions very simple.

    Some time ago, I was associated with an application to collect real-time data from a number of sources and save them in circular buffers so that new data would overwrite old data once the buffer got filled up. If a designated trigger event was sensed through a digital input, a fixed number of data samples would be saved in the buffers so that a snapshot of pre- and post-trigger data would be available. The original application was written in assembly language. After the application was used for a few months, it was felt that it would be very useful to have the application mail the snapshots to authorized supervisors whenever the trigger event occurred. Of course, it would have been possible to write this extension in assembly, but the team felt that in that particular instance it was easier to write that extension in Java and hook it up with the ASM program. As I had earlier worked with ASM-oriented JNI, I knew this could be done and, indeed, the project was implemented quickly and successfully.

    I am sure there are many legacy applications written in assembly language that could benefit from such add-ons. However, it is not only for old applications in need of renovation that JNI can prove useful. Although it may seem unlikely to some of us, assembly language is still used for writing selected portions of new programs. In an article published not very long ago, the author says, "I have found that many of Sun's partners still use assembly language in their products to ensure that hot code paths are as efficient as possible. While compilers are able to generate much more efficient code today, the resulting code still doesn't always compete with hand-coded assembly written by an engineer that knows how to squeeze performance out of each microprocessor instruction. Assembly language remains a powerful tool for optimization, granting the programmer greater control, and with judicious use can enhance performance." Clearly, in such "mixed language" applications the ability to use Java with ASM can be useful.

    Note that the technique shown here can also be used to call Java code from languages other than ASM. If JInvoke is rewritten as a .dll, code written in FORTRAN, for instance, can link to it and call a Java method.

    I have used JNI with legacy ASM code in two ways:

    • Functional enhancement: Mail-enabling an existing ASM application, as mentioned earlier.
    • Interface enhancement: Adding interactive user interface (mostly AWT, but some Swing as well).

    These enhanced applications have run on Windows 2000 and XP. The Java versions used were 1.3, 1.4, and 1.6. In all cases the applications worked smoothly.

    Read the rest of this article . . .

    Distributing a Java Web Start Application via CD-R

    posted: 03 Feb 2009

    by Luan O'Carroll

    Isn't Java Web Start (JWS) supposed to allow web-based distribution of applications? So why would one want to distribute a Java Web Start (JWS) application via CD-ROM? Well, for a number of reasons. For larger applications, a complete installation can still be a major download even with high-speed broadband. Secondly, not all desktops are online, and not all can access the internet (for corporate security reasons, for example). And, lastly, some people just like CDs.

    A client company required that their application be distributed worldwide, including to places where broadband coverage is sparse. The application contains information about a large number of products, including detailed drawings and diagrams. All this information constituted a major part of the application, and a complete installation including the JVM amounts to over 40 MB. In addition to this, the company wanted to be able to distribute the application on CDs at trade fairs and with promotional materials; therefore, a CD-based distribution was required. Normally a CD install is possible using either commercial or open source installers, of which there are many. However, when an application is to run with Java Web Start, it needs to be installed in a specific location and not at the user's discretion, as is the norm for installers.

    This article describes the steps involved in installing an application that installs both from CD and the internet. The installation process requires that:

    1. The installed application must check for updates and integrate with the JWS cache.
    2. The installation should work on a machine without an existing or up-to-date version of Java.
    3. The installed application should not require an internet connection.
    4. A installation must be easy to use and must provide a simple user interface.

    Application installation is normally carried out with a generic installer application, but a traditional install process would effectively create a separate application that is unaware of JWS. Each time an update is released, the user would have to download and install the new version, whereas a JWS application only downloads those components that have been updated, making the process far more efficient and reliable. The article therefore also describes a JWS application installer.

    JWS Primer

    Java Web Start allows Java applications to be launched via a link to a JNLP file. The JNLP file describes the main method or entry point into the application and it references the resources used by the application.

    When a JWS application is launched, the JVM tries to access the required resources, updating them if necessary, and copies the file to its cache. On subsequent attempts to launch the application, JWS can check this cache and skip download of the resources. If the client machine is offline or if the server cannot be contacted, then JWS can run the application in an offline mode.

    If the JWS launch file (the JNLP) were placed on CD, JWS would attempt to contact the server and download any new files. Obviously this would defeat the purpose of distributing the files via CD if the client machine is online. Instead, we need some way to update the JWS cache as though the application had been previously loaded by JWS.

    Updating the JWS Cache

    The Java 5 version of JWS includes a little known -import option that imports a JWS application into the cache from a specified location.

    The CD image at this location is just a copy of what you would normally place on the web server: the JNLP file, plus the .jars and resources referred to by that JNLP file. If you use a servlet to serve up the JNLP, then the CD image will need a self-contained snapshot of the generated JNLP file.

    The CD image can thus be installed into the JWS cache by calling:

    <JAVA_HOME>/jre/bin/javaws -codebase <CACHE_IMAGE> -import <CACHE_IMAGE>/<XXXX>.jnlp

    where <JAVA_HOME> is the root of the (possible new) JVM, <CACHE_IMAGE> is location of the JWS application on the CD, and <XXXX> is the name of the application JNLP file. Later, we will see how this command is automated and wrapped in a simple GUI.

    In installing the cached application, JWS conveniently prompts the user to install desktop and menu shortcuts for starting the application. Once the JWS install has been completed, we can again call JWS to start the newly installed application.

    <JAVA_HOME>/jre/bin/javaws -import <CACHE_IMAGE>/<XXXX>.jnlp

    Again the CD is used, but this time JWS will use the installation referred to by the JNLP file. If the machine is connected to the internet, it will check for updates in the normal way, as part of the process, and then start the application. If there is no network connection, the application will launch as delivered on CD.

    The next time the user starts the application they can use the menu or desktop shortcuts and the CD will no longer be needed. Alternatively, the user can start the application from a link on a web page that points to the same URL/JNLP file combination; i.e., the original version from the website.

    JVM Complications

    One gotcha in all of this is that the above commands require the presence of a JVM, and in some rare cases this may not be installed or may not be available by default on the system path and therefore some extra measures are needed to locate a usable JVM. Furthermore, when a user inserts the CD, the installation should begin, and the installation should check for the presence of an existing JVM. The process of checking for a JVM is then as follows:

    1. Check for a JVM (for the installer).
    2. Install the JVM if not present.
    3. Launch the installer, showing the usual license information.
    4. Install the target JVM (if required by the application and different from 1 above).
    5. Import the JWS cache.
    6. Start the JWS application.

    Some further complications arise from the fact that the minimum JVM for the JWS -import option is Java 5, so even if a JVM is present and usable for the application, this import option will still require a fairly recent JVM. Secondly, the import process takes some time and must complete before the application is launched, and this sort of delayed execution is difficult to achieve with many of the normal installers.

    Given these complications, it was necessary to build a custom launch application that could perform these steps.

    Read the rest of the article.

    Where's the State?

    posted: 03 Feb 2009

     from Tim Boudreau's Blog

     Where's the state? This is a small but useful question when deciding how a problem domain gets carved up into objects: What things have state? What things have values that can change? When and how can they change? Can the changes be observed? Who needs to observe changes in state?

    These questions make a good start for figuring out how to carve up a problem domain into objects, if you observe the principle keep all related state in one place. I would go so far as to say that the majority of the appeal of Model-View-Controller (MVC) architecture is that it encourages you to keep state in one place.

    One big reason why statefulness matters is threading. In practice, it is common to see designs where threading was an afterthought. This is to be expected. Concurrency is a not natural way for human beings to think. So commonly a library is designed with no thought about threading; then a problem shows up which multi-threading can solve. Threading ends up being introduced into a codebase that was never designed for it. Retrofitting a threading model on something designed without one is very painful and sometimes impossible.

    Some threading models can be simple. For example, any time you are writing a Swing application, and you have a method that can do long running I/O, the first line of that method should be

    assert !EventQueue.isDispatchThread();

    under all circumstances.

    But keeping I/O off the event thread is the trivial case. Managing mutations to a model inside an application is a more complicated, less obvious problem.

    Any time you find yourself writing a setter (or more generally, mutator) method, it is worth asking yourself Who does this piece of state really belong to? Am I putting state in the right place? Blindly following the beans pattern (add a getter/setter to whatever type you happen to be editing) can result in state being distributed all over objects in an application. The result will be harder to maintain, and if it has any lifespan, less and less maintainable over time. Like entropy, or the no broken windows theory, the more state is distributed all over the place, the more that will masquerade as design and make it okay for a programmer to further diffuse state throughout objects in the application. Human beings can only hold so many things in their minds at a time. The more state is diffused, the more things someone must pay attention to simultaneously to solve a problem in that code.

    Read the rest of this blog . . .

    Exchanging Data with XML and JAXB, Part 1

    posted: 03 Feb 2009

    by Jennie Hall

    In this tip, you'll learn how to to use the Java Architecture for XML Binding (JAXB) in Java SE 6 to exchange XML data between systems without having to delve into the specifics of XML processing. Among other key improvements, JAXB 2.0 offers support for binding Java objects to XML via the use of annotations. This feature allows you to generate XML data from your application simply by annotating your existing object model.

    What's JAXB?

    JAXB simplifies the use of XML data in Java applications by shielding you and your code from the low-level details of working with XML. Essentially, JAXB allows you to move easily back-and-forth between XML and Java. A JAXB implementation supplies a schema compiler, which takes in an XML schema and generates Java classes which map to that schema. Data from XML documents which are instances of the schema can be bound automatically to the generated classes by JAXB's binding runtime framework. This is called unmarshalling. Once unmarshalled, content can be manipulated or modified in Java as needed. JAXB can also write (marshal) data from Java objects to an XML instance document. JAXB optionally performs validation of content as part of these operations.

    In addition to the schema compiler, a JAXB implementation also provides a schema generator, which looks at annotations on Java classes and generates a corresponding XML schema. This feature is new to JAXB 2.0.

    The Sample Application

    A veterinary office wants to send notices to its clients reminding them of their pets' upcoming appointments. Because they are nice folks, they also like to send a birthday card to an owner on the pet's birthday. The veterinary office, NiceVet, contracts with a service provider, WePrintStuff, to do their printing and mailing for them.

    NiceVet already has an application which maintains information about the animals under NiceVet's care and their owners. Here's a diagram of NiceVet's existing object model:

    NiceVet needs a way to get the necessary notification data to WePrintStuff for processing without spending a lot of time and money modifying their current application.

    Generating the XML

    It's time to annotate our object model so we can generate some XML data for the printing and mailing service. Looking at NiceVet's object model, we see that the PrintOrder class holds the information about upcoming events, and thus makes sense as the root element of the data we want to send. Let's add the @XmlRootElement annotation to the PrintOrder class. This will establish printOrder as the root element of our generated XML.

    import javax.xml.bind.annotation.XmlRootElement; // the root element of our generated XML @XmlRootElement(name = "printOrder") public class PrintOrder { private long id; private List<Event> events;

    Let's add some more annotations:

    import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; // the root element of our generated XML @XmlRootElement(name = "printOrder") // maps this class to a generated schema type @XmlType(name = "PrintOrderType", propOrder = {"events"}) // bind all non-static, non-transient fields // to XML unless annotated with @XmlTransient @XmlAccessorType(XmlAccessType.FIELD) public class PrintOrder { // maps this field to an XML attribute @XmlAttribute(required = true) private long id; // custom adapter maps this field to a type, // NotificationsType, which makes it easy to // generate the desired XML @XmlJavaTypeAdapter(value=EventAdapter.class) @XmlElement(name = "notifications") private List<Event> events;

    @XmlType maps the PrintOrder class to a generated schema type named PrintOrderType. The @XmlAccessorType(XmlAccessType.FIELD) annotation tells JAXB to bind all non-static, non-transient fields to XML unless annotated with @XmlTransient, which prevents the mapping of a field or property. With the XmlAccessType.FIELD setting, getter/setter pairs are not bound to XML unless explicitly annotated. The default setting is XmlAccessType.PUBLIC_MEMBER (all public fields and getter/setter pairs are bound unless otherwise annotated); other settings are XmlAccessType.PROPERTY and XmlAccessType.NONE.

    Moving down the code listing, we see that the field id maps to an XML attribute on the PrintOrderType element. Some kind of identifier on the print order is required, and we've annotated the id field accordingly.

    The PrintOrder class has a list of events which contains both appointments and birthdays. The annotation @XmlJavaTypeAdapter tells JAXB that we are going to use a custom adapter, EventAdapter, to help us map this Java construct to XML. To support this annotation, we need to write an adapter class which extends XmlAdapter and overrides its abstract marshal and unmarshal methods.

    public class EventAdapter extends XmlAdapter<NotificationsType, List<Event>> { // adapt original Java construct to a type, NotificationsType, // which we can easily map to the XML output we want public NotificationsType marshal(List<Event> events) throws Exception { List<Appointment> appointments = new ArrayList<Appointment>(); List<Birthday> birthdays = new ArrayList<Birthday>(); for (Event e : events) { if (e instanceof Appointment) { appointments.add((Appointment)e); } else { birthdays.add((Birthday)e); } } return new NotificationsType(appointments, birthdays); } // reverse operation: map XML type to Java public List<Event> unmarshal(NotificationsType notifications) throws Exception { ... }

    EventAdapter's marshal method takes the original Java construct, List<Event> events, and converts it to a type which maps more easily to the XML output we want. Let's take a look at NotificationsType:

    import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlElement; import java.util.List; public class NotificationsType { // produce a wrapper XML element around this collection @XmlElementWrapper(name = "appointments") // maps each member of this list to an XML element named appointment @XmlElement(name = "appointment") //private List<Appointment> appointments; private List<Appointment> appointments; // produce a wrapper XML element around this collection @XmlElementWrapper(name = "birthdays") // maps each member of this list to an XML element named birthday @XmlElement(name = "birthday") //private List<Birthday> birthdays; private List<Birthday> birthdays; public NotificationsType() {} public NotificationsType(List<Appointment> appointments, List<Birthday> birthdays) { this.appointments = appointments; this.birthdays = birthdays; }

    The @XmlElementWrapper annotation creates a wrapper XML element around each collection, and the @XmlElement annotation maps each member of the collection to an XML element of the appropriate type. Combined with the @XmlElement(name = "notifications") annotation on PrintOrder.events, what we end up with will look something like this:

    <printOrder> <notifications> <appointments> <appointment> ... </appointment> </appointments> <birthdays> <birthday> ... </birthday> </birthdays> </notifications> </printOrder>

    Another annotation worth mentioning lies in the abstract base class Event (listing below), from which subclasses Appointment and Birthday inherit. This inheritance hierarchy won't hold much meaning for our service provider, WePrintStuff, so we'll eliminate it with the @XmlTransient annotation on Event. We still need fields owner and pet, however, so we annotate them with @XmlElement(required = true).

    import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlTransient; // no need to capture this inheritance hierarchy in XML @XmlTransient public abstract class Event { @XmlElement(required = true) private Owner owner; @XmlElement(required = true) private Pet pet; public Event() {} public Event(Owner owner, Pet pet) { this.owner = owner; this.pet = pet; }

    When objects of type Birthday and Appointment are marshalled to XML, they will include all the relevant data from their supertype, Event. Take a look at the listing below for the Birthday class:

    @XmlType(name = "BirthdayType", propOrder = {"age", "birthday", "owner", "pet"}) @XmlAccessorType(XmlAccessType.FIELD) public class Birthday extends Event { @XmlElement(required = true) private int age; public Birthday() {} public Birthday(Owner owner, Pet pet) { super(owner, pet); this.age = this.calculateAge(); } @XmlElement(name = "birthday", required = true) public Date getBirthday() { return this.getPet().getDateOfBirth(); }

    @XmlType.propOrder specifies the order in which content is marshalled and unmarshalled. This order would otherwise be unspecified due to the nature of Java reflection. Alternatively, you can impose an order with the @XmlAccessorOrder annotation. As mentioned in the Java Tutorial, imposing an order improves application portability across JAXB implementations.

    Although the field dateOfBirth is actually on the Pet class, it makes more sense in terms of our XML representation if this data is organized as an element under the birthday element. We can achieve this without a dateOfBirth field on the Birthday class by annotating the getBirthday() method, which delegates to Pet.

    On a related note, WePrintStuff doesn't need to do any additional processing with the dates NiceVet sends them; they just need to locate the correct notification template and print the information out. As we saw earlier with the events list on the PrintOrder class, we can supply a custom adapter to get the desired XML output. This time, however, we'll specify the adapter through a package-level annotation. The adapter will take in all java.util.Date instances and output nicely formatted Strings. The package-level annotation goes in the package-info.java file (located in the vet package):

    // every java.util.Date class in the vet package should be // processed by DateAdapter @XmlJavaTypeAdapter(value=DateAdapter.class, type=Date.class) package vet; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import java.util.Date;

    Although JAXB can handle marshalling java.util.Date to XML in a default manner on its own, we're telling JAXB to use an alternative, customized mapping which better suits our purposes. We implement the adapter in much the same way as above:

    import java.util.Date; import java.text.SimpleDateFormat; import javax.xml.bind.annotation.adapters.XmlAdapter; public class DateAdapter extends XmlAdapter<String, Date> { // the desired format private String pattern = "MM/dd/yyyy"; public String marshal(Date date) throws Exception { return new SimpleDateFormat(pattern).format(date); } public Date unmarshal(String dateString) throws Exception { return new SimpleDateFormat(pattern).parse(dateString); }

    The annotations on the other classes in the object model are similar to what we've seen so far; take a peek at the sample code included with this tip to see exactly what's going on in the other classes.

    We've finished annotating NiceVet's object model and we're ready to generate some XML. If we run the sample application, we should see an XML instance document something like the following listing. The document, niceVet.xml, is located in the root directory of the java-to-schema project.

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <printOrder id="1218226109781"> <notifications> <appointments> <appointment> <apptType>Yearly Checkup</apptType> <apptDate>09/15/2008</apptDate> <owner> <firstName>Joe</firstName> <lastName>Outdoors</lastName> <address> <addressLine1>123 Whitewater Street</addressLine1> <city>OurTown</city> <state>CA</state> <zip>90347</zip> <zipExt>1234</zipExt> </address> </owner> <pet> <name>Honcho</name> <species>Dog</species> </pet> </appointment> <appointment> <apptType>Well Mom Exam</apptType> <apptDate>09/12/2008</apptDate> <owner> <firstName>Missy</firstName> <lastName>Fairchild</lastName> <address> <addressLine1>456 Scenic Drive</addressLine1> <city>West OurTown</city> <state>CA</state> <zip>90349</zip> <zipExt>6789</zipExt> </address> </owner> <pet> <name>Miss Kitty</name> <species>Cat</species> </pet> </appointment> </appointments> <birthdays> <birthday> <age>7</age> <birthday>09/07/2000</birthday> <owner> <firstName>Violet</firstName> <lastName>Flowers</lastName> <address> <addressLine1>22375 Willow Court</addressLine1> <city>West OurTown</city> <state>CA</state> <zip>90349</zip> <zipExt>6789</zipExt> </address> </owner> <pet> <name>Tom</name> <species>Cat</species> </pet> </birthday> </birthdays> </notifications> </printOrder>

    The generated schema for the XML instance document above is named schema1.xsd, and it lives in /generated/schema under the root directory of the java-to-schema project. It looks like this:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="printOrder" type="PrintOrderType"/> <xs:complexType name="PrintOrderType"> <xs:sequence> <xs:element name="notifications" type="notificationsType" minOccurs="0"/> </xs:sequence> <xs:attribute name="id" type="xs:long" use="required"/> </xs:complexType> <xs:complexType name="notificationsType"> <xs:sequence> <xs:element name="appointments" minOccurs="0"> <xs:complexType> <xs:sequence> <xs:element name="appointment" type="AppointmentType" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="birthdays" minOccurs="0"> <xs:complexType> <xs:sequence> <xs:element name="birthday" type="BirthdayType" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> <xs:complexType name="AppointmentType"> <xs:sequence> <xs:element name="apptType" type="xs:string"/> <xs:element name="apptDate" type="xs:string"/> <xs:element name="owner" type="OwnerType"/> <xs:element name="pet" type="PetType"/> </xs:sequence> </xs:complexType> <xs:complexType name="OwnerType"> <xs:sequence> <xs:element name="firstName" type="xs:string"/> <xs:element name="lastName" type="xs:string"/> <xs:element name="address" type="AddressType"/> </xs:sequence> </xs:complexType> <xs:complexType name="AddressType"> <xs:sequence> <xs:element name="addressLine1" type="xs:string"/> <xs:element name="addressLine2" type="xs:string" minOccurs="0"/> <xs:element name="city" type="xs:string"/> <xs:element name="state" type="xs:string"/> <xs:element name="zip" type="xs:string"/> <xs:element name="zipExt" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType> <xs:complexType name="PetType"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="species" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType> <xs:complexType name="BirthdayType"> <xs:sequence> <xs:element name="age" type="xs:int"/> <xs:element name="birthday" type="xs:string"/> <xs:element name="owner" type="OwnerType"/> <xs:element name="pet" type="PetType"/> </xs:sequence> </xs:complexType> </xs:schema>

    If we take a quick peek at the main() method, we can see that we are generating the schema as follows:

    // specify where the generated XML schema will be created final File dir = new File("generated" + File.separator + "schema"); // specify a name for the generated XML instance document OutputStream os = new FileOutputStream("niceVet.xml"); // create a JAXBContext for the PrintOrder class JAXBContext ctx = JAXBContext.newInstance(PrintOrder.class); // generate an XML schema from the annotated object model; create it // in the dir specified earlier under the default name, schema1.xsd ctx.generateSchema(new SchemaOutputResolver() { @Override public Result createOutput(String namespaceUri, String schemaName) throws IOException { return new StreamResult(new File(dir, schemaName)); } });

    To get the XML instance document, we first create some data, then we obtain a Marshaller from the JAXBContext and marshal the Java content tree to XML:

    // create some data PrintOrder order = new PrintOrder(EventUtil.getEvents()); ... // create a marshaller Marshaller marshaller = ctx.createMarshaller(); // the property JAXB_FORMATTED_OUTPUT specifies whether or not the // marshalled XML data is formatted with linefeeds and indentation marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // marshal the data in the Java content tree // to the XML instance document niceVet.xml marshaller.marshal(order, os);

    Running the Sample Application


    To run the sample application, download the sample code and unzip it. Launch NetBeans and select File -> Open Project. In the Open Project dialog box, navigate to the directory where you unzipped the sample code and select the folder java-to-schema. Select the Open as Main Project check box. Click Open Project Folder. Right-click the java-to-schema project and select Run Project.

    Conclusion

    In this tip, you've seen how JAXB makes it easy to generate XML data to exchange with a business partner. The XML data your partner receives, however, may need a little tweaking in order to mesh with their system. In a future tip, you'll learn how to apply customizations to a supplied schema to get the Java object model - and content tree - you want.

    References and Resources

  • Sample code for this tip
  • The Java Tutorial.
  • Kohsuke Kawaguchi's Blog. Kohsuke Kawaguchi is a staff engineer at Sun Microsystems, where among other projects he works on JAXB.
  • Jennie Hall is a lead developer working in the financial sector.

    Exchanging Data With XML and JAXB, Part 2

    posted: 03 Feb 2009

    by Jennie Hall

    In Exchanging Data With XML and JAXB, Part 1, we saw how the Java Architecture for XML Binding (JAXB) expedites the exchange of data between systems. With JAXB 2.0's annotation support, generating XML data for a business partner is as easy as annotating your existing object model. 

    In this tip, you'll learn how JAXB's binding customization features can facilitate XML processing for the recipient of data as well as for the sender. Binding customization offers a level of control over the characteristics of the Java classes generated by the JAXB schema compiler. This allows you to work with an object model that makes sense for your business domain even when processing XML instance documents based on a schema that is not of your own design.

    For a brief overview of the Java Architecture for XML Binding (JAXB), see the "What's JAXB?" section of Exchanging Data With XML and JAXB, Part 1.

    The Sample Application

    Let's continue with our scenario from Part 1.A veterinary office, NiceVet, wants to send upcoming appointment reminders and pet birthday cards to its clients. NiceVet contracts with a service provider, WePrintStuff, to do the printing and mailing. In Part 1, we assembled some data for WePrintStuff by annotating NiceVet's existing object model and then using JAXB to marshal application data from Java objects to an XML instance document. We also used JAXB's schema generator to produce a corresponding schema from the annotated Java classes.

    On the receiving end, WePrintStuff will run the JAXB schema compiler against the source schema to generate the schema-derived classes. JAXB will create instances of these classes as it unmarshals NiceVet's data from the XML instance document and binds it into a Java content tree. In some cases, however, the default manner in which JAXB generates the schema-derived classes and binds the data is not exactly what is needed. JAXB's binding customization features provide us with some nice ways to handle many of these cases. We'll take a look at some of them in the next section.

    Customizing the Source Schema

    We can make some customizations right away to make life easier at WePrintStuff. For example, we would like to work with meaningful package and class names and avoid any naming collisions. To accomplish this, we can add custom binding declarations to the source schema itself -- this is known as inline customization.

    Here are the first few lines of the annotated schema:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" <!-- JAXB namespace declaration required for inline customization --> xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" jxb:extensionBindingPrefixes="xjc" <!-- JAXB version number required for inline customization --> jxb:version="2.0"> <xs:annotation> <xs:appinfo> <!-- All collections in this schema will use implementation class java.util.ArrayList. --> <jxb:globalBindings collectionType="java.util.ArrayList"/> <jxb:schemaBindings> <!-- Specify the package for the generated classes; avoid naming collisions. --> <jxb:package name="weprintstuff.generated"> <!-- Specify some Javadoc that describes our package and its uses. --> <jxb:javadoc><![CDATA[<body> The package weprintstuff.generated contains the schema-derived classes that make up the object model used to process print client orders.</body>]]> </jxb:javadoc> </jxb:package> </jxb:schemaBindings> </xs:appinfo> </xs:annotation>

    To enable inline customization, we must include a JAXB namespace declaration and the JAXB version number. The JAXB namespace prefix we'll use in our sample application is jxb:. We can override the default JAXB bindings at different scopes. Lower-level, more fine-grained scopes inherit declarations made at higher-level scopes, but binding declarations made at lower-level scopes can override these inherited declarations. The scopes in order from highest to lowest level are global, schema, definition, and component.

    In the preceding example, we've made a declaration at global scope that specifies that all collections in this schema will use the implementation class java.util.ArrayList. If our source schema imported another schema, this declaration would apply to the second schema as well. There can be only one <globalBindings> declaration per schema. This declaration is valid in the top-level schema only.

    At schema scope, we've specified the package into which the JAXB schema compiler will generate the schema-derived classes. We've also included some package-level Javadoc that describes our package and its uses. Note that we've wrapped our custom binding declarations in <annotation><appinfo> tags.

    Now that we have the package we want, let's see if we can get some class names that better reflect Java naming conventions and WePrintStuff's business domain. The default bindings for the original schema would generate classes with names like ClassAType, ClassBType, and so on.This is not how we typically name classes in the Java programming language, so let's fix it:

    <xs:element name="printOrder" type="PrintOrderType"/> <xs:complexType name="PrintOrderType"> <xs:annotation> <xs:appinfo> <!-- Name the generated class PrintOrder rather than PrintOrderType. --> <jxb:class name="PrintOrder"> <!-- Provide some Javadoc for PrintOrder. --> <jxb:javadoc><![CDATA[<code>PrintOrder</code> javadoc goes here]]> </jxb:javadoc> </jxb:class> </xs:appinfo> </xs:annotation> <xs:sequence> <xs:element name="notifications" type="notificationsType" minOccurs="0"> <xs:annotation> <xs:appinfo> <!-- PrintOrder's notifications property becomes printItems. --> <jxb:property name="printItems"/> </xs:appinfo> </xs:annotation> </xs:element> </xs:sequence> ... </xs:complexType> <xs:complexType name="notificationsType"> <xs:annotation> <xs:appinfo> <!-- Name the generated class PrintItems rather than NotificationsType. --> <jxb:class name="printItems" /> </xs:appinfo> </xs:annotation> ... </xs:complexType>

    PrintOrderType becomes PrintOrder, NotificationsType becomes PrintItems, and so on. Dealing with class names is straightforward, but unfortunately we're stuck with a class structure that is less than ideal. There are too many wrappers around the data, which results in a long chain of method calls. Here's what the calls would have looked like with classes derived from the original schema. As you can see, methods with singular method names return collections, which is misleading.

    List<AppointmentType> appointments = printOrder.getNotifications().getAppointments().getAppointment(); List<BirthdayType> birthdays = printOrder.getNotifications().getBirthdays().getBirthday();

    The following improved calls show the new names, which say what they mean and are more relevant to WePrintStuff's business domain.

    List<Appointment> appointments = printOrder.getPrintItems().getAppointmentHolder().getAppointments(); List<Birthday> birthdays = printOrder.getPrintItem().getBirthdayHolder().getBirthdays();

    I tried to solve some of the class structure issues by using the enhanced <jxb:javaType> customization (<xjc:javaType>) and an adapter class derived from XmlAdapter, but JAXB currently does not support this customization for XML complex types. This issue is documented in the JAXB issue tracker as issue number 209. Meanwhile, we'll look at an example of the enhanced <jxb:javaType> customization used with an XML simple type in the upcoming paragraphs.

    Moving down the source schema, we see that each print order received will have an ID of type long. We know, however, that WePrintStuff employs a natural key strategy in its persistent store. For print-order records, the key consists of a client name and an order ID. WePrintStuff uses the class PrintOrderKey to encapsulate this key information. We'll apply a binding customization that causes the JAXB schema compiler to use an adapter class, IdAdapter, to replace the original print-order ID with WePrintStuff's PrintOrderKey class. Take a look:

    <xs:element name="printOrder" type="PrintOrderType"/> <xs:complexType name="PrintOrderType"> <xs:sequence> <xs:element name="notifications" type="notificationsType" minOccurs="0"/> </xs:sequence> <xs:attribute name="id" type="xs:long" use="required"> <xs:annotation> <xs:appinfo> <!-- Use an XmlAdapter-based adapter to create WePrintStuff's PrintOrderKey class. --> <!-- Specify the name orderKey for this property. --> <jxb:property name="orderKey"> <jxb:baseType> <!-- Specify weprintstuff.print.PrintOrderKey as the type of the orderKey property. --> <!-- Specify the adapter class that will map the schema type to the Java type. --> <xjc:javaType name="weprintstuff.print.PrintOrderKey" adapter="weprintstuff.print.IdAdapter"/> </jxb:baseType> </jxb:property> </xs:appinfo> </xs:annotation> </xs:attribute> </xs:complexType>

    We've changed the name of the print-order property to orderKey and specified the types of the adapter and the order key. During the unmarshalling process, JAXB calls IdAdapter's unmarshal() method, which receives the value of id and incorporates it into a new PrintOrderKey containing the order ID and client name. Here's the code for IdAdapter:

    public class IdAdapter extends XmlAdapter<String, PrintOrderKey> { // When marshalling a Java content tree to an XML instance document, // move from the type that we work with in Java (PrintOrderKey) // to the type that JAXB understands. public String marshal(PrintOrderKey key) throws Exception { return key.getOrderId().toString(); } // When unmarshalling an XML instance document to a Java content tree, // move from the type that JAXB understands (String) to the type // we want to work with in Java technology. public PrintOrderKey unmarshal(String id) throws Exception { // WePrintStuff uses natural keys. Add client name and // convert String ID to required Long. return new PrintOrderKey("NICEVET", new Long(id)); } }

    The <xjc:javaType> customization we've just discussed is an example of a vendor extension. The JAXB reference implementation (RI) offers additional customizations that are not part of the JAXB specification. To use these extensions, we must include a declaration for the JAXB RI vendor extension namespace and specify a namespace prefix:

    <xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" <!-- JAXB RI vendor extension namespace declaration is required. --> xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" <!-- JAXB RI vendor extension namespace prefix is required. --> jxb:extensionBindingPrefixes="xjc" jxb:version="2.0">

    Finally, vendor extensions require that we run the schema compiler with the -extension switch. For an example of the standard <jxb:javaType> customization, which is not a vendor extension, check out niceVet.xsd and weprintstuff.print.CustomDataTypeConverter. These files are included with the sample code that comes with this tech tip. In our example, we've used the <jxb:javaType> customization to convert XML dateTime types to nicely formatted String dates ready for output.

    Things are looking better, but we'd also like to handle the printing of appointment reminders and birthday cards as polymorphically as we can. Take a look at the following:

    <xs:complexType name="AppointmentType"> <xs:annotation> <xs:appinfo> <!-- Name has generated class Appointment rather than AppointmentType. --> <jxb:class name="Appointment"> <jxb:javadoc><![CDATA[<code>Appointment</code> javadoc goes here]]> </jxb:javadoc> </jxb:class> </xs:appinfo> </xs:annotation> <xs:complexContent> <!-- XML Schema extension element causes AppointmentType to derive from printItemType, a type we've added to the schema. --> <xs:extension base="printItemType"> <xs:sequence> ... </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> ... <!-- Add this complex type to the schema so that Appointment class now derives from PrintItem, a base class that we've implemented. --> <xs:complexType name="printItemType"> <xs:annotation> <xs:appinfo> <!-- Due to this customization, JAXB will not generate a class for printItemType; it will use the PrintItem class that we've written. --> <jxb:class ref="weprintstuff.print.PrintItem"/> </xs:appinfo> </xs:annotation> </xs:complexType>

    Fortunately for us, we can modify the structure of the source schema somewhat while retaining the schema's ability to validate the same set of XML instance documents. In this case, we have added a totally new complex type, printItemType, to the schema. Then we modified the complex type AppointmentType with the XML Schema extension element, causing AppointmentType to derive from printItemType.

    At this point, we applied a JAXB customization to printItemType that directs the schema compiler to use the specified class that we've written rather than generating one. Because we've implemented PrintItem ourselves, we can include behavior: specifically, a print() method that allows us to handle subclasses, such as Appointment and Birthday, in a more polymorphic manner. Here's the code for PrintItem:

    package weprintstuff.print; import java.util.*; public abstract class PrintItem { private static final Map<String,Printer> printers; static { printers = new HashMap<String,Printer>(); printers.put("weprintstuff.generated.Birthday", new BirthdayPrinter()); printers.put("weprintstuff.generated.Appointment", new AppointmentPrinter()); } public void print() { Printer p = this.getPrinter(); p.print(this); } private Printer getPrinter() { return printers.get(this.getClass().getName()); } }

    The Printer interface has a single method:

    public void print(PrintItem item);

    Although we're still hampered by the structure of the schema-derived classes as discussed earlier, the code to print the items in the print order is now pretty clean. Here's an excerpt from our main() method:

    // Unmarshal the data in the XML instance document to a Java content tree // made up of instances of the schema-derived classes. JAXBElement printOrderElement = (JAXBElement)unmarshaller.unmarshal(new FileInputStream(input)); PrintOrder printOrder = (PrintOrder)printOrderElement.getValue(); List<Appointment> appointments = printOrder.getPrintItems().getAppointmentHolder().getAppointments(); for (Appointment a : appointments) { a.print(); } List<Birthday> birthdays = printOrder.getPrintItems().getBirthdayHolder().getBirthdays(); for (Birthday b : birthdays) { b.print(); }

    Another nice customization feature that JAXB provides is the ability to map XML simple types to typesafe enumerations. This is convenient because WePrintStuff currently has printing templates for certain types of appointments only. We'll modify the schema to include the supported appointment types and add a customization that causes the schema compiler to map the elements of ApptType to a Java typesafe enum class. XML instance documents with appointment types that WePrintStuff does not support will be rejected:

    <xs:simpleType name="ApptType"> <xs:annotation> <xs:appinfo> <!-- Map the elements of this simple type to a Java typesafe enum class. --> <jxb:typesafeEnumClass/> </xs:appinfo> </xs:annotation> <!-- Use XML Schema elements restriction and enumeration to define the supported appointment types. --> <xs:restriction base="xs:string"> <xs:enumeration value="Yearly Checkup"/> <xs:enumeration value="Well Mom Exam"/> <xs:enumeration value="Teeth Cleaning"/> <xs:enumeration value="Vaccination"/> <xs:enumeration value="Senior Pet Checkup"/> </xs:restriction> </xs:simpleType>

    Here is the resultant typesafe enum class, ApptType:

    package weprintstuff.generated; import javax.xml.bind.annotation.XmlEnum; import javax.xml.bind.annotation.XmlEnumValue; import javax.xml.bind.annotation.XmlType; @XmlType(name = "ApptType") @XmlEnum public enum ApptType { @XmlEnumValue("Yearly Checkup") YEARLY_CHECKUP("Yearly Checkup"), @XmlEnumValue("Well Mom Exam") WELL_MOM_EXAM("Well Mom Exam"), @XmlEnumValue("Teeth Cleaning") TEETH_CLEANING("Teeth Cleaning"), @XmlEnumValue("Vaccination") VACCINATION("Vaccination"), @XmlEnumValue("Senior Pet Checkup") SENIOR_PET_CHECKUP("Senior Pet Checkup"); private final String value; ApptType(String v) { value = v; } public String value() { return value; } public static ApptType fromValue(String v) { for (ApptType c: ApptType.values()) { if (c.value.equals(v)) { return c; } } throw new IllegalArgumentException(v); } }

    So far we've made what are known as inline binding customizations, but JAXB also accepts customizations in the form of one or more external binding customization files. Inline and external customizations can be used in combination, but they cannot be used together on the same element. External binding files are useful when you cannot modify a given schema or when you want to reuse customizations across schemas. For example, WePrintStuff has its own class for U.S. addresses, DomesticAddress, and WePrintStuff would like to use this class to represent the concept of an address throughout the system.

    Here's the external binding file, binding.xjb:

    <!-- XML Schema namespace is required, as are the JAXB namespace and version. --> <jxb:bindings version="2.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <!-- Specify the schema name and root schema node. --> <jxb:bindings schemaLocation="niceVet.xsd" node="/xs:schema"> <!-- Specify the schema node to which this customization should apply with an XPath expression. --> <jxb:bindings node="//xs:complexType[@name='AddressType']"> <!-- Direct the schema compiler to use the DomesticAddress class rather than generating a class for complex type AddressType. --> <jxb:class ref="weprintstuff.print.DomesticAddress"/> </jxb:bindings> <!-- node="//xs:complexType[@name='AddressType']" --> </jxb:bindings> <!-- schemaLocation="niceVet.xsd" node="/xs:schema" --> </jxb:bindings>

    The binding customization file is just an ASCII text file. A valid binding file must specify the schema name and node. We identify nodes using XPath expressions. In the previous listing, we've applied a customization that directs the schema compiler to use WePrintStuff's DomesticAddress class rather than generating a class for the complex type AddressType. This is the same customization illustrated earlier in our PrintItem base class example, although in that situation, we declared the customization inline. The syntax for supplying a binding customization file to the schema compiler is the following:

    xjc -b bindings schema

    We can apply multiple binding files to one schema, one binding file to multiple schemas, or multiple binding files to multiple schemas. Each binding file must have its own -b switch.

    Generating the Schema-Derived Classes

    We generate our schema-derived classes at the command line with the following command to the schema compiler, xjc:

    xjc -d ./src -b binding.xjb -extension niceVet.xsd

    The schema compiler informs us that it has generated our classes with the following output:

    parsing a schema... compiling a schema... weprintstuff\generated\Adapter1.java weprintstuff\generated\Adapter2.java weprintstuff\generated\Appointment.java weprintstuff\generated\ApptType.java weprintstuff\generated\Birthday.java weprintstuff\generated\ObjectFactory.java weprintstuff\generated\Owner.java weprintstuff\generated\Pet.java weprintstuff\generated\PrintItems.java weprintstuff\generated\PrintOrder.java weprintstuff\generated\package.html

    The JAXB schema compiler has a number of options. For example, it will mark generated classes as read only in response to the -readOnly switch. Invoke xjc with no options or with the -help switch for more information.

    The Payoff

    We've modified our source schema, made our customizations, and generated our schema-derived classes. All that's left to do is run our printing program, let JAXB bind the XML data supplied by NiceVet, and print out the appointment reminders and birthday cards. We'll take a look at our XML instance document:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <printOrder id="1219271208522"> <notifications> <appointments> <appointment> <apptType>Yearly Checkup</apptType> <apptDate>2008-09-15T00:00:00-07:00</apptDate> <owner> <firstName>Joe</firstName> <lastName>Outdoors</lastName> <address> <addressLine1>123 Whitewater Street</addressLine1> <city>OurTown</city> <state>CA</state> <zip>90347</zip> <zipExt>1234</zipExt> </address> </owner> <pet> <name>Honcho</name> <species>Dog</species> </pet> </appointment> <appointment> <apptType>Well Mom Exam</apptType> <apptDate>2008-09-12T00:00:00-07:00</apptDate> <owner> <firstName>Missy</firstName> <lastName>Fairchild</lastName> <address> <addressLine1>456 Scenic Drive</addressLine1> <city>West OurTown</city> <state>CA</state> <zip>90349</zip> <zipExt>6789</zipExt> </address> </owner> <pet> <name>Miss Kitty</name> <species>Cat</species> </pet> </appointment> </appointments> <birthdays> <birthday> <age>7</age> <birthday>2000-09-07T00:00:00-07:00</birthday> <owner> <firstName>Violet</firstName> <lastName>Flowers</lastName> <address> <addressLine1>22375 Willow Court</addressLine1> <city>West OurTown</city> <state>CA</state> <zip>90349</zip> <zipExt>6789</zipExt> </address> </owner> <pet> <name>Tom</name> <species>Cat</species> </pet> </birthday> </birthdays> </notifications> </printOrder>

    And here's another look at the main() method:

    // The XML instance document received from NiceVet contains NiceVet's print order. File input = new File("niceVet.xml"); // Create a JAXBContext for the weprintstuff.generated package. JAXBContext ctx = JAXBContext.newInstance("weprintstuff.generated"); // Create an unmarshaller. Unmarshaller unmarshaller = ctx.createUnmarshaller(); // Unmarshal the data in the XML instance document to a Java content tree made up of instances of the schema-derived classes. JAXBElement printOrderElement = (JAXBElement)unmarshaller.unmarshal(new FileInputStream(input)); PrintOrder printOrder = (PrintOrder)printOrderElement.getValue(); // Print out the print items. List<Appointment> appointments = printOrder.getPrintItems().getAppointmentHolder().getAppointments(); for (Appointment a : appointments) { a.print(); } List<Birthday> birthdays = printOrder.getPrintItems().getBirthdayHolder().getBirthdays(); for (Birthday b : birthdays) { b.print(); }

    Check out our results: No one will ever miss an appointment or a birthday again.

    Hi Joe! Our records show that your dog Honcho has a Yearly Checkup appointment scheduled on 09/15/2008. Please call us 24 hours prior to your appointment if you need to reschedule. Sincerely, NiceVet Hi Missy! Our records show that your cat Miss Kitty has a Well Mom Exam appointment scheduled on 09/12/2008. Please call us 24 hours prior to your appointment if you need to reschedule. Sincerely, NiceVet Hi Violet! Our records show that your cat Tom will turn 7 years old on 09/07. Happy Birthday, Tom, from all of us at NiceVet!

    Running the Sample Application

    To run the sample application, download the sample code and unzip it. Navigate to the directory <sample-install-dir>/schema-to-java and generate the schema-derived classes as described in the section "Generating the Schema-Derived Classes."

    Launch the NetBeans IDE and select File > Open Project. In the Open Project dialog box, navigate to the directory where you unzipped the sample code and select the folder schema-to-java. Select the Open as Main Project check box. Click Open Project Folder. Right-click the schema-to-java project and select Run Project.

    Conclusion

    In Exchanging Data With XML and JAXB, Parts 1 and 2, we've learned how JAXB can facilitate the flow of data between business partners. With features that expedite XML processing on both ends of the exchange, JAXB can enable simpler, more productive integration between systems.

    References and Resources

    Sample code for this tip
    Java EE 5 Tutorial
    Kohsuke Kawaguchi's Blog - Kohsuke Kawaguchi is a staff engineer at Sun Microsystems, where he works on JAXB, among other projects.
    JSR 222: JAXB Specification

    About the Author

    Jennie Hall is a lead developer working in the financial sector.

    Making Progress With Swing's Progress Monitoring A

    posted: 03 Feb 2009

    by Jennie Hall
    Updated Jan. 23, 2009

    In this tip, you'll learn how to use Swing's progress indicator support to monitor and report on the progress of long-running operations. It is a good practice to keep users informed as they interact with an application; one way to do this is with a progress bar. A progress bar is an animated image that indicates the degree of completion of a given task. The animation typically looks like a rectangular bar that fills in as the task becomes more complete.

    Swing's Progress Monitoring API consists of three classes that enable the use of progress bars. JProgressBar subclasses JComponent and is a graphical component that illustrates the progress of an operation. It can be embedded within other graphical components. ProgressMonitor subclasses Object and is not itself a graphical component. It monitors a task and pops a dialog box with a progress bar in it. ProgressMonitorInputStream is a stream filter with an associated progress monitor. As the stream is read, the progress monitor automatically receives updates on the number of bytes read and displays the percentage of work completed in its dialog box.

    The Java Tutorial provides some good rules of thumb that help to determine the appropriate class to use in a given situation. For example, use JProgressBar when you need more than one progress bar or you would like more control over the configuration of the progress bar. If you need a convenient way to cancel the monitored task or to allow the user to dismiss the dialog box while continuing to run the task in the background, ProgressMonitor provides for this. ProgressMonitor also features a modifiable status note in its dialog box that can be updated periodically by your application. The sample application for this tip uses ProgressMonitor.

    The Sample Application

    The sample application copies files located in a source directory (in) to a destination directory (out). It has a Swing GUI that allows the user to launch the copy operation by clicking the Copy Files button as shown in Figure 1.

    Figure 1: Sample Application

    Upon the launch of the copy operation, the application creates a progress monitor that keeps track of the amount of work completed and displays this information in a dialog containing a progress bar. The application also writes output regarding the progress of the operation to the console as shown in Figure 2.

    Figure 2: Dialog containing progress bar

    As shown above, the GUI displays the number of kilobytes copied and the file name of the file currently being copied. The user may cancel the operation at any time by clicking the Cancel button. After the copy operation completes, the GUI appears as shown in Figure 3:

    Stepping Through the Sample Application

    The sample application consists of a class, ProgressMonitorExample, that extends javax.swing.JPanel and implements java.awt.event.ActionListener and java.beans.PropertyChangeListener. ProgressMonitorExample's main() method tells the event dispatch thread to schedule the execution of a Runnable that creates the application GUI:

    public static void main(String[] args) { // tell the event dispatch thread to schedule the execution // of this Runnable (which will create the example app GUI) for a later time SwingUtilities.invokeLater(new Runnable() { public void run() { // create example app window JFrame frame = new JFrame("Progress Monitor Example"); // application will exit on close frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); // create example app content pane // ProgressMonitorExample constructor does additional GUI setup JComponent contentPane = new ProgressMonitorExample(); contentPane.setOpaque(true); frame.setContentPane(contentPane); ...

    ProgressMonitorExample contains an inner class, CopyFiles, that extends javax.swing.SwingWorker. When the user clicks the Copy Files button, ProgressMonitorExample's actionPerformed() method receives the event, creates a new ProgressMonitor, and starts the file-copying operation on a background thread. Here's the code that creates the ProgressMonitor:

    public void actionPerformed(ActionEvent event) { // make sure there are files to copy File srcDir = new File("in"); if (srcDir.exists() && (srcDir.listFiles() != null && srcDir.listFiles().length > 0)) { // set up the destination directory File destDir = new File("out"); // create the progress monitor progressMonitor = new ProgressMonitor(ProgressMonitorExample.this, "Operation in progress...", "", 0, 100); progressMonitor.setProgress(0); ...

    ProgressMonitor has a single constructor. The first argument is the parent component to the progress monitor's dialog box. The second argument, of type Object, is displayed on the dialog box. It should be a string, icon, or component. This example supplies the constructor with a string that lets the user know that the requested operation is underway. The third argument is an optional status note that also appears on the dialog box. This status note can be updated periodically as the monitored task runs. Set this value to null if no status note is necessary. The fourth and fifth arguments are the minimum and maximum values for the progress bar in the progress monitor dialog box.

    The code below, also excerpted from actionPerformed(), creates a new instance of CopyFiles, adds ProgressMonitorExample as a property change listener on the instance, and executes the instance:

    // schedule the copy files operation for execution on a background thread operation = new CopyFiles(srcDir, destDir); // add ProgressMonitorExample as a listener on CopyFiles; // of specific interest is the bound property progress operation.addPropertyChangeListener(this); operation.execute(); // we're running our operation; disable copy button copyButton.setEnabled(false);

    CopyFiles subclasses SwingWorker, so the call to inherited method execute() schedules CopyFiles for execution on a background thread and returns immediately. Time-consuming activities should always run on a background thread rather than the event dispatch thread. This way, the GUI remains responsive.

    Although the file-copying operation has begun, the progress monitor dialog box doesn't pop up right away. By default, ProgressMonitor waits for 500 ms before making a decision on whether or not to show the dialog box at all. After this time period has elapsed, if ProgressMonitor determines that the monitored operation has already completed or is likely to complete before the dialog box can be displayed, ProgressMonitor does not pop the dialog box. ProgressMonitor's method setMillisToDecideToPopup() controls this setting. setMillisToPopup() sets the estimated amount of time it will take the dialog box to appear; the default value for this property is 2 seconds.

    The real work of copying the files occurs in doInBackground(), an abstract method on SwingWorker that CopyFiles overrides. Here's a partial listing:

    // perform time-consuming copy task in the worker thread @Override public Void doInBackground() { int progress = 0; // initialize bound property progress (inherited from SwingWorker) setProgress(0); // get the files to be copied from the source directory File[] files = srcDir.listFiles(); // determine the scope of the task long totalBytes = calcTotalBytes(files); long bytesCopied = 0; while (progress < 100 && !isCancelled()) { // copy the files to the destination directory for (File f : files) { File destFile = new File(destDir, f.getName()); long previousLen = 0; try { InputStream in = new FileInputStream(f); OutputStream out = new FileOutputStream(destFile); byte[] buf = new byte[1024]; int counter = 0; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); counter += len; bytesCopied += (destFile.length() - previousLen); previousLen = destFile.length(); if (counter > PROGRESS_CHECKPOINT || bytesCopied == totalBytes) { // get % complete for the task progress = (int)((100 * bytesCopied) / totalBytes); counter = 0; CopyData current = new CopyData(progress, f.getName(), getTotalKiloBytes(totalBytes), getKiloBytesCopied(bytesCopied)); // set new value on bound property // progress and fire property change event setProgress(progress); // publish current progress data for copy task publish(current); } } in.close(); out.close(); } catch (IOException e) { e.printStackTrace(); } ...

    doInBackground() gets any files located in the in directory and copies them one by one to the out directory. Each time a specified number of bytes have been copied, the application calculates what percentage of the total number of bytes has been copied so far, then creates an instance of the inner class CopyData to hold this information along with the total number of kilobytes, the number of kilobytes copied so far, and the filename of the file currently being copied. The application then updates the bound property progress with the calculated percentage, firing a property change event in the process. The call to publish() makes the copy task's current progress data available for processing in the event dispatch thread.

    ProgressMonitorExample's propertyChange() method extracts the progress value from the property change event. It then updates the progress monitor animation by calling its setProgress() and passing the progress value. Here's the code:

    // executes in event dispatch thread public void propertyChange(PropertyChangeEvent event) { // if the operation is finished or has been canceled by // the user, take appropriate action if (progressMonitor.isCanceled()) { operation.cancel(true); } else if (event.getPropertyName().equals("progress")) { // get the % complete from the progress event // and set it on the progress monitor int progress = ((Integer)event.getNewValue()).intValue(); progressMonitor.setProgress(progress); } }

    Notice that ProgressMonitor provides a convenient way to determine if the dialog has been canceled by the user. The sample application responds to a user cancellation by terminating the monitored activity, but in other situations it might be appropriate to allow the user to dismiss the dialog box while the activity continues to run in the background.

    By overriding the SwingWorker method process(), CopyFiles can use the progress data made available by the call to publish() to update the GUI. process() executes in the event dispatch thread, so it is safe to update Swing components in this method. Here's the code:

    // process copy task progress data in the event dispatch thread @Override public void process(List data) { if(isCancelled()) { return; } CopyData update = new CopyData(0, "", 0, 0); for (CopyData d : data) { // progress updates may be batched, so get the most recent if (d.getKiloBytesCopied() > update.getKiloBytesCopied()) { update = d; } } // update the progress monitor's status note with the // latest progress data from the copy operation, and // additionally append the note to the console String progressNote = update.getKiloBytesCopied() + " of " + update.getTotalKiloBytes() + " kb copied."; String fileNameNote = "Now copying " + update.getFileName(); if (update.getProgress() < 100) { progressMonitor.setNote(progressNote + " " + fileNameNote); console.append(progressNote + "\n" + fileNameNote + "\n"); } else { progressMonitor.setNote(progressNote); console.append(progressNote + "\n"); } }

    As shown above, process() updates the progress monitor's status note with the number of kilobytes copied so far and the filename of the file currently being copied, then appends this information to the console.

    When its background operation is finished, CopyFiles sets its own state to done and invokes the done() method in the event dispatch thread. done() invokes the SwingWorker method get(), which returns the final result of the background task. In the case of the sample application, however, there is no final result to be processed. The sample application calls get() to determine whether or not the background task was canceled before completion and responds appropriately:

    // perform final updates in the event dispatch thread @Override public void done() { try { // call get() to tell us whether the operation completed or // was canceled; we don't do anything with this result Void result = get(); console.append("Copy operation completed.\n"); } catch (InterruptedException e) { } catch (CancellationException e) { // get() throws CancellationException if background task was canceled console.append("Copy operation canceled.\n"); } catch (ExecutionException e) { console.append("Exception occurred: " + e.getCause()); } // reset the example app copyButton.setEnabled(true); progressMonitor.setProgress(0); }


    Running the Sample Application

    To run the sample application, download the sample code and unzip it. The sample application assumes that there are files to copy in the in directory located under the project root, so add some (preferably large) files of your choice to this directory. Launch NetBeans and select File -> Open Project. In the Open Project dialog box, navigate to the directory where you unzipped the sample code and select the folder progressMonitorExample. Select the Open as Main Project check box. Click Open Project Folder. Right-click the progressMonitorExample project and select Build, then right-click the project again and select Run.

    References and Resources

    Sample code for this tip
    The Java Tutorial

    About the Author

    Jennie Hall is a lead developer working in the financial sector.

    Java Performance Tuning: A Conversation With Java

    posted: 25 Aug 2008

    Java Champion and Java performance expert Kirk Pepperdine shares ways to identify and solve Java performance bottlenecks.

    Learning Curve Journal, Part 3: JavaFX Script Func

    posted: 25 Aug 2008

    Part 3 shows you how to use JavaFX Script functions to implement actions in a Java FX Script program.

    Learning Curve Journal, Part 4: Accessing a Web Se

    posted: 25 Aug 2008

    Part 4 completes the JavaFX Script version of the application, which enables users to search, list, and display images from the Flickr web site.

    Learning Curve Journal, Part 1: Exploring the Java

    posted: 25 Aug 2008

    This series follows a developer as he learns the JavaFX technology. In Part I, he sets up the environment for JavaFX Script programming, then builds and runs a JavaFX Script program.

    Learning Curve Journal, Part 2: Declarative User I

    posted: 25 Aug 2008

    Part 2 focuses on the declarative style of coding enabled by JavaFX Script. You'll see how this style can make the code for a graphical application simpler and more intuitive.

    Teaching Mobile Computing to Generation C: A Conve

    posted: 25 Aug 2008

    Java Champion Qusay Mahmoud discusses ways to integrate mobile devices into the computer science curriculum -- and incur the gratitude of students.

    JDK 6 Update 7

    posted: 25 Aug 2008

    Java VisualVM is a new GUI-based tool for troubleshooting Java applications and is available as part of Java SE 6 Update 7. VisualVM incorporates technologies such as jvmstat, JMX, the NetBeans profiler, and more. Also see the Open Source Project Page.

    The New Draggable Applet Feature in the Java SE 6

    posted: 25 Aug 2008

    Java SE 6 Update 10, currently in beta release, reintroduces the concept of the applet, which unifies the desktop and browser experience.

    An Introduction to Real-Time Java Technology (Part

    posted: 25 Aug 2008

    Learn about approaches to garbage collection (GC) and the new features and improved tools in the Sun Java Real-Time System (Java RTS).

    An Introduction to Real-Time Java Technology (Part

    posted: 25 Aug 2008

    Often associated with high speed, real-time computing is more about predictability, the knowledge that the system will always perform within the required time frame.

    Java ME Technology: Everything a Developer Needs f

    posted: 25 Aug 2008

    This overview looks at the economics, cost of entry, and learning curve in using Java ME technology to penetrate the mobile market. It introduces the JSRs included in the Mobile Services Architecture (MSA) standard.

    Digisoft.tv's John Allen on Interactive Television

    posted: 25 Aug 2008

    Sun's Jennifer Yonemitsu interviews Digisoft.tv CEO John Allen to discuss the Java ME-based client platform for IPTV set-top boxes, using Sun's Java technology-based media client and Digisoft.tv's middleware/SDK.

    Book Review: Head First Software Development

    posted: 25 Aug 2008

    O'Reilly's new Head First Software Development sums up decades of research on what works and what doesn't in the often arcane world of software development.

    Bluetooth and GPS: Part 1 - Reading Wireless Seria

    posted: 25 Aug 2008

    In the first of this multi-part series, Bruce Hopkins describes how to construct a low-cost solution that allows you to install, debug, and test your JSR-82 applications on your computer, and how to read data from a Bluetooth-enabled GPS device.

    Long Running Web Process (LRWP) in the Java Platfo

    posted: 03 Aug 2008

    LRWP is a protocol, similar to CGI but faster, used by a web server to communicate with its peers. In Making Java Technology Faster than C with LRWP, we showed that the Long Running Web Process (LRWP) implementation in Java using GlassFish was faster than the implementation in C and Xitami. This paper describes that LRWP implementation.

    Project Darkstar's New World of Online Games: A Co

    posted: 03 Aug 2008

    By radically simplifying online game development and enabling games to scale to tens of thousands of players without a glitch, Sun's Project Darkstar -- with a preliminary release now freely available for download under open-source licensing -- is changing the rules of the game business.

    Becoming a Better Programmer: A Conversation With

    posted: 03 Aug 2008

    Dr. Heinz Kabutz, Java Champion and creator of the Java Specialists' Newsletter, discusses the importance of design patterns and unit testing, the 10 laws of Java concurrency, and life as a developer on the island of Crete.

    Learning Curve Journal, Part 3: JavaFX Script Func

    posted: 03 Aug 2008

    Part 3 of the Learning Curve Journals describes the differences between functions and operations in JavaFX Script.