X

Recent Posts

TechTip

Overhauling the Java UTF-8 charset

By Xueming ShenThe 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:#BitsBit pattern170xxxxxxx   211110xxxxx10xxxxxx  3161110xxxx10xxxxxx10xxxxxx 42111110xxx10xxxxxx10xxxxxx10xxxxxxThe 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: A Java application would like to filter the incoming UTF-8 input stream to reject certain key words, for example "ABC". 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. 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 66517The new UTF-8 charset implementation has been integrated inJDK7, 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.

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...

TechTip

Closing a URLClassLoader

By Michael McMahonComplex 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.

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...

TechTip

Making Progress With Swing's Progress Monitoring API

by Jennie HallUpdated Jan. 23, 2009In 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. ProgressMonitorExamples 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 timeSwingUtilities.invokeLater(new Runnable() {public void run() {// create example app windowJFrame frame = new JFrame("Progress Monitor Example");// application will exit on closeframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);// create example app content pane// ProgressMonitorExample constructor does additional GUI setupJComponent 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, ProgressMonitorExamples 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. ProgressMonitors 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@Overridepublic Void doInBackground() {int progress = 0;// initialize bound property progress (inherited from SwingWorker)setProgress(0);// get the files to be copied from the source directoryFile[] files = srcDir.listFiles();// determine the scope of the tasklong totalBytes = calcTotalBytes(files);long bytesCopied = 0;while (progress < 100 && !isCancelled()) { // copy the files to the destination directoryfor (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 taskprogress = (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 eventsetProgress(progress);// publish current progress data for copy taskpublish(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. ProgressMonitorExamples 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@Overridepublic 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 recentif (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 consoleString 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@Overridepublic void done() {try {// call get() to tell us whether the operation completed or // was canceled; we don't do anything with this resultVoid result = get();console.append("Copy operation completed.\\n"); } catch (InterruptedException e) {} catch (CancellationException e) { // get() throws CancellationException if background task was canceledconsole.append("Copy operation canceled.\\n");} catch (ExecutionException e) {console.append("Exception occurred: " + e.getCause());}// reset the example appcopyButton.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.

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...

TechTip

Exchanging Data With XML and JAXB, Part 2

by Jennie Hall In Exchanging Data With XML and JAXB, Part 1, we sawhow the Java Architecture for XML Binding (JAXB) expeditesthe exchange of data between systems. With JAXB 2.0's annotationsupport, generating XML data for a business partner is as easy asannotating your existing object model.  In this tip, you'll learn how JAXB's binding customizationfeatures can facilitate XML processing for the recipient of data aswell as for the sender. Binding customization offers a level ofcontrol over the characteristics of the Java classes generated bythe JAXB schema compiler. This allows you to work with an objectmodel that makes sense for your business domain even when processingXML instance documents based on a schema that is not of your owndesign. 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, Part1. The Sample Application Let's continue with our scenario from Part 1.A veterinary office, NiceVet, wants to send upcomingappointment reminders and pet birthday cards to its clients. NiceVetcontracts with a service provider, WePrintStuff, to do the printingand mailing. InPart 1, we assembled some data for WePrintStuff by annotatingNiceVet's existing object model and then using JAXB to marshalapplication data from Java objects to an XML instance document. Wealso used JAXB's schema generator to produce a corresponding schemafrom the annotated Java classes. On the receiving end, WePrintStuff will run the JAXB schemacompiler against the source schema to generate the schema-derivedclasses. JAXB will create instances of these classes as itunmarshals NiceVet's data from the XML instance document and bindsit into a Java content tree. In some cases, however, the defaultmanner in which JAXB generates the schema-derived classes and bindsthe data is not exactly what is needed. JAXB's binding customizationfeatures provide us with some nice ways to handle many of thesecases. 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 atWePrintStuff. For example, we would like to workwith meaningful package and class names and avoid any namingcollisions. To accomplish this, we can add custom bindingdeclarations to the source schema itself -- this is known as inlinecustomization. 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 namespacedeclaration and the JAXB version number. The JAXB namespace prefixwe'll use in our sample application is jxb:. We canoverride the default JAXB bindings at different scopes. Lower-level,more fine-grained scopes inherit declarations made at higher-levelscopes, but binding declarations made at lower-level scopes canoverride these inherited declarations. The scopes in order fromhighest to lowest level are global, schema, definition, and component. In the preceding example, we've made a declaration at globalscope that specifies that all collections in this schema will usethe implementation class java.util.ArrayList. If oursource schema imported another schema, this declaration would applyto the second schema as well. There can be only one<globalBindings> declaration per schema. Thisdeclaration is valid in the top-level schema only. At schema scope, we've specified the package into which the JAXBschema compiler will generate the schema-derived classes. We've alsoincluded some package-level Javadoc that describes our package andits 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 getsome class names that better reflect Java naming conventions andWePrintStuff's business domain. The default bindings for theoriginal schema would generate classes with names likeClassAType, ClassBType, and soon.This is not how we typically name classes in theJava 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, andso on. Dealing with class names is straightforward, butunfortunately we're stuck with a class structure that is less thanideal. There are too many wrappers around the data, which results ina long chain of method calls. Here's what the calls would have lookedlike 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 theymean 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 theenhanced <jxb:javaType> customization(<xjc:javaType>) and an adapter class derivedfrom XmlAdapter, but JAXB currently does not supportthis customization for XML complex types. This issue is documentedin the JAXB issue tracker as issue number 209.Meanwhile, we'll look at an example of the enhanced<jxb:javaType> customization used with an XMLsimple type in the upcoming paragraphs. Moving down the source schema, we see that each print orderreceived will have an ID of type long. We know,however, that WePrintStuff employs a natural keystrategy in its persistent store. For print-order records, the keyconsists of a client name and an order ID. WePrintStuff uses theclass PrintOrderKey to encapsulate this keyinformation. We'll apply a binding customization that causes theJAXB schema compiler to use an adapter class,IdAdapter, to replace the original print-order ID withWePrintStuff's PrintOrderKey class. Take alook: <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 toorderKey and specified the types of the adapter and theorder key. During the unmarshalling process, JAXB callsIdAdapters unmarshal() method, whichreceives the value of id and incorporates it into a newPrintOrderKey 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 justdiscussed is an example of a vendor extension. The JAXB referenceimplementation (RI) offers additional customizations that are not part ofthe JAXB specification. To use these extensions, we must include adeclaration for the JAXB RI vendor extension namespace and specify anamespace 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 schemacompiler with the -extension switch. For an example ofthe standard <jxb:javaType> customization, whichis not a vendor extension, check out niceVet.xsd andweprintstuff.print.CustomDataTypeConverter. These filesare included with the sample code that comes with this techtip. In our example, we've used the<jxb:javaType> customization to convert XML dateTimetypes to nicely formatted String dates ready for output. Things are looking better, but we'd also like to handle theprinting of appointment reminders and birthday cards aspolymorphically 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 sourceschema somewhat while retaining the schema's ability to validate thesame set of XML instance documents. In this case, we have added atotally new complex type, printItemType, to the schema.Then we modified the complex type AppointmentType withthe XML Schema extension element, causingAppointmentType to derive fromprintItemType. At this point, we applied a JAXB customization toprintItemType that directs the schema compiler to usethe specified class that we've written rather than generating one.Because we've implemented PrintItem ourselves, we caninclude behavior: specifically, a print() method thatallows us to handle subclasses, such as Appointment andBirthday, in a more polymorphic manner. Here's the codefor 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 theschema-derived classes as discussed earlier, the code to print theitems in the print order is now pretty clean. Here's an excerpt fromour 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 theability to map XML simple types to typesafe enumerations. This isconvenient because WePrintStuff currently has printing templates forcertain types of appointments only. We'll modify the schema toinclude the supported appointment types and add a customization thatcauses the schema compiler to map the elements ofApptType to a Java typesafe enum class. XML instancedocuments with appointment types that WePrintStuff does not supportwill 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")@XmlEnumpublic 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 bindingcustomizations, but JAXB also accepts customizations in the form ofone or more external binding customization files. Inline and externalcustomizations can be used in combination, but they cannot be usedtogether on the same element. External binding files are useful whenyou cannot modify a given schema or when you want to reusecustomizations across schemas. For example, WePrintStuff has its ownclass for U.S. addresses, DomesticAddress, and WePrintStuffwould 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 isjust an ASCII text file. A valid binding file must specify theschema name and node. We identify nodes using XPath expressions.In the previous listing, we've applied a customization that directsthe schema compiler to use WePrintStuff'sDomesticAddress class rather than generating a classfor the complex type AddressType. This is the samecustomization illustrated earlier in our PrintItem baseclass example, although in that situation, we declared thecustomization inline. The syntax for supplying a bindingcustomization file to the schema compiler is the following: xjc -b bindings schema We can apply multiple binding files to one schema, one bindingfile to multiple schemas, or multiple binding files to multipleschemas. Each binding file must have its own -b switch. Generating the Schema-Derived Classes We generate our schema-derived classes at the command line withthe 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 classeswith the following output: parsing a schema...compiling a schema...weprintstuff\\generated\\Adapter1.javaweprintstuff\\generated\\Adapter2.javaweprintstuff\\generated\\Appointment.javaweprintstuff\\generated\\ApptType.javaweprintstuff\\generated\\Birthday.javaweprintstuff\\generated\\ObjectFactory.javaweprintstuff\\generated\\Owner.javaweprintstuff\\generated\\Pet.javaweprintstuff\\generated\\PrintItems.javaweprintstuff\\generated\\PrintOrder.javaweprintstuff\\generated\\package.html The JAXB schema compiler has a number of options. For example, itwill mark generated classes as read only in response tothe -readOnly switch. Invoke xjc with nooptions or with the -help switch for moreinformation. The Payoff We've modified our source schema, made our customizations, andgenerated our schema-derived classes. All that's left to do is runour printing program, let JAXB bind the XML data supplied byNiceVet, 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 abirthday 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, NiceVetHi 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, NiceVetHi 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 unzipit. Navigateto the directory<sample-install-dir>/schema-to-java and generatethe schema-derived classes as described in the section "Generatingthe Schema-Derived Classes." Launch the NetBeans IDE and select File > Open Project.In the Open Project dialog box, navigate to the directory where youunzipped the sample code and select the folder schema-to-java.Select the Open as Main Project check box. Click Open ProjectFolder. Right-click the schema-to-java project and select RunProject. Conclusion In Exchanging Data With XML and JAXB, Parts 1 and 2, we've learned how JAXB can facilitate the flow of data betweenbusiness partners. With features that expedite XML processing onboth ends of the exchange, JAXB can enable simpler, more productiveintegration 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.

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 annotationsupport,...

TechTip

Exchanging Data with XML and JAXB, Part 1

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@XmlTransientpublic 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 createdfinal File dir = new File("generated" + File.separator + "schema");// specify a name for the generated XML instance documentOutputStream os = new FileOutputStream("niceVet.xml");// create a JAXBContext for the PrintOrder classJAXBContext 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.xsdctx.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 dataPrintOrder 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 indentationmarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);// marshal the data in the Java content tree // to the XML instance document niceVet.xmlmarshaller.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.

by Jennie HallIn this tip, you'll learn how to to use the Java Architecture for XML Binding (JAXB) in Java SE 6to exchange XML data between systems without having to delve into the specifics of XML...

Sun

Where's the State?

 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 principlekeep 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 yourselfWho 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 theno 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 . . .

 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 thatcan...

TechTip

Distributing a Java Web Start Application via CD-ROM

by Luan O'Carroll Isn't JavaWeb Start (JWS) supposed to allow web-based distribution ofapplications? So why would one want to distribute a Java Web Start(JWS) application via CD-ROM? Well, for a number of reasons. Forlarger applications, a complete installation can still be a majordownload even with high-speed broadband. Secondly, not all desktopsare online, and not all can access the internet (for corporatesecurity reasons, for example). And, lastly, some people just likeCDs. A client company required that their application be distributedworldwide, including to places where broadband coverage is sparse.The application contains information about a large number ofproducts, including detailed drawings and diagrams. All thisinformation constituted a major part of the application, and acomplete installation including the JVM amounts to over 40 MB. Inaddition to this, the company wanted to be able to distribute theapplication on CDs at trade fairs and with promotional materials;therefore, a CD-based distribution was required. Normally a CDinstall is possible using either commercial or open sourceinstallers, of which there are many. However, when an applicationis to run with Java Web Start, it needs to be installed in aspecific location and not at the user's discretion, as is the normfor installers. This article describes the steps involved in installing anapplication that installs both from CD and the internet. Theinstallation process requires that: The installed application must check for updates and integratewith the JWS cache. The installation should work on a machine without an existingor up-to-date version of Java. The installed application should not require an internetconnection. A installation must be easy to use and must provide a simpleuser interface. Application installation is normally carried out with a genericinstaller application, but a traditional install process wouldeffectively create a separate application that is unaware of JWS.Each time an update is released, the user would have to downloadand install the new version, whereas a JWS application onlydownloads those components that have been updated, making theprocess far more efficient and reliable. The article therefore alsodescribes a JWS application installer. JWS Primer Java Web Start allows Java applications to be launched via alink to a JNLPfile. The JNLP file describes the main method or entry point intothe application and it references the resources used by theapplication. When a JWS application is launched, the JVM tries to access therequired resources, updating them if necessary, and copies the fileto its cache. On subsequent attempts to launch the application, JWScan check this cache and skip download of the resources. If theclient 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 wouldattempt to contact the server and download any new files. Obviouslythis would defeat the purpose of distributing the files via CD ifthe client machine is online. Instead, we need some way to updatethe JWS cache as though the application had been previously loadedby JWS. Updating the JWS Cache The Java 5 version of JWS includes a little known-import option that imports a JWS application into thecache from a specified location. The CD image at this location is just a copy of what you wouldnormally place on the web server: the JNLP file, plus the .jars andresources referred to by that JNLP file. If you use a servlet toserve up the JNLP, then the CD image will need a self-containedsnapshot of the generated JNLP file. The CD image can thus be installed into the JWS cache bycalling: <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 ofthe JWS application on the CD, and <XXXX> isthe name of the application JNLP file. Later, we will see how thiscommand is automated and wrapped in a simple GUI. In installing the cached application, JWS conveniently promptsthe user to install desktop and menu shortcuts for starting theapplication. Once the JWS install has been completed, we can againcall 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 theinstallation referred to by the JNLP file. If the machine isconnected to the internet, it will check for updates in the normalway, as part of the process, and then start the application. Ifthere is no network connection, the application will launch asdelivered on CD. The next time the user starts the application they can use themenu or desktop shortcuts and the CD will no longer be needed.Alternatively, the user can start the application from a link on aweb page that points to the same URL/JNLP file combination; i.e., theoriginal version from the website. JVM Complications One gotcha in all of this is that the above commands require thepresence of a JVM, and in some rare cases this may not be installedor may not be available by default on the system path and thereforesome extra measures are needed to locate a usable JVM. Furthermore,when a user inserts the CD, the installation should begin, and theinstallation should check for the presence of an existing JVM. Theprocess of checking for a JVM is then as follows: Check for a JVM (for the installer). Install the JVM if not present. Launch the installer, showing the usual licenseinformation. Install the target JVM (if required by the application anddifferent from 1 above). Import the JWS cache. Start the JWS application. Some further complications arise from the fact that the minimumJVM for the JWS -import option is Java 5, so even if aJVM is present and usable for the application, this import optionwill still require a fairly recent JVM. Secondly, the import processtakes some time and must complete before the application islaunched, and this sort of delayed execution is difficult toachieve with many of the normal installers. Given these complications, it was necessary to build a customlaunch application that could perform these steps. Read the rest of the article.

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...

TechTip

Launch Java Applications from Assembly Language Programs

by Biswajit Sarkar JavaNative Interface (JNI) is a mechanism that can be used toestablish communication between native language programs and theJava virtual machine. The documentation for JNI and the technicalliterature on JNI deal extensively with interactions between theJVM and C/C++ code. The Java SDK even provides a utility togenerate a header file to facilitate calling C/C++ programs fromJava code. However, there is hardly any mention of Java andassembly language code working together. In an earlier article I showed how assembly language programs can becalled from Java applications. Here I deal with the technique forinvoking Java programs from an ASM process through a demoapplication that calls a Java method from assembly language code.The Java method brings up a Swing JDialog to show thatit has, indeed, been launched. Why Java with ASM? JNI is essential to the implementation of Java, since the JVMneeds to interact with the native platform to implement some of itsfunctionality. Apart from that, however, use of Java classes canoften be an attractive supplement to applications written in otherlanguages, as Java offers a wide selection of APIs that makesimplementation of advanced functions very simple. Some time ago, I was associated with an application to collectreal-time data from a number of sources and save them in circularbuffers so that new data would overwrite old data once the buffergot filled up. If a designated trigger event was sensed through adigital input, a fixed number of data samples would be saved in thebuffers so that a snapshot of pre- and post-trigger data would beavailable. The original application was written in assemblylanguage. After the application was used for a few months, it wasfelt that it would be very useful to have the application mail thesnapshots to authorized supervisors whenever the trigger eventoccurred. Of course, it would have been possible to write thisextension in assembly, but the team felt that in that particularinstance it was easier to write that extension in Java and hook itup with the ASM program. As I had earlier worked with ASM-orientedJNI, I knew this could be done and, indeed, the project wasimplemented quickly and successfully. I am sure there are many legacy applications written in assemblylanguage that could benefit from such add-ons. However, it is notonly for old applications in need of renovation that JNI can proveuseful. Although it may seem unlikely to some of us, assemblylanguage is still used for writing selected portions of newprograms. In an article published not very long ago, the author says, "I havefound that many of Sun's partners still use assembly language intheir products to ensure that hot code paths are as efficient aspossible. While compilers are able to generate much more efficientcode today, the resulting code still doesn't always compete withhand-coded assembly written by an engineer that knows how tosqueeze performance out of each microprocessor instruction.Assembly language remains a powerful tool for optimization,granting the programmer greater control, and with judicious use canenhance 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 Javacode from languages other than ASM. If JInvoke isrewritten 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 ASMapplication, 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. TheJava versions used were 1.3, 1.4, and 1.6. In all cases theapplications worked smoothly. Read the rest of this article . . .

by Biswajit Sarkar Java Native Interface (JNI) is a mechanism that can be used to establish communication between native language programs and theJava virtual machine. The documentation for JNI and...

TechTip

Add Logging at Class Load Time with Java Instrumentation

by Thorbjørn Ravn Andersen When you're trying to analyze why a program failed, a very valuablepiece of information is what the program was actually doing when itfailed. In many cases, this can be determined with a stack trace, butfrequently that information is not available, or perhaps what youneed is information about the data that was being processed at thetime of failure. Traditionally this means using a logging framework like log4j or the Java Logging API, and then writing and maintaining allnecessary 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 theclasses as they are loaded. This article will show how to implement such a Java agent, whichtransparently will add entry and exit logging to all methods in allyour classes with the standard Java Logging API. The example usedis 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 statementsadded: 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 mainINFO: > main(args=[])Hello World2007-12-22 22:08:52 LoggingHelloWorld mainINFO: < main() Note that each log statement is printed on two lines. First, aline with a time stamp, the provided log name, and the method inwhich the call was made, and then a line with the provided logtext. The rest of the article will demonstrate how to make theoriginal Hello World program behave like the logging Hello World bymanipulating the byte code when it is loaded. The manipulationmechanism is the Java Instrumentation API added in Java 5. Read the rest of this article.

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 itfailed. In many cases, this can be...

TechTip

Source Code Analysis Using Java 6 APIs

by Seema Richard, Deepa SobhanaHave 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 processwith the help of these three APIs.Invoking the Compiler from Code: The Java Compiler APIWe all use the javac command-line tool forcompiling Java source files to class files. Then why do we need anAPI to compile Java files? Well, the answer is quite simple: as thename describes, this new standard API lets us invoke the compilerfrom our own Java applications; i.e., you can programmaticallyinteract with the compiler and thereby make compilation part ofapplication-level services. Some typical uses of this API arelisted below.The compiler API helps application servers to minimize the timetaken to deploy applications, for example, by avoiding the overheadof using an external compiler for compiling the servlet sourcesgenerated from the JSP pages.Developer tools like IDEs and code analyzers can invoke thecompiler from within the editor or build tools that significantlyreduce the compile time.The Java compiler classes are packaged under thejavax.tools package. The ToolProviderclass of this package provides a method calledgetSystemJavaCompiler() that returns an instance ofsome class that implements the JavaCompiler interface.This compiler instance can be used to create a compilation taskthat will perform the actual compilation. The Java source files tobe compiled will be then passed to the compilation task. For this,the compiler API provides a file manager abstraction calledJavaFileManager, which allows Java files to beretrieved from various sources, such as the file system, databases, memory, and so on. In this sample, we use StandardFileManager, afile manager based on java.io.File. The standard filemanager can be acquired by calling thegetStandardFileManager() method of theJavaCompiler instance. The code snippet for theabove-mentioned steps is shown below://Get an instance of java compilerJavaCompiler compiler = ToolProvider.getSystemJavaCompiler();//Get a new instance of the standard file manager implementationStandardJavaFileManager fileManager = compiler. getStandardFileManager(null, null, null); // Get the list of java file objects, in this case we have only // one file, TestClass.javaIterable compilationUnits1 = fileManager.getJavaFileObjectsFromFiles("TestClass.java");A diagnostic listener can be optionally passed to thegetStandardFileManager() method to produce diagnosticreports of any non-fatal problems. In this code snippet, we passnull values, since we are not collecting thediagnostics from the tool. For details of the other parameterspassed to these methods, please refer to the Java 6 API. The getJavaFileObjectsfromFiles()method of the StandardJavaFileManager returns all theJavaFileObject instances that correspond to thesupplied Java source files.Read the rest of this article

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...

TechTip

Nimbus Look and Feel in Java SE 6 Update 10 Beta

By Ethan Nicholas When the venerable Metal look and feel for Swing first debuted, its main aestheticcompetition was the Windows 95 interface. Given the state of graphical user interfaces a decadeago, Metal was an attractive and elegant alternative to the other commoninterfaces of the time.The updated Ocean theme in Java SE 5 helped to keep Metal a viable choice up to thepresent day, but it's time for Swing's cross-platform look and feel to get anoverhaul.Enter the Nimbus Look and Feel. Abrand new, modern look and feel based on Synth, Nimbus provides apolished look to applications which choose to use it. And because Nimbus isdrawn entirely using Java 2D vector graphics, rather than static bitmaps, it'stiny (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, butupdating applications to use Nimbus couldn't be simpler. It only takes a singleline 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.NimbusLookAndFeelto the file <JAVA_HOME>/lib/swing.properties. You will have to createthe swing.properties file if it does not already exist.For further reading about Nimbus, take a look at the Nimbus earlyaccess page.

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 decadeago,...

TechTip

JSlider Appearance Improvements

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 is0 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 andSOUTH 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 theJSlider, 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); } }}

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 fornumerical input, that really serves the...

TechTip

Using Generics With Wildcards and Extends

By John Zukowski Java 2 Platform, Standard Edition 5.0 (J2SE 5.0) introducedgenerics to the Java programming language and platform. In thesimplest case and typical usage, generics allow for theidentification of what you want to store in a collection. So insteadof saying that your program has a List ofObjects, you can now specify that you have aList of String objects or some other classtype. Then, if you accidentally try to add something to theList that is of the wrong type, the compiler notifiesyou of the error and it can be fixed at compile time, rather thanhaving to wait until you run the program and the program reaches thepoint in code where the fetch operation produces a runtime castingexception. This brings up a second benefit of generics. Iterators now becometypesafe. The next() method of theIterator interface returns the typesafe version of thenext element of the collection. But this is not a tip about the use of generics, which a 2005 Core Java Technologies Tip explained. Mostpeople don't fully understand the use of the extendskeyword when using generics. A typical example shown to explain theuse of extends has to do with drawing shapes. Instead,this tech tip will use an example that uses Swing components so thatyou do not have to create extra new classes. In a very limited case,the class hierarchy for Swing button components is shown here, withObject as the real root: Component|- Container |- JComponent |- AbstractButton |- JButton |- JMenuItem |- JCheckBoxMenuItem |- JMenu |- JRadioButtonMenuItem |- JToggleButton |- JCheckBox |- JRadioButton One thing that all AbstractButton subclasses sharein common is a getText() method. So in the spirit ofgenerics, you can define a method that takes a List ofAbstractButton items and return a List ofthe String labels of those buttons. Here's the firstversion 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 aList 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() callis as follows: [Hello, World, Hola, Mundo] With a List of AbstractButtons,everything functions fine, but this breaks down when theList is of something else, specifically a subclass. Onewould logically think that with a List ofJButton items, everything would work fine, becauseJButton is a subclass of AbstractButton.Shouldn't you be able to callgetLabels(List<AbstractButton>) with aList 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 toaccept only an AbstractButton List, you cannot passanything 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 inhandy. Instead of defining the getLabels() method toaccept only an AbstractButton list, define it to acceptany List of AbstractButton subclasses: public static List<String> getLabels( List<? extends AbstractButton> list) { The wildcard ? here says that the method doesn'tcare what the exact class type is, as long as it is a subclass ofAbstractButton. Here's a complete example that puts allthe 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 withgenerics and are thinking of accepting an abstract class as thegeneric argument, or any superclass, remember to use wildcards sothat the same method works best with subclasses too. For more information on generics, see two earlier tutorials byGilad Bracha: a 2004tutorial (PDF) and the generics lesson in the online JavaTutorial.

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 theidentif...

TechTip

Getting to Know BoxLayout

One of the standard layout managers that come with the Java platform isBoxLayout. 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 UsageBoxLayout 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) toGridLayout 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 GlueThe 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.AlignmentLife 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 theComponent 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.comPlease 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!

One of the standard layout managers that come with the Java platform isBoxLayout. This allows you to layout a single row or column of componentsin a container. This may sound like a not-so-complicated...

TechTip

Sorting Strings

by John ZukowskiSorting strings with the Java platform can be thought ofas an easy task, but there is much more thought that shouldbe put into it when developing programs for an internationalmarket. If you're stuck in the English-only mindset, and youthink your program works fine because it shows that the stringtomorrow comes after today, you might think all is great. But,once you have a Spanish user who wants mañana to be sortedproperly, if all you use is the default compare() method of Stringfor sorting, the ñ character will come after the z characterand will not be the natural Spanish ordering, between the n characterand o character. That's where the Collator class of the java.textpackage comes into play.Imagine a list of words first mañana man many maxi nextUsing the default sorting mechanism of String, its compare() method,this will result in a sorted list of: first man many maxi mañana nextHere, mañana comes between maxi and next. In the Spanish world, whatshould 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 writeyour own custom sort routine to handle the ñ, what happens to your programwhen a German user comes around and wants to use their own diacriticalmarks, or what about just a list of design patterns with façade? Doyou 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 classtakes into account language-sensitive sorting issues and doesn't justtry to sort words based upon their ASCII/Unicode character values.Using Collator requires understanding one additional property beforeyou can fully utilize its features, and that is something calledstrength. The strength setting of the Collator determines howstrong (or weak) a match is used for ordering. There are four possiblevalues for the property: PRIMARY, SECONDARY, TERTIARY, and IDENTICAL.What actually happens with each is dependent on the locale. Typicallywhat happens is as follows. In reverse order, IDENTICAL strength meansjust that, the characters must be identical for them to be treated thesame. TERTIARY typically is for ignoring case differences. SECONDARYis for ignoring diacritical marks, like n vs. ñ. PRIMARY is likeIDENTICAL for base letter differences, but has some differences whenhandling control characters and accents. See the Collator javadocfor more information on these differences and decomposition mode rules.To work with Collator, you need to start by getting one. You can eithercall getInstance() to get one for the default locale, or pass thespecific Locale to the getInstance() method to get a locale forthe one provided. For instance, to get one for the Spanish language,you would create a Spanish Locale with new Locale("es") and thenpass 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 thesort() routine of Collections to get your sorted List: Collections.sort(list, esCollator);Working with the earlier list, that now gives you a propersorting with the Spanish alphabet: first man many mañana maxi nextHad you instead used the US Locale for the Collator, mañana wouldappear 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 requiredimport 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. TheCollator returned by the getInstance() call is typically an instanceof RuleBasedCollator for the supported languages. You can useRuleBasedCollator to define your own collation sequences. The javadocfor the class describes the rule syntax more completely, but lets sayyou had a four character alphabet and wanted the order of the lettersto 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 lettercases 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 newrules:> javac Rule.java> java Rule[cafe, ace, face, ef]After reading the rule syntax some more in the javadocs, try to expand thealphabet and work with the different diacritical marks, too.Now, when developing programs for the global world, your programscan be better prepared to suit the local user. Be sure to keep stringsin resource bundles, too, as shown in an earlier tip:Earlier tip\*\*\*\*\*\*\*\*\*Connect and Participate With GlassFishTry 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

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 internationalmarket....

TechTip

Quiz Answers

Answers1. Given two objects one and two of BigDecimal type, how do you multiply the two factors to calculate a product in object three? Answer: DTo 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: CUse 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: BThe 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: CZ-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: CAlthough 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.

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 BigDecimalt...

TechTip

January 2008 Core Java Tech Tips Quiz

by John ZukowskiWe'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. Collection4. 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.

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...

TechTip

Placing Components on Tabs

by John ZukowskiThe JTabbedPane component is a special Swingcontainer that accepts components to be placed within a panel foreach tab. Select a tab to see that tab's components. Technicallyspeaking, one component is associated with each tab. However, thatcomponent is typically a panel that contains others. Identificationfor each tab is done with title text and an icon, settable per tab.The first Core Java Technologies Tech Tip to discuss theJTabbedPane component was published in July 2001, witha simple introductory tip. The methods of JTabbedPane to support the use ofcomponents 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 secondgets it back; and the last one tells you which tab is associatedwith the component, if any. Typically, you use only the firstmethod, but the others are available. To get started with the task at hand, it is helpful to have anIcon implementation that draws a little x on it. You could just usethe letter x as the button label, but you should avoid that option.Typically, when a user sees an x in a box, thissignals a command to close a window, so it serves as a goodindicator 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 theprogram's framework, one that creates a frame with aJTabbedPane 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

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. Technicallyspeaking...

News

Tech Days Comes Back to the USA

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

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...

News

New Certification Courses

New Certification CoursesSun 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 PlaygroundJoin 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.\*\*\*\*\*\*

New Certification CoursesSun Certified Programmer for the Java Platform, Standard Edition 6-- The Sun Certified Programmer for Java Platform, Standard Edition 6 certification exam is for programmers...

TechTip

Using Callable to Return Results From Runnables

by John ZukowskiThe Runnable interface has been around since thebeginning of time for the Java platform. It allows you to define atask to be completed by a thread. As most people probably knowalready, it offers a single method run() that acceptsno arguments and returns no values, nor can it throw any checkedexceptions. If you need to get a value back from the now-completedtask, you must use a method outside the interface and wait for somekind of notification message that the task completed. For example,the following demonstrates what you might do for just such ascenario: 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 donedifferently now, thanks to the Callable interfaceintroduced in J2SE 5.0. Instead of having a run()method, the Callable interface offers acall() method, which can return an Object or, morespecifically, any type that is introduced in the genericized form: public interface Callable<V> { V call() throws Exception; }Because you cannot pass a Callable into aThread to execute, you instead use theExecutorService to execute the Callableobject. The service accepts Callable objects to run byway of the submit() method: <T> Future<T> submit(Callable<T> task)As the method definition shows, submitting a Callable object tothe ExecutorService returns a Futureobject. The get() method of Future willthen block until the task is completed. This is the equivalent ofthe join() call in the first example. Actually, it isthe equivalent of both the join() call and the getvalue call as get() returns the value calculated by theCallable instance.To demonstrate, the following example creates separateCallable instances for each word passed in on thecommand line and sums up their length. Each Callablewill just calculate the sum of its individual word. The set ofFuture objects are saved to acquire the calculatedvalue from each. If the order of the returned values needed to bepreserved, 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 theword's length as the value returned by the call()method. This value could take some time to generate but in this caseis known immediately. The only requirement of call() isthe value is returned at the end of the call. When theget() method of Future is later called,the Future will either have the value immediately ifthe task runs quickly, as in this case, or will wait until the valueis done generating. Multiple calls to get() will notcause the task to be rerun in the thread.Because the goal of the program is to calculate the sum of allword lengths, it doesn't matter in which order theCallable tasks finish. It is perfectly OK if the lasttask completes before the first three. The first get()call to Future will just wait for the first task in theSet to complete. This does not block other tasks fromrunning to completion separately. It is just waiting for that onethread or task to complete.This particular example uses a fixed-size thread pool for theExecutorService, 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. TheSwingWorker class is another example of aRunnable object that works with a Future,though in a slightly different way. See the Worker Threads and SwingWorker lesson for more information onthat.\*\*\*\*\*\*\*\*\*\*\*\*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

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 knowalread...

TechTip

Using the MBeanRegistration Interface to Manage MBean Life Cycles

By Daniel FuchsThe 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 InterfaceThe 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 CaseLet'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 InterfaceThe 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 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(...); ...ConclusionThis 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 AuthorDaniel 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.

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_H...

News

Getting to Know System Tray

by John ZukowskiThe Java SE 6 release introduced several new capabilities to the Abstract Window Toolkit (AWT). Users are no longer limited to Swing and enhancing the graphical user interface (GUI) components. With Java SE 6, you have new access to things such as desktop applications through what used to be called the JDesktop Integration Components (JDIC), described in a previous tip: Communicating With Native Applications Using JDIC. Although its package changed to just java.awt, the API remains much as described in that tip. You also now have the ability to add applications to the Microsoft Windows taskbar, the Gnome notification area, or KDE's system tray. That is what this tip is all about.Access to the system tray requires only two new classes: SystemTray to represent the desktop's system tray and TrayIcon for its icon. Why not use Image or Icon for that? TrayIcon has an attached PopupMenu and tool tip text. Here's how all the pieces fit together.The SystemTray class builds on the concept of the platform having a single instance of the tray. Calling the getSystemTray() method of SystemTray will return that instance. But some platforms might not support a system tray, so it is best to check first through the isSupported() method. Otherwise, an UnsupportedOperationException will be thrown if the platform does not support a system tray. Here's the typical pattern you'll need for acquiring that system tray instance: if (SystemTray.isSupported()) { SystemTray tray = SystemTray.getSystemTray(); }Most of the common platforms -- Microsoft Windows, Solaris Operating System, and Linux -- will support a system, but some platforms may not.After getting the SystemTray, TrayIcon comes into play. You get to add one or more of those to the tray, one for each menu and tool tip you wish to add, with of course an Image.Getting started with a basic framework to use SystemTray gives you the following program. The program puts a single TrayIcon on the SystemTray with a single MenuItem on its PopupMenu. In the installed JDK directory, there is a demo subdirectory. You can get an icon from that subdirectory. Any supported format will do.import javax.swing.\*;import java.awt.\*;public class BasicTray { public static void main(String args[]) { Runnable runner = new Runnable() { public void run() { if (SystemTray.isSupported()) { SystemTray tray = SystemTray.getSystemTray(); Image image = Toolkit.getDefaultToolkit().getImage("gifIcon.gif"); PopupMenu popup = new PopupMenu(); MenuItem item = new MenuItem("A MenuItem"); popup.add(item); TrayIcon trayIcon = new TrayIcon(image, "The Tip Text", popup); try { tray.add(trayIcon); } catch (AWTException e) { System.err.println("Can't add to tray"); } } else { System.err.println("Tray unavailable"); } } }; EventQueue.invokeLater(runner); }}Resting your mouse over the icon shows the tool tip text. Clicking the icon pops the menu up, showing the menu item. Had there been an Action/ActionListener added to the MenuItem, it would have been activated upon selection.Besides the ability to create a big frame for your application when you select the menu item, one other important method of TrayIcon is worth mentioning: displayMessage(). Calling this method will show a pop-up message near the icon on the system tray. The method takes three arguments: the title for the pop-up window, which can be null; the message itself; and a MessageType to indicate the type of message. Valid types are ERROR, WARNING, INFO, or NONE. All but NONE have their own icon added to the pop-up window. The icon is a smaller version of the one shown in a JOptionPane.The following program adds a few more menu items to the earlier example to demonstrate the displayMessage() method and all its message types. The final menu item with Close uses the remove() method of SystemTray to remove the TrayIcon from the SystemTray and shuts down. import javax.swing.\*;import java.awt.\*;import java.awt.event.\*;public class FullTray { static class ShowMessageListener implements ActionListener { TrayIcon trayIcon; String title; String message; TrayIcon.MessageType messageType; ShowMessageListener( TrayIcon trayIcon, String title, String message, TrayIcon.MessageType messageType) { this.trayIcon = trayIcon; this.title = title; this.message = message; this.messageType = messageType; } public void actionPerformed(ActionEvent e) { trayIcon.displayMessage(title, message, messageType); } } public static void main(String args[]) { Runnable runner = new Runnable() { public void run() { if (SystemTray.isSupported()) { final SystemTray tray = SystemTray.getSystemTray(); Image image = Toolkit.getDefaultToolkit().getImage("gifIcon.gif"); PopupMenu popup = new PopupMenu(); final TrayIcon trayIcon = new TrayIcon(image, "The Tip Text", popup); MenuItem item = new MenuItem("Error"); item.addActionListener(new ShowMessageListener(trayIcon, "Error Title", "Error", TrayIcon.MessageType.ERROR)); popup.add(item); item = new MenuItem("Warning"); item.addActionListener(new ShowMessageListener(trayIcon, "Warning Title", "Warning", TrayIcon.MessageType.WARNING)); popup.add(item); item = new MenuItem("Info"); item.addActionListener(new ShowMessageListener(trayIcon, "Info Title", "Info", TrayIcon.MessageType.INFO)); popup.add(item); item = new MenuItem("None"); item.addActionListener(new ShowMessageListener(trayIcon, "None Title", "None", TrayIcon.MessageType.NONE)); popup.add(item); item = new MenuItem("Close"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { tray.remove(trayIcon); } }); popup.add(item); try { tray.add(trayIcon); } catch (AWTException e) { System.err.println("Can't add to tray"); } } else { System.err.println("Tray unavailable"); } } }; EventQueue.invokeLater(runner); }}Compile and run the program to have the tray icon added to the system tray. On a Microsoft Windows box, you'll need to right click on the icon to see the pop-up menu. Select each icon to see each message displayed, and select Close when you are done. Removing the tray icon essentially causes the event dispatch thread to end, allowing the program to exit.\*\*\*\*\*\*\*jMaki is a lightweight framework that makes it easy to build Ajax-based web applications. Learn more in this Deep Dive with jMaki Project lead, Greg Murray. \*\*\*\*\*\*\*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.

by John Zukowski The Java SE 6 release introduced several new capabilities to the Abstract Window Toolkit (AWT). Users are no longer limited to Swing and enhancing the graphical user interface (GUI)...

News

Socket Logging

by John ZukowskiA 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.ConsoleHandlerBut the file also includes a default FileHandler definition:java.util.logging.FileHandler.pattern = %h/java%u.logjava.util.logging.FileHandler.limit = 50000java.util.logging.FileHandler.count = 1java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatterAnd 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.ConsoleHandlerThe 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 Loggerwith 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 aboutNetBeans 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.htmlYou can find more information at:http://jdk6.dev.java.net/ProjectHamburg.html

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 Locali...

Sun

Writing toString Methods & Tech Days

by Glen McCluskeyOne 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 testingYou might wonder how it's possible to print an arbitrary classobject. 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, theString.valueOf method converts the object to a string. String.valueOf first checks to make sure that the reference isnot 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 stringvalue? The format is illustrated in the second print statementabove. The name of the class, an "@", and the hex version of theobject's hashcode are concatenated into a string and returned. Thedefault hashCode method in Object is typically implemented byconverting 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 . TSDemo1If you look at the bytecode, you'll notice that part of itinvolves creating a StringBuffer object, and then usingStringBuffer.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 objectto 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 37The 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 isa 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 47This 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 beparticularly 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 DaysThe 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. 19Shanghai, China Oct. 23-25Beijing, China Nov. 1-3Tokyo, Japan Nov. 6-8Frankfurt, Germany Dec. 3-5 See the Sun Tech Days website for more information about a Tech Days near you.

by Glen McCluskey One of the standard methods defined in java.lang.Object istoString. This method is used to obtain a string representationof an object. You can (and normally should) override...

TechTip

The Preferences API

The author of this tip is John Zukowski, president and principalconsultant of JZ Ventures, Inc. The Preferences API was first covered here shortly after it was introducedwith 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 settinguser specific settings. There are system preferences, import and exportpreferences, and event notifications associated with preferences. Thereis even a way to provide your own custom location for storage ofpreferences. 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. Thefirst set is for the individual user, allow multiple users on the samemachine to have different settings defined. These are called userpreferences. Each user who shares the same machine can have his or her own unique set of values associated with a group of preferences. Somethinglike this could be like a user password or starting directory. You don'twant every person on the same machine to have the same password and homedirectory. Well, I would hope you don't want that.The other form of preferences is the system type. All users of a machineshare the same set of system preferences. For instance, the locationof an installed printer would typically be a system preference. Youwouldn't necessarily have a different set of printers installed fordifferent users. Everyone running on one machine would know about allprinters 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 preferencewould 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 isidentical.The example is a simple game, using the game term loosely here. It picksa random number from 0 to 99. If the number is higher than the previouslysaved number, it updates the "high score." The example also shows thecurrent high score. The Preferences API usage is rather simple. Theexample just gets the saved value with getSavedHighScore(), providinga default of -1 if no high score had been saved yet, andupdateHighScore(int value) to store the new high score. The HIGH_SCOREkey 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 isnot apt to be your high score, but it certainly could be.You can try running the application as different users to see that they allshare the same high score. Import and ExportIn the event that you wish to transfer preferences from one user to anotheror from one system to another, you can export the preferences from that oneuser/system, and then import them to the other side. When preferences areexported, they are exported into an XML formatted document whose DTD isspecified by http://java.sun.com/dtd/preferences.dtd, though you don'treally need to know that. You can export either a whole subtree withthe exportSubtree() method or just a single node with the exportNode()method. Both methods accept an OutputStream argument to specify where tostore things. The XML document will be UTF-8 character encoded. Importingof the data then happens via the importPreferences() method, which takesan InputStream argument. From an API perspective, there is no differencein importing a system node/tree or a user node.Adding a few lines of code to the previous example will export the newlyupdated high score to the file high.xml. Much of the added code isresponsible for launching a new thread to save the file and for handlingexceptions. 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". Thisstates the type of node it is. The node also has a name attributevalued at "<unnamed>". Since the High class was not placed in a package,you get to work in the unnamed system node area. The entry attributeprovide the current high score value, 95 in the example here, thoughyour value could differ.While we won't include any import code in the example here, theway to import is just a static method call on Preferences, passingin the appropriate input stream: FileInputStream fis = new FileInputStream("high.xml"); Preferences.importPreferences(fis); fis.close(); Since the XML file includes information about whether the preferencesare system or user type, the import call doesn't have to explicitlyinclude this bit of information. Besides the typical IOExceptions thatcan happen, the import call will throw an InvalidPreferencesFormatExceptionif the file format is invalid. Exporting can also throw aBackingStoreException if the data to export can't be read correctlyfrom the backing store.Event NotificationsThe original version of the High game updated the high scorepreference, then explicitly made a call to update the label on thescreen. A better way to perform this action would be to add a listener to the preferences node, then a value change canautomatically trigger the label to update its value. That way, if the high score is ever updated from multiple places, you won't needto remember to add code to update the label after saving the updatedvalue. 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 PreferenceChangeEventfor just such a task. The listener will be notified for all changes to theassociated 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, newnew value, and the node itself. The new value doesn't have all the conveniencemethods of Preferences though. For example, you can't retrieve thevalue as an int. Instead you must manually convert the valueyourself. 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 aNodeChangeListener and NodeChangeEvent combo for notification ofpreference changes. However, these are for notification nodesadditions and removals, not changing values of specific nodes. Of course, if youare writing something like a Preferences viewer, clearly you'd want to knowif/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 lifeof your application without having to rely on a database system. For more informationon the API, see the article Sir, What is Your Preference?

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 introducedwith the 1.4 version of the...

TechTip

Using Enhanced For-Loops with Your Classes

The author of this tip is John Zukowski, president and principalconsultant of JZ Ventures, Inc.The enhanced for-loop is a popular feature introduced with the Java SEplatform in version 5.0. Its simple structure allows one tosimplify code by presenting for-loops that visit each element ofan array/collection without explicitly expressing how one goes fromelement to element. Because the old style of coding didn't become invalid with the new for-loop syntax, you don't have to use anenhanced for-loop when visiting each element of anarray/collection. However, with the new style, one's code wouldtypically change from something like the following:for (int i=0; i < array.length; i++) { System.out.println("Element: " + array[i]);}to the newer formfor (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 loopsthrough 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 Penguinobjects, without doing anything extra to get the enhanced for-loop towork, one way you would loop through each penguin element would be toreturn an Iterator and iterate through the colony. Unfortunately, theenhanced for-loop does not work with Iterator, so the following won't even compile: // Does not compileimport 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 2ndline 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 errorIn order to be able to use your class with an enhanced for-loop, itdoes need an Iterator, but that Iterator must be provided via theIterable 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 thedesignated 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 forloop. By making the Colony class implement Iterable, and havingits new iterator() method return the Iterator that getPenguins()provides, you'll be able to loop through the penguins in thecolony 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 Settype collection so the returned order doesn't necessarily match theinsertion order, which in this case it doesn't.Remember to genericize the implements clause for the class "implementsIterable<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 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 SEplatform in version 5.0. Its...

TechTip

The Attach API

by John ZukowskiWhen 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 findsupport for extending the javadoc tool and an API called the AttachAPI.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 monitorwhat's going on and potentially detect problems before theyhappen. The Attach API classes are found in the com.sun.tools.attachand 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 areexception classes and one a permission. That doesn't leave much tolearn about, only VirtualMachine and its associatedVirtualMachineDescriptor class.The VirtualMachine class represents a specific Java virtual machine(JVM) instance. You connect to a JVM by providing the VirtualMachineclass with the process id, and then you load a management agent to doyour customized behavior:VirtualMachine vm = VirtualMachine.attach (processid);String agent = ...vm.loadAgent(agent);The other manner of acquiring a VirtualMachine is to ask for the listof virtual machines known to the system, and then pick the specificone 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 thefollowing, assuming the arguments are passed into the agent's argsvariable. String options[] = args.split(",");for (String option: options) System.out.println(option);}The second thing to mention is how to detach the current virtual machinefrom the target virtual machine. That's done via the detachmethod. After you load the agent with loadAgent, you should callthe detach method.A JMX agent exists in the management-agent.jar file that comes withthe JDK. Found in the same directory as tools.jar, the JMX managementagent allows you to start the remote JMX agent's MBean Server and getan MBeanServerConnection to that server. And, with that, you can listthings like threads in the remote virtual machine. The following program does just that. First, it attaches to theidentified virtual machine. It then looks for a running remote JMXserver and starts one if not already started. The management-agent.jar file is specified by finding the java.home of the remote virtualmachine, not necessarily the local one. Once connected, theMBeanServerConnection is acquired, from which you query theManagementFactory 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.javaFrom here, you could run the program, but there is nothing to attach to. So, here's a simple Swing program that displays a frame. Nothingfancy, 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 runtimelocation so the system can connect to the remote virtual machine.Launch MyFrame with the typical startup command:> java MyFrameThen, 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 directoryyou are using. Your output, specifically the process ids, willprobably be different: > jps5156 Jps4276 MyFrameSince the jps command is itself a Java program, it shows up in thelist, too. Here, 4276 is what is needed to pass into the Threadsprogram. Your id will most likely be different. Running Threads thendumps the list of running threads: > java -cp %JAVA_HOME%/lib/tools.jar;. Threads 4276JMX server connection timeout 18 / TIMED_WAITINGRMI Scheduler(0) / TIMED_WAITINGRMI TCP Connection(1)-192.168.0.101 / RUNNABLERMI TCP Accept-0 / RUNNABLEDestroyJavaVM / RUNNABLEAWT-EventQueue-0 / WAITINGAWT-Windows / RUNNABLEAWT-Shutdown / WAITINGJava2D Disposer / WAITINGAttach Listener / RUNNABLESignal Dispatcher / RUNNABLEFinalizer / WAITINGReference Handler / WAITINGYou can use the JMX management agent to do much more than just listthreads. For instance, you can call the findDeadlockedThreads methodof ThreadMXBean to find deadlocked threads. Creating your own agent is actually rather simple. Similar to howapplications 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 intoa JAR file and specify the Agent-Class in the manifest:Agent-Class: SecretAgentThe 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 referencethere 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.

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...

TechTip

Logging Localized Message

by John ZukowskiThe Logging API was last covered in the October 22, 2002 tip FilteringLogged 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-codedstring. Second, the message can take arguments. Internally, relying onMessageFormat, the logger will take any arguments passed in after thelog message string and use them to fill in any blanks in themessage. The first argument after the message string argument to log()will have index 0 and is represented by the string {0}. The nextargument 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 offormatting really comes in handy is when you treat the messageargument as a lookup key into a resource bundle. When you fetch aLogger, you can either pass in only the logger name, or both the nameand a resource bundle name. By combining messages fetched from a resource bundle with localarguments, you get all the benefits of localized, parameterizedmessages, not just in your programs, but in your log messages aswell. To treat the message argument as a lookup key to a resource bundle,the manner of fetching the Logger needs to change slightly. If youwant to use resource bundles, avoid creating a Logger object likethis: private static Logger logger = Logger.getLogger("com.example");Instead, add an optional second argument to the getLogger() call. Theargument 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 thegetLogger() 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, younow have the associated message text logged to the LoggingAPI. That message text can include those {0}-like arguments toget 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", sinceits 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 applicationthat shows localized logging. To keep things simple, these resource bundles will bePropertyResourceBundle 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, WorldMessage2=Hello, {0}The second language will be Spanish. Place the following in the file messages_ES.properties:Message1=Hola, mundoMessage2=Hola, {0}Now, we have to create the application. Notice that thegetAnonymousLogger() method also includes a second version thataccepts 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 messagessimilar to the following:> java LocalLogAug 4, 2007 12:00:35 PM LocalLog mainSEVERE: Hello, WorldAug 4, 2007 12:00:35 PM LocalLog mainSEVERE: Hello, JohnTo run the program with a different locale, set the user.language systemproperty on the command line:>java -Duser.language=ES LocalLogago 4, 2007 12:01:18 p.m. LocalLog mainGRAVE: Hola, mundoago 4, 2007 12:01:18 p.m. LocalLog mainGRAVE: Hola, JohnNotice that your log message contains the localizedresource 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 canuse resource bundles to provide both localized log messages and userinterface text. Those reading log files should be able to see translatedtext, too.For additional information on resource bundles, see the Resource BundleLoading tip and the Isolating Locale-Specific Data lesson in The Java Tutorial.

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...

TechTip

The need for BigDecimal

by John ZukowskiWorking 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.50Tax+Total: $94.55The 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.50225Tax+Total: 94.54725Looking 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 infinityDOWN which always rounds towards zero FLOOR which always rounds towards negative infinityUP which always rounds away from zeroHALF_DOWN which always rounds towards nearest neighbor, unless both neighbors are equidistant, in which case it rounds downHALF_UP which always rounds towards nearest neighbor, unless both neighbors are equidistant, in which case it rounds upUNNECESSARY which asserts exact result, with no rounding necessaryBefore 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.74In 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.735For 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 topoint out how to create one. There are 16 constructors for the class.Since you can't necessarily store the value of a BigDecimal in aprimitive 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.34999999999999997779553950749686919152736663818359375Instead, what you should do is create the BigDecimal directly with thestring ".35" as shown here: BigDecimal d = new BigDecimal(".35");resulting in the following output: .35 = 0.35After creating the value, you can explicitly set the scale of thenumber 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 calculationrequires working with another BigDecimal and setting its scale toensure the math operations work for dollars and cents. If you want todeal with partial pennies, you can certainly go to three decimalplaces 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 backif 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.54BigDecimal offers more functionality than what these examplesshow. There is also a BigInteger class for when you need unlimited precision using whole numbers. The Java platform documentation for the twoclasses offers more details for the two classes, includingmore details on scales, the MathContext class, sorting, and equality.

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...

TechTip

Listeners vs Adapters

by John ZukowskiThe JavaBeans component model (and thus the Swing component set) isbuilt 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 toreceive notification of their occurrence. Although working withproperties is simple, listener objects require a little extradiscussion to understand how they work, typically in the graphicaluser interface (GUI) world. Specifically, this tip describes the AWT and Swing event-related classes that offer both a listener interfaceand 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/WindowAdapterpackage java.awt.dnd- DragSourceListener/DragSourceAdapter- DragTargetListener/DragTargetAdapterpackage javax.swing.event- InternalFrameListener/InternalFrameAdapter- MouseInputListener/MouseInputAdapterThese class pairs offer two ways to do the same thing. First, considera simple example that doesn't offer an adapter class. TheActionListener 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 theActionListener 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 ofthe interface must provide an implementation of that single method forit to do much of anything.Other listener interfaces aren't quite so simplistic. For example, theMouseMotionListener interface has two methods: mouseDragged and mouseMoved. When you implement an interface, you must implement allthe 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 eventsfor a particular listener interface. Maybe your code only needs torespond to one or two of the methods in a listener interface. Forinstance, do you really want to know when a mouse moves, or only whenit moves with a mouse button depressed? You cannot implement just oneof 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 errorsince an interface isn't fully implemented. With an interfacelike MouseMotionListener, that isn't too much of a problem, youjust have to provide a stub for the method you aren't interestedin: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 interfaceimplementation 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 orreleased over a component, the other three methods will be empty andignored. Those methods are unnecessary code. The adapter classes canhelp reduce the amount of code you must write when your applicationneeds 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 anadapter 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 ofimplementing 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 yourown 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 thistip offer them. Also, the adapters are true classes, notinterfaces. If you want your custom JButton subclass to also implementMouseListener, you cannot have that class subclass MouseAdapter, as only single inheritance is allowed. For example, the following codecauses a compilation-time error because it attempts to subclass bothJButton 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 implementthe interface itself. This may be a good example of when you shouldcreate the listener as an inner or anonymous class instead.If you use an integrated development environment (IDE) to createyour user interface, the IDE will often generate the interfaceframework for you. You will need to code the business logic inside thenecessary interface methods. An IDE can simplify the implementation ofa large interface.For more information about this topic, read the How to Write a MouseListener lesson of The Java Tutorial.Adapters aren't limited to mouse listening. However, the MouseAdapteris a frequent example because the MouseListener interface has so manymethods. The WindowListener interface is also another large interface,and it has an associated WindowAdapter class.

by John Zukowski The JavaBeans component model (and thus the Swing component set) is built upon properties and events. Properties have setterand getter methods for working with their values....

TechTip

Adding Drop Support to JTree

by John ZukowskiOver 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 SwingThe 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, oreven 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_SELECTIONDropMode.ONDropMode.INSERTDropMode.ON_OR_INSERTHowever, 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.

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),...

TechTip

Cookie Handling in Java SE 6

by John ZukowskiBack in September 2005, the Cookie Management with CookieHandler tip described the J2SE 5.0 support for managing cookies over network connections. The 5.0 release provided the framework for dealing with these little nuggets of information, but the task of doing all the work was left to you, the developer. You only had to follow the defined API, but there was much work that needed to be done to actually get cookie management going well for your programs.A quick look back to the J2SE 5.0 API brings you right to the abstract CookieHandler class but with no real implementation, no storage mechanism, no storage policy, and nothing to store. Jump ahead to Java SE 6, and the CookieManager class offers just such an implementation of CookieHandler. CookieStore is the storage mechanism, and CookiePolicy offers a policy for accepting or rejecting cookies. Lastly, HttpCookie is the object to store.What used to be a difficult though doable task in J2SE 5.0, becomes simple construction of existing classes in Java SE 6. Here's the working J2SE 5.0 program for working with cookies:import java.io.\*;import java.net.\*;import java.util.\*;public class Fetch { public static void main(String args[]) throws Exception { if (args.length == 0) { System.err.println("URL missing"); System.exit(-1); } String urlString = args[0]; CookieHandler.setDefault(new ListCookieHandler()); URL url = new URL(urlString); URLConnection connection = url.openConnection(); Object obj = connection.getContent(); url = new URL(urlString); connection = url.openConnection(); obj = connection.getContent(); }}Notice the setDefault method call to CookieHandler in the middle. The ListCookieHandler class was the tip's implementation of a basic CookieHandler. The Java SE 6 version of the same program is nearly identical but has just one change to the following line:CookieHandler.setDefault(new CookieManager());You don't need to provide any additional code. It is really that simple to go between the different APIs. There are some other differences, but changing the one line in the Fetch program has the second network connection use the cookies returned from the first.Now for the differences. Java SE 6 has a CookiePolicy which offers the CookieManager the ability to accept all cookies, accept no cookies, or only accept cookies from the original host. To setup the CookieManager with a different policy, you use one of the constants in the CookiePolicy interface: ACCEPT_ALL, ACCEPT_NONE, or ACCEPT_ORIGINAL_SERVER. The last option is thedefault when none is set. To configure the cookie manager, just call its setCookiePolicy method with the appropriate constant as shown here:CookieManager manager = new CookieManager();manager.setCookiePolicy(CookiePolicy.ACCEPT_NONE);CookieHandler.setDefault(manager);In addition, you can call the second constructor for CookieManager that accepts both a CookieStore and CookiePolicy argument:CookieManager(CookieStore store, CookiePolicy cookiePolicy)Passing in a null store will have the system use the predefined in-memory version. You can also define your own implementation of the interface if you wish to offer longer term storage of cookies.Before showing the corrected version of the Fetch program, there is one more API difference to mention: how to get the list of cookies from the cookie jar, err..., manager. The CookieManager class has a getCookieStore method to get the CookieStore. You then ask the store for its List of cookies with the getCookies method, and can loop through the list.Here's a modified version of the earlier program that utilizes the Java SE 6 version of the cookie handling API. While the basic program hasn't changed much, the need for all the supporting custom classes is now no longer necessary.import java.io.\*;import java.net.\*;import java.util.\*;public class Fetch { public static void main(String args[]) throws Exception { Console console = System.console(); if (args.length == 0) { System.err.println("URL missing"); System.exit(-1); } String urlString = args[0]; CookieManager manager = new CookieManager(); manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); CookieHandler.setDefault(manager); URL url = new URL(urlString); URLConnection connection = url.openConnection(); Object obj = connection.getContent(); url = new URL(urlString); connection = url.openConnection(); obj = connection.getContent(); CookieStore cookieJar = manager.getCookieStore(); List cookies = cookieJar.getCookies(); for (HttpCookie cookie: cookies) { console.printf("Cookie: %s%n", cookie); } }}Just compile and run the program to see if visiting a particular URL will leave a cookie on your system. If you don't enable ACCEPT_ALL, visiting http://www.sun.com will not show any cookies, but enabling all will show three. Your run will produce different results.> java Fetch http://www.sun.comCookie: JROUTE=9999Cookie: JSESSIONID=999999995d3f13ffffffffc68433979b7f5b0Cookie: Starload=star-fep7Yes, going from the J2SE 5.0 cookie handling mechanism to Java SE 6 is that easy. You don't have to implement even one additional interface if the default functionality is sufficient. For a refresher on the difficulties involved in creating the J2SE 5.0 version of the handler, be sure to revisit the earlier tip.Be sure to try out the Tech Tips Quiz, found later in that same issue.

by John Zukowski Back in September 2005, the Cookie Management with CookieHandler tip described the J2SE 5.0 support for managing cookies over network connections.The 5.0 release provided the framework...

TechTip

Using printf with Customized Formattable Classes

by John ZukowskiJava SE 1.5 added the ability to format output using formatting strings like "%5.2f%n" to print a floating point number and a newline. An October 2004 tip titled Formatting Output with the New Formatter described this.The Formattable interface is an important feature but wasn't part of the earlier tip. This interface is in the java.util package. When your class implements the Formattable interface, the Formatter class can use it to customize output formatting. You are no longer limited to what is printed by toString() for your class. By implementing the formatTo() method of Formattable, you can have your custom classes limit their output to a set width or precision, left or right justify the content, and even offer different output for different locales, just like the support for the predefined system data types.The single formatTo() method of Formattable takes four arguments:public void formatTo(Formatter formatter, int flags, int width, int precision)The formatter argument represents the Formatter from which to get the locale and send the output when done. The flags parameter is a bitmask of the FormattableFlags set. The user can have a - flag to specify left justified (LEFT_JUSTIFY), \^ flag for locale-sensitive uppercase (UPPERCASE), and # for using the alternate (ALTERNATE) formatting.A width parameter represents the minimum output width, using spaces to fill the output if the displayed value is too short. The width value -1 means no minimum. If output is too short, output will be left justified if the flag isset. Otherwise, it is right justified.A precision parameter specifies the maximum number of characters to output. If the output string is "1234567890" with a precision of 5 and a width of 10, the first five characters will be displayed, with the remaining five positions filled with spaces, defining a string of width 10. Having a precision of -1 means there is no limit.A width or precision of -1 means no value was specified in the formattingstring for that setting.When creating a class to be used with printf and Formatter, you never call the formatTo() method yourself. Instead, you just implement the interface. Then, when your class is used with printf, the Formatter will call formatTo() for your class to find out how to display its value. To demonstrate, let us create some object that has both a short and long name that implements Formattable. Here's what the start of the class definition looks like. The class has only two properties, an empty implementation of Formattable, and its toString() method.import java.util.Formattable;import java.util.Formatter;public class SomeObject implements Formattable { private String shortName; private String longName; public SomeObject(String shortName, String longName) { this.shortName = shortName; this.longName = longName; } public String getShortName() { return shortName; } public void setShortName(String shortName) { this.shortName = shortName; } public String getLongName() { return longName; } public void setLongName(String longName) { this.longName = longName; } public void formatTo(Formatter formatter, int flags, int width, int precision) { } public String toString() { return longName + " [" + shortName + "]"; }}As it is now, printing the object with println() will display the long name, followed by the short name within square brackets as defined in the toString() method. Using the Formattable interface, you can improve the output. A better output will use the current property values and formattable flags. For this example, formatTo() will support the ALTERNATE and LEFT_JUSTIFY flags of FormattableFlags.The first thing to do in formatTo() is to find out what to output. For SomeObject, the long name will be the default to display, and the short name will be used if the precision is less than 7 or if the ALTERNATE flag is set. Checking whether the ALTERNATE flag is set requires a typical bitwise flag check. Be careful with the -1 value for precision because that value means nolimit. Check the range for the latter case. Then, pick the starting string based upon the settings.String name = longName;boolean alternate = (flags & FormattableFlags.ALTERNATE) == FormattableFlags.ALTERNATE;alternate |= (precision >= 0 && precision < 7);String out = (alternate ? shortName : name);Once you have the starting string, you get to shorten it down if necessary, based on the precision passed in. If the precision is unlimited or the string fits, just use that for the output. If it doesn't fit, then you need to trim it down. Typically, if something doesn't fit, the last character is replaced by a \*, which is done here.StringBuilder sb = new StringBuilder();if (precision == -1 || out.length()

by John Zukowski Java SE 1.5 added the ability to format output using formatting strings like "%5.2f%n" to print a floating point number and a newline. An October 2004 tip titled Formatting Output with...

TechTip

Creating ZIP and JAR Files

ZIP files offer a packaging mechanism, allowing multiple files to be bundled together as one. Thus, when you need to download a group of files from the web, you can package them into one ZIP file for easier transport as a single file. The bundling can include additional information like directory hierarchy, thus preserving necessary paths for an application or series of resources once unbundled.This tip will address three aspects of ZIP file usage: creating themadding files to themcompressing those added filesCreating Zip StreamsThe ZipOutputStream offers a stream for compressing the outgoing bytes. There is a single constructor for ZipOutputStream, one that accepts another OutputStream:public ZipOutputStream(OutputStream out)If the constructor argument is the type FileOutputStream, then the compressed bytes written by the ZipOutputStream will be saved to a file. However, you aren't limited to using the ZipOutputStream with a file. You can also use the OutputStream that comes from a socket connection or some other non-file-oriented stream. Thus, for a file-oriented ZipOutputStream, the typical usage will look like this:String path = "afile.zip"; FileOutputStream fos = new FileOutputStream(path);ZipOutputStream zos = new ZipOutputStream(fos);Adding EntriesOnce created, you don't just write bytes to a ZipOutputStream. Instead, you need to treat the output stream as a collection of components. Each component of a ZipOutputStream is paired with a ZipEntry. It is this ZipEntry that you create and then add to the ZipOutputStream before actually writing its contents to the stream.String name = ...; ZipEntry entry = new ZipEntry(name);zos.putNextEntry(entry);zos.write(>); Each entry serves as a marker in the overall stream, where you'll find thebytes related to the entry in the library file. After the ZIP file has beencreated, when you need to get the entry contents back, just ask for the relatedinput stream:ZipFile zip = new ZipFile("afile.zip"); ZipEntry entry = zip.getEntry("anEntry.name");InputStream is = zip.getInputStream(entry);Now that you've seen how to create the zip file and add entries to that file, it is important to point out that the java.util.zip libraries offer some level of control for the added entries of the ZipOutputStream. First, the order you add entries to the ZipOutputStream is the order they are physically located in the .zip file. You can manipulate the enumeration of entries returned back by the entries() method of ZipFile to produce a list in alphabetical or size order, but the entries are still stored in the order they were written to the output stream. Compressing FilesFiles added to a ZIP/JAR file are compressed individually. Unlike Microsoft CAB files which compress the library package as a whole, files in a ZIP/JAR file are each compressed or not compressed separately. Before adding a ZipEntry to the ZipOutputStream, you determine whether its associated bytes are compressed. The setMethod method of ZipEntry allows you to specify which of the two available compression formats to use. Use the STORED constant of ZipEntry to give you an uncompressed file and the DEFLATED settingfor a compressed version. You cannot control the compression efficiency. That depends on the type of data in the associated entry. Straight text can be compressed to around 80% of its size quite easily, whereas MP3 or JPEGdata will be compressed much less.While you might think it obvious that everything should be compressed, it does take time to compress and uncompress a file. If the task of compressing is too costly a task to do at the point of creation, it may sometimes be better to just store the data of the whole file in a STORED format, which just stores the raw bytes. The same can be said of the time cost of uncompression. Of course, uncompressed files are larger, and you have to pay the cost with either higher disk space usage or bandwidth when transferring file. Keep in mind that you need to change the setting for each entry, not the ZipFile as a whole. However, it is more typical to compress or not compress a whole ZipFile, as opposed to different settings for each entry.There is one key thing you need to know if you use the STORED constant for the compression method: you must explicitly set certain attributes of the ZipEntry which are automatically set when the entry is compressed. These are the size, compressed size, and the checksum of the entry's input stream. Assuming an input file, the size and compressed size can just be the file size. To compute the checksum, use the CRC32 class in the java.util.zip package. You cannot just pass in 0 or -1 to ignore the checksum value; the CRC value will be used to validate your input when creating the ZIP and when reading from it later.ZipEntry entry = new ZipEntry(name);entry.setMethod(ZipEntry.STORED);entry.setCompressedSize(file.length());entry.setSize(file.length());CRC32 crc = new CRC32();crc.update(>); entry.setCrc(crc.getValue());zos.putNextEntry(entry);To demonstrate, the following program will combine a series of files using the STORED compression method. The first argument to the program will be the ZIP file to create. Remaining arguments represent the files to add. If the ZIP file to create already exists, the program will exit without modifying thefile. If you add a non-existing file to the ZIP file, the program will skipthe non-existing file, adding any remaining command line arguments to the created ZIP. import java.util.zip.\*;import java.io.\*;public class ZipIt { public static void main(String args[]) throws IOException { if (args.length < 2) { System.err.println("usage: java ZipIt Zip.zip file1 file2 file3"); System.exit(-1); } File zipFile = new File(args[0]); if (zipFile.exists()) { System.err.println("Zip file already exists, please try another"); System.exit(-2); } FileOutputStream fos = new FileOutputStream(zipFile); ZipOutputStream zos = new ZipOutputStream(fos); int bytesRead; byte[] buffer = new byte[1024]; CRC32 crc = new CRC32(); for (int i=1, n=args.length; i < n; i++) { String name = args[i]; File file = new File(name); if (!file.exists()) { System.err.println("Skipping: " + name); continue; } BufferedInputStream bis = new BufferedInputStream( new FileInputStream(file)); crc.reset(); while ((bytesRead = bis.read(buffer)) != -1) { crc.update(buffer, 0, bytesRead); } bis.close(); // Reset to beginning of input stream bis = new BufferedInputStream( new FileInputStream(file)); ZipEntry entry = new ZipEntry(name); entry.setMethod(ZipEntry.STORED); entry.setCompressedSize(file.length()); entry.setSize(file.length()); entry.setCrc(crc.getValue()); zos.putNextEntry(entry); while ((bytesRead = bis.read(buffer)) != -1) { zos.write(buffer, 0, bytesRead); } bis.close(); } zos.close(); }} More InformationFor more information on JAR files, including how to seal andversion them, be sure to visit the "Packing Programs in JAR Files"lesson in The Java Tutorial.

ZIP files offer a packaging mechanism, allowing multiple files to be bundled together as one. Thus, when you need to download a group of filesfrom the web, you can package them into one ZIP file for...