| CARVIEW |
Select Language
HTTP/2 302
server: nginx
date: Wed, 04 Feb 2026 07:18:54 GMT
content-type: text/plain; charset=utf-8
content-length: 0
x-archive-redirect-reason: found capture at 20100420123221
location: https://web.archive.org/web/20100420123221/https://feeds.feedburner.com/corejavatechtips/
server-timing: captures_list;dur=0.774811, exclusion.robots;dur=0.067739, exclusion.robots.policy;dur=0.054188, esindex;dur=0.013279, cdx.remote;dur=6.028283, LoadShardBlock;dur=118.127020, PetaboxLoader3.datanode;dur=68.279799
x-app-server: wwwb-app212-dc8
x-ts: 302
x-tr: 154
server-timing: TR;dur=0,Tw;dur=0,Tc;dur=0
set-cookie: wb-p-SERVER=wwwb-app212; path=/
x-location: All
x-as: 14061
x-rl: 0
x-na: 0
x-page-cache: MISS
server-timing: MISS
x-nid: DigitalOcean
referrer-policy: no-referrer-when-downgrade
permissions-policy: interest-cohort=()
HTTP/2 200
server: nginx
date: Wed, 04 Feb 2026 07:18:55 GMT
content-type: text/xml; charset=UTF-8
x-archive-orig-etag: l2Cym56mYONQSj1JFYFxy1pCwxM
x-archive-orig-last-modified: Fri, 16 Apr 2010 03:03:27 GMT
x-archive-orig-date: Tue, 20 Apr 2010 12:32:21 GMT
x-archive-orig-expires: Tue, 20 Apr 2010 12:32:21 GMT
x-archive-orig-cache-control: private, max-age=0
x-archive-orig-x-content-type-options: nosniff
x-archive-orig-x-xss-protection: 1; mode=block
x-archive-orig-server: GSE
cache-control: max-age=1800
x-archive-guessed-content-type: text/xml
x-archive-guessed-charset: utf-8
memento-datetime: Tue, 20 Apr 2010 12:32:21 GMT
link: ; rel="original", ; rel="timemap"; type="application/link-format", ; rel="timegate"
content-security-policy: default-src 'self' 'unsafe-eval' 'unsafe-inline' data: blob: archive.org web.archive.org web-static.archive.org wayback-api.archive.org athena.archive.org analytics.archive.org pragma.archivelab.org wwwb-events.archive.org
x-archive-src: 52_15_20100420095956_crawl100-c/52_15_20100420123123_crawl102.arc.gz
server-timing: captures_list;dur=2.436882, exclusion.robots;dur=0.023432, exclusion.robots.policy;dur=0.011132, esindex;dur=0.011767, cdx.remote;dur=49.268461, LoadShardBlock;dur=58.131222, PetaboxLoader3.datanode;dur=85.393990, load_resource;dur=132.154688, PetaboxLoader3.resolve;dur=53.369439
x-app-server: wwwb-app212-dc8
x-ts: 200
x-tr: 268
server-timing: TR;dur=0,Tw;dur=0,Tc;dur=1
x-location: All
x-as: 14061
x-rl: 0
x-na: 0
x-page-cache: MISS
server-timing: MISS
x-nid: DigitalOcean
referrer-policy: no-referrer-when-downgrade
permissions-policy: interest-cohort=()
Core Java Technologies Tech Tips
Tips for using core Java SE APIs
https://blogs.sun.com/CoreJavaTechTips/feed/entries/atom
2009-09-30T10:09:45-07:00
Apache Roller
https://blogs.sun.com/CoreJavaTechTips/entry/superduper_slow_jar
Superduper Slow Jar Command
Christine Dorffi
2009-05-15T14:08:04-07:00
2009-05-15T14:08:04-07:00
Xueming Shen explains how peeking into the Jar source code (after many years) turned up an opportunity to rev the look-up operation.
<a href="#bio">By Xueming Shen</a>
<p>It's well known that creating a Jar file can be a "little" slow. How slow? On my aged SunBlad1000, it takes about 1 minute and 40 seconds to jar the whole rt.jar in cf0M mode (no compress, no manifest) -- and it costs you a little more if done in compress mode.
<p>But then we figured we were talking about creating jars for ten of thousands of classes with a total size of over 50M. Given the number of files and the total size, it seemed a reasonable amount of time. So, until now, we assumed it really needed that time — until someone accidentally noticed that "the CPU went to 100% busy for quite some time, perhaps a minute or more on my laptop, before starting to hit the disk to create the Jar archive."
<p>That sounds strange, as the main job the Jar is supposed to do is to copy and compress the files (into the Jar). Thus it should hit the disk from the very beginning to the end.
<p>So I peeked into the Jar source code (after many years), and it turned out we had a very embarassing bug in the jar code: We were doing a <code>O(n)</code> look-up on a Hashtable (via the <code>contains()</code> method) for each and every file we were jarring, where it really should be a <code>O(1)</code> look-up operation with a HashSet. Given the number of files the command is working on, this simple mistake caused us to spend the majority of the time (that 1 min 40+ seconds) in collecting the list of files that need to jar, instead of the real "jarring" work. Sigh:-(
<p>With that fixed (in JDK 7 build44 and later), the Jar is now much faster.
<p>Following are the quick time-measure numbers of 10 runs of jarring/zipping the rt.jar/zip, in Store Only mode and Zip Compression mode.
<p><ul>
<li> b43: the JDK 7/build43, which does not have the fix.</li>
<li> b47: the JDK 7/build47, which does have the fix.</li>
<li> zip: the default zip installed on my Solaris, which is zip2.3/1999</li>
</ul></p>
<table width="60%" border="1" cellpadding="2">
<tr><td colspan="3"><code>jar cf0M / zip -r0q</code> (store, no zip compression)</td></tr>
<tr>
<th>Build 43</th>
<th>Build 47<sup>1</sup></th>
<th>Zip </th></tr>
<tr><td>1:43.7 </td><td>20.6 </td><td>10.2 </td></tr>
<tr><td>1:40.3 </td><td>20.2 </td><td>9.2 </td></tr>
<tr><td>1:40.1 </td><td>21.0 </td><td>9.0 </td></tr>
<tr><td>1:40.5 </td><td>19.6 </td><td>10.4 </td></tr>
<tr><td>1:40.9 </td><td>19.6 </td><td>8.7 </td></tr>
<tr><td>1:40.2 </td><td>19.6 </td><td>9.1 </td></tr>
<tr><td>1:40.0 </td><td>18.6 </td><td>10.0 </td></tr>
<tr><td>1:39.1 </td><td>20.0 </td><td>8.6 </td></tr>
<tr><td>1:41.3 </td><td>18.5 </td><td>9.0 </td></tr>
<tr><td>1:42.1 </td><td>19.6 </td><td>9.6 </td></tr>
<tr><td colspan="3"><code>jar cfM/zip -rq</code> (with zip compression)</td></tr>
<tr><th>Build 43 </th> <th>Build 47<sup>1</sup><br></th> <th>Zip</th></tr>
<tr><td>1:47.0</td> <td>25.3</td> <td>15.7</td></tr>
<tr><td>1:45.9</td> <td>23.4</td> <td>14.2</td></tr>
<tr><td>1:44.7</td> <td>23.3</td> <td>14.9</td></tr>
<tr><td>1:45.4</td> <td>23.7</td> <td>14.3</td></tr>
<tr><td>1:45.6</td> <td>23.3</td> <td>14.3</td></tr>
<tr><td>1:44.9</td> <td>23.6</td> <td>14.0</td></tr>
<tr><td>1:45.9</td> <td>23.2</td> <td>14.6</td></tr>
<tr><td>1:44.0</td> <td>23.0</td> <td>14.2</td></tr>
<tr><td>1:44.9</td> <td>23.3</td> <td>14.8</td></tr>
<tr><td>1:45.8</td> <td>23.5</td> <td>14.2</td></tr>
</table>
<sup>1</sup> The fix is in JDK 7 only, for now.<br>
<p><a href="https://cr.openjdk.java.net/~sherman/6496274/src/share/classes/sun/tools/jar/Main.java.sdiff.html">This page</a> contains details of the fix.</p>
<p>We are making much progress on the Jar tool, and it is performing much better, though it is still slower compared to the Zip command. So we will continue our efforts going forward. I have to admit I do have some code that make Jar processing time much closer to Zip, but it will take time to make it into the product. Stay tuned!</p>
<p><a name="bio"></a><i>Xueming Shen is an engineer at Sun Microsystems, working in the Java core technologies group.</i></p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/TNqL3XvYkaU" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/superduper_slow_jar
https://blogs.sun.com/CoreJavaTechTips/entry/the_overhaul_of_java_utf
Overhauling the Java UTF-8 charset
Christine Dorffi
2009-03-16T12:24:38-07:00
2009-04-30T11:54:09-07:00
Sun engineer Xueming Shen talks about why the UTF-8 charset implementation in JDK 7 now rejects non-shortest-form UF-8 byte sequences, and why you should embrace the change. Two words: <i>security</i> and <i>performance</i>.
<p><i><a href="#bio">By Xueming Shen</a></i></p>
<p>The UTF-8 charset implementation, which is available in all JDK/JRE releases from Sun, has been updated recently to reject non-shortest-form UTF-8 byte sequences. This is because the old implementation might be leveraged in security attacks. Since then I have been asked many times about what this "non-shortest-form" issue is and what the possible impact might be, so here are some answers.</p>
<p><b>The first question usually goes: "What is the non-shortest-form issue"?</b></p>
<p>The detailed and official answer is at <a href="https://www.unicode.org/versions/corrigendum1.html">Unicode Corrigendum #1: UTF-8 Shortest Form</a>. 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:</p>
<table width="70%" cellspacing="5">
<tr>
<td width="5%" align="left"><b>#</b></td>
<td width="5%" align="left"><b>Bits</b></td>
<td colspan="4" width="60%" aligh="left"><b>Bit pattern</b></td>
</tr>
<tr>
<td width="5%">1</td>
<td width="5%">7</td>
<td width="15%">0xxxxxxx</td>
<td width="15%"> </td>
<td width="15%"> </td>
<td width="15%"> </td>
</tr>
<tr>
<td>2</td>
<td>11</td>
<td>110xxxxx</td>
<td>10xxxxxx</td>
<td> </td>
<td> </td>
</tr>
<tr>
<td>3</td>
<td>16</td>
<td>1110xxxx</td>
<td>10xxxxxx</td>
<td>10xxxxxx</td>
<td> </td>
</tr>
<tr>
<td>4</td>
<td>21</td>
<td>11110xxx</td>
<td>10xxxxxx</td>
<td>10xxxxxx</td>
<td>10xxxxxx</td>
</tr>
</table>
<p>The pattern is close, but it's actually <i>wrong</i>, 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.</p>
<p>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 <code>0x00..0x7f</code> (in one-byte form) in UTF-8. Based on the preceding pattern, however, these characters can also be represented in 2-bytes form as <code>[c0, 80]..[c1, bf]</code>, the "non-shortest-form".</p>
<p>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).
<pre><code>
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");
}
}
</code></pre></p>
<p>The output would be as follows:
<pre><code>
...
[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]
...
</code></pre></p>
<p>So, for a string like "ABC", you would have two forms of UTF-8 sequences:
<pre><code>
"0x41 0x42 0x43" and "0xc1 0x81 0xc1 0x82 0xc1 0x83"
</code></pre></p>
<p>The <a href="https://www.unicode.org/versions/corrigendum1.html">Unicode Corrigendum #1: UTF-8 Shortest Form</a> 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 <i>illegal</i>."</p>
<p>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.
<pre><code>
/* 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
*/
</code></pre></p>
<p><b>The next question usually is: "What would be the issue/problem if we keep using the old version of JDK/JRE?"</b></p>
<p>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 <i>not</i> 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".</p>
<p>A simple scenario that might give you an idea about what the above "may be leveraged to attack..." really means:
<ol>
<li> A Java application would like to filter the incoming UTF-8 input stream to reject certain key words, for example "ABC".</li>
<li> 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:
<pre><code>
String utfStr = new String(bytes, "UTF-8");
if ("ABC".equals(strUTF)) { ... }
</code></pre>
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.</li>
<li> 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.</li></ol>
<p>So the recommendation is: Update to the latest JDK/JRE releases to avoid the potential risk.</p>
<p><b>Wait, there is another big bonus for updating: performance.</b></p>
<p>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.</p>
<p>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 <code>-server vm</code>. (This is <b>not</b> an official benchmark: it is provided only to give a rough idea of the performance boost.)</p>
<p>The new implementation is much faster, especially when decoding or encoding single bytes (those ASCIIs). The new decoding and encoding are faster under <code>-client vm</code> as well, but the gap is not as big as in <code>-server vm</code>. I wanted to show you the best:-)</p>
<pre><code>
Method Millis Millis(OLD)
Decoding 1b UTF-8 : 1786 12689
Decoding 2b UTF-8 : 21061 30769
Decoding 3b UTF-8 : 23412 44256
Decoding 4b UTF-8 : 30732 35909
Decoding 1b (direct)UTF-8 : 16015 22352
Decoding 2b (direct)UTF-8 : 63813 82686
Decoding 3b (direct)UTF-8 : 89999 111579
Decoding 4b (direct)UTF-8 : 73126 60366
Encoding 1b UTF-8 : 2528 12713
Encoding 2b UTF-8 : 14372 33246
Encoding 3b UTF-8 : 25734 26000
Encoding 4b UTF-8 : 23293 31629
Encoding 1b (direct)UTF-8 : 18776 19883
Encoding 2b (direct)UTF-8 : 50309 59327
Encoding 3b (direct)UTF-8 : 77006 74286
Encoding 4b (direct)UTF-8 : 61626 66517
</code></pre>
<p>The new UTF-8 charset implementation has been integrated in
<a href="https://download.java.net/openjdk/jdk7/">JDK7</a>, <a href="https://download.java.net/openjdk/jdk6/">Open JDK 6</a>, <a href="https://java.sun.com/javase/downloads/?intcmp=1281">JDK 6 update 11</a> and later, JDK5.0u17, and 1.4.2_19.</p>
<p>If you are interested in what the change looks like, you can take a peek at the webrev of the new <a href="https://cr.openjdk.java.net/~sherman/4486841/webrev/src/share/classes/sun/nio/cs/UTF_8.java.sdiff.html">UTF_8.java</a> for OpenJDK7.</p>
<a name="bio"></a>
<p><i>Xueming Shen is an engineer at Sun Microsystems, working in the Java core technologies group.</i></p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/MxO_Sc_X4Pk" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/the_overhaul_of_java_utf
https://blogs.sun.com/CoreJavaTechTips/entry/closing_a_urlclassloader
Closing a URLClassLoader
Christine Dorffi
2009-03-11T16:05:17-07:00
2009-03-12T10:12:40-07:00
Sun engineer Michael McMahon describes the new close() method in URLClassLoader, It 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.
By <a href="#bio">Michael McMahon</a>
<p>Complex Java programs, such as application servers, sometimes create their own class loaders using the <a href="https://download.java.net/jdk7/docs/api/index.html?java/net/URLClassLoader.html">URLClassLoader</a> type. With URLClassLoader, applications can load classes and resources from a search path of URLs. The following URL types are supported:
<ul>
<li>file: (loads from file-system directories)</li>
<li>jar: (loads from JAR files)</li>
<li>http: (loads from http servers)</li></ul></p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>To alleviate this problem, URLClassLoader has acquired a new method called <a href="https://download.java.net/jdk7/docs/api/java/net/URLClassLoader.html#close()">close()</a>. It has been implemented since <a href="https://download.java.net/jdk7/">Build 48 of JDK7</a>.</p>
<p>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.</p>
<p>The new method follows the familiar "Closeable" pattern, and URLClassLoader now implements the <a href="https://download.java.net/jdk7/docs/api/index.html?java/io/Closeable.html">Closeable interface</a>, which defines URLClassLoader.close(). The following sample code shows how one might use the method.</p>
<p>
<pre>
//
// 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();
</pre>
</p>
<p><a name="bio"></a><i>Michael McMahon is an engineer at Sun Microsystems. He works in the Java Security, Networking, and Libraries group.</i></p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/5-zLqMTRYXc" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/closing_a_urlclassloader
https://blogs.sun.com/CoreJavaTechTips/entry/making_progress_with_swing_s
Making Progress With Swing's Progress Monitoring API
dananourie
2008-12-15T09:35:35-08:00
2009-03-12T08:24:15-07:00
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.
<p>by Jennie Hall<br>Updated Jan. 23, 2009</p>
<p>In this tip, you'll learn how to use Swing's progress indicator support to monitor and report on the progress of long-running operations. It is a good practice to keep users informed as they interact with an application; one way to do this is with a progress bar. A progress bar is an animated image that indicates the degree of completion of a given task. The animation typically looks like a rectangular bar that fills in as the task becomes more complete.</p>
<p>Swing's Progress Monitoring API consists of three classes that enable the use of progress bars. <code>JProgressBar</code> subclasses <code>JComponent</code> and is a graphical component that illustrates the progress of an operation. It can be embedded within other graphical components. <code>ProgressMonitor</code> subclasses <code>Object</code> and is not itself a graphical component. It monitors a task and pops a dialog box with a progress bar in it. <code>ProgressMonitorInputStream</code> 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.</p>
<p>The <a href="https://java.sun.com/docs/books/tutorial/uiswing/components/progress.html#deciding">Java Tutorial</a> provides some good rules of thumb that help to determine the appropriate class to use in a given situation. For example, use <code>JProgressBar</code> 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, <code>ProgressMonitor</code> provides for this. <code>ProgressMonitor</code> 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 <code>ProgressMonitor</code>. </p>
<p><strong>The Sample Application</strong></p>
<p> The <a href="/CoreJavaTechTips/resource/progressMonitorExamplev2.zip">sample application</a> 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.</p>
<p><img width="702" height="352" src="/CoreJavaTechTips/resource/screen0_v2.gif" /></p>
<p>Figure 1: Sample Application</p>
<p>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.</p>
<p><img width="699" height="346" src="/CoreJavaTechTips/resource/screen1_v2.gif" /></p>
<p>Figure 2: Dialog containing progress bar</p>
<p>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:</p>
<p><img width="699" height="348" src="/CoreJavaTechTips/resource/screen2_v2.gif" /></p>
<p><strong>Stepping Through the Sample Application</strong></p>
<p> The sample application consists of a class, <code>ProgressMonitorExample</code>, that extends <code>javax.swing.JPanel</code> and implements <code>java.awt.event.ActionListener</code> and <code>java.beans.PropertyChangeListener</code>. <code>ProgressMonitorExample</code>'s <code>main()</code> method tells the event dispatch thread to schedule the execution of a <code>Runnable</code> that creates the application GUI:</p>
<pre> public static void main(String[] args) {
// tell the event dispatch thread to schedule the execution
// of this Runnable (which will create the example app GUI) for a later time
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// create example app window
JFrame frame = new JFrame("Progress Monitor Example");
// application will exit on close
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// create example app content pane
// ProgressMonitorExample constructor does additional GUI setup
JComponent contentPane = new ProgressMonitorExample();
contentPane.setOpaque(true);
frame.setContentPane(contentPane);
...
</pre>
<p><code>ProgressMonitorExample</code> contains an inner class, <code>CopyFiles</code>, that extends <code>javax.swing.SwingWorker</code>. When the user clicks the Copy Files button, <code>ProgressMonitorExample</code>'s <code>actionPerformed()</code> method receives the event, creates a new <code>ProgressMonitor</code>, and starts the file-copying operation on a background thread. Here's the code that creates the <code>ProgressMonitor</code>:</p>
<pre> 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);
...
</pre>
<p><code>ProgressMonitor</code> has a single constructor. The first argument is the parent component to the progress monitor's dialog box. The second argument, of type <code>Object</code>, 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. </p>
<p>The code below, also excerpted from <code>actionPerformed()</code>, creates a new instance of <code>CopyFiles</code>, adds <code>ProgressMonitorExample</code> as a property change listener on the instance, and executes the instance:</p>
<pre> // 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);</pre>
<p><code>CopyFiles</code> subclasses <code>SwingWorker</code>, so the call to inherited method <code>execute()</code> schedules <code>CopyFiles</code> 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. </p>
<p>Although the file-copying operation has begun, the progress monitor dialog box doesn't pop up right away. By default, <code>ProgressMonitor</code> 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 <code>ProgressMonitor</code> determines that the monitored operation has already completed or is likely to complete before the dialog box can be displayed, <code>ProgressMonitor</code> does not pop the dialog box. <code>ProgressMonitor</code>'s method <code>setMillisToDecideToPopup()</code> controls this setting. <code>setMillisToPopup()</code> sets the estimated amount of time it will take the dialog box to appear; the default value for this property is 2 seconds. </p>
<p>The real work of copying the files occurs in <code>doInBackground()</code>, an abstract method on <code>SwingWorker</code> that <code>CopyFiles</code> overrides. Here's a partial listing:</p>
<pre> // perform time-consuming copy task in the worker thread
@Override
public Void doInBackground() {
int progress = 0;
// initialize bound property progress (inherited from SwingWorker)
setProgress(0);
// get the files to be copied from the source directory
File[] files = srcDir.listFiles();
// determine the scope of the task
long totalBytes = calcTotalBytes(files);
long bytesCopied = 0;
while (progress < 100 && !isCancelled()) {
// copy the files to the destination directory
for (File f : files) {
File destFile = new File(destDir, f.getName());
long previousLen = 0;
try {
InputStream in = new FileInputStream(f);
OutputStream out = new FileOutputStream(destFile);
byte[] buf = new byte[1024];
int counter = 0;
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
counter += len;
bytesCopied += (destFile.length() - previousLen);
previousLen = destFile.length();
if (counter > PROGRESS_CHECKPOINT || bytesCopied == totalBytes) {
// get % complete for the task
progress = (int)((100 * bytesCopied) / totalBytes);
counter = 0;
CopyData current = new CopyData(progress, f.getName(), getTotalKiloBytes(totalBytes), getKiloBytesCopied(bytesCopied));
// set new value on bound property
// progress and fire property change event
setProgress(progress);
// publish current progress data for copy task
publish(current);
}
}
in.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
...
</pre>
<p><code>doInBackground()</code> 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 <code>CopyData</code> 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 <code>publish()</code> makes the copy task's current progress data available for processing in the event dispatch thread.</p>
<p><code>ProgressMonitorExample</code>'s <code>propertyChange()</code> method extracts the progress value from the property change event. It then updates the progress monitor animation by calling its <code>setProgress()</code> and passing the progress value. Here's the code:</p>
<pre> // 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);
}
}
</pre>
<p>Notice that <code>ProgressMonitor</code> 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.</p>
<p>By overriding the <code>SwingWorker</code> method <code>process()</code>, <code>CopyFiles</code> can use the progress data made available by the call to <code>publish()</code> to update the GUI. <code>process()</code> executes in the event dispatch thread, so it is safe to update Swing components in this method. Here's the code:</p>
<pre> // process copy task progress data in the event dispatch thread
@Override
public void process(List<copydata> data) {
if(isCancelled()) { return; }
CopyData update = new CopyData(0, "", 0, 0);
for (CopyData d : data) {
// progress updates may be batched, so get the most recent
if (d.getKiloBytesCopied() > update.getKiloBytesCopied()) {
update = d;
}
}
// update the progress monitor's status note with the
// latest progress data from the copy operation, and
// additionally append the note to the console
String progressNote = update.getKiloBytesCopied() + " of "
+ update.getTotalKiloBytes() + " kb copied.";
String fileNameNote = "Now copying " + update.getFileName();
if (update.getProgress() < 100) {
progressMonitor.setNote(progressNote + " " + fileNameNote);
console.append(progressNote + "\n" + fileNameNote + "\n");
} else {
progressMonitor.setNote(progressNote);
console.append(progressNote + "\n");
}
}
</copydata></pre>
<p>As shown above, <code>process()</code> 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.</p>
<p>When its background operation is finished, <code>CopyFiles</code> sets its own state to done and invokes the <code>done()</code> method in the event dispatch thread. <code>done()</code> invokes the <code>SwingWorker</code> method <code>get()</code>, 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 <code>get()</code> to determine whether or not the background task was canceled before completion and responds appropriately:</p>
<pre> // perform final updates in the event dispatch thread
@Override
public void done() {
try {
// call get() to tell us whether the operation completed or
// was canceled; we don't do anything with this result
Void result = get();
console.append("Copy operation completed.\n");
} catch (InterruptedException e) {
} catch (CancellationException e) {
// get() throws CancellationException if background task was canceled
console.append("Copy operation canceled.\n");
} catch (ExecutionException e) {
console.append("Exception occurred: " + e.getCause());
}
// reset the example app
copyButton.setEnabled(true);
progressMonitor.setProgress(0);
}
</pre>
<p> <br /> <strong>Running the Sample Application</strong></p>
<p>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 <code>progressMonitorExample</code>. Select the Open as Main Project check box. Click Open Project Folder. Right-click the <code>progressMonitorExample</code> project and select Build, then right-click the project again and select Run. </p>
<p><strong>References and Resources</strong></p>
<p><a href="/CoreJavaTechTips/resource/progressMonitorExamplev2.zip">Sample code for this tip</a><br /> <a href="https://java.sun.com/docs/books/tutorial/uiswing/components/progress.html#api">The Java Tutorial</a></p>
<p><strong>About the Author</strong></p>
<p>Jennie Hall is a lead developer working in the financial sector.</p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/G_bwX1nIHmk" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/making_progress_with_swing_s
https://blogs.sun.com/CoreJavaTechTips/entry/exchanging_data_with_xml_and1
Exchanging Data With XML and JAXB, Part 2
dananourie
2008-09-15T09:57:04-07:00
2009-03-12T08:24:32-07:00
In this tip, you'll learn how JAXB's binding customization features can facilitate XML processing for the recipient of data as well as for the sender.
<p>by Jennie Hall</p>
<p>In <a href="/CoreJavaTechTips/entry/exchanging_data_with_xml_and">Exchanging Data With XML and JAXB, Part 1</a>, we saw
how the Java Architecture for XML Binding (<a href="https://jaxb.dev.java.net/" target="_blank">JAXB</a>) expedites
the exchange of data between systems. With JAXB 2.0's annotation
support, generating XML data for a business partner is as easy as
annotating your existing object model. </p>
<p>In this tip, you'll learn how JAXB's binding customization
features can facilitate XML processing for the recipient of data as
well as for the sender. Binding customization offers a level of
control over the characteristics of the Java classes generated by
the JAXB schema compiler. This allows you to work with an object
model that makes sense for your business domain even when processing
XML instance documents based on a schema that is not of your own
design.</p>
<p>For a brief overview of the Java Architecture for XML Binding
(JAXB), see the "What's JAXB?" section of <a href="/CoreJavaTechTips/entry/exchanging_data_with_xml_and">Exchanging Data With XML and JAXB, Part
1</a>.</p>
<h2>The Sample Application</h2>
<p>Let's continue with our scenario from Part 1.A veterinary office, NiceVet, wants to send upcoming
appointment reminders and pet birthday cards to its clients. NiceVet
contracts with a service provider, WePrintStuff, to do the printing
and mailing.<b> </b>In
Part 1, we assembled some data for WePrintStuff by annotating
NiceVet's existing object model and then using JAXB to marshal
application data from Java objects to an XML instance document. We
also used JAXB's schema generator to produce a corresponding schema
from the annotated Java classes.</p>
<p>On the receiving end, WePrintStuff will run the JAXB schema
compiler against the source schema to generate the schema-derived
classes. JAXB will create instances of these classes as it
unmarshals NiceVet's data from the XML instance document and binds
it into a Java content tree. In some cases, however, the default
manner in which JAXB generates the schema-derived classes and binds
the data is not exactly what is needed. JAXB's binding customization
features provide us with some nice ways to handle many of these
cases. We'll take a look at some of them in the next section.</p>
<h2>Customizing the Source Schema</h2>
<p>We can make some customizations right away to make life easier at
WePrintStuff. For example, we would like to work
with meaningful package and class names and avoid any naming
collisions. To accomplish this, we can add custom binding
declarations to the source schema itself -- this is known as inline
customization.
</p>
<p>Here are the first few lines of the annotated schema:</p>
<pre> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="https://www.w3.org/2001/XMLSchema"
<!-- JAXB namespace declaration required for inline customization -->
xmlns:jxb="https://java.sun.com/xml/ns/jaxb"
xmlns:xjc="https://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>
</pre>
<p>To enable inline customization, we must include a JAXB namespace
declaration and the JAXB version number. The JAXB namespace prefix
we'll use in our sample application is <code>jxb:</code>. We can
override the default JAXB bindings at different scopes. Lower-level,
more fine-grained scopes inherit declarations made at higher-level
scopes, but binding declarations made at lower-level scopes can
override these inherited declarations. The scopes in order from
highest to lowest level are global, schema, definition, and component.
</p>
<p>In the preceding example, we've made a declaration at global
scope that specifies that all collections in this schema will use
the implementation class <code>java.util.ArrayList</code>. If our
source schema imported another schema, this declaration would apply
to the second schema as well. There can be only one
<code><globalBindings></code> declaration per schema. This
declaration is valid in the top-level schema only.</p>
<p>At schema scope, we've specified the package into which the JAXB
schema compiler will generate the schema-derived classes. We've also
included some package-level Javadoc that describes our package and
its uses. Note that we've wrapped our custom binding declarations in
<code><annotation><appinfo></code> tags.</p>
<p>Now that we have the package we want, let's see if we can get
some class names that better reflect Java naming conventions and
WePrintStuff's business domain. The default bindings for the
original schema would generate classes with names like
<code>ClassAType</code>, <code>ClassBType</code>, and so
on.This is not how we typically name classes in the
Java programming language, so let's fix it:</p>
<pre> <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>
<b></b>
</pre>
<p><code>PrintOrderType</code> becomes <code>PrintOrder</code>,
<code>NotificationsType</code> becomes <code>PrintItems</code>, and
so on. Dealing with class names is straightforward, but
unfortunately we're stuck with a class structure that is less than
ideal. There are too many wrappers around the data, which results in
a long chain of method calls. Here's what the calls would have looked
like with classes derived from the original schema. As you can see,
methods with singular method names return collections, which is misleading.</p>
<pre> List<AppointmentType> appointments = printOrder.getNotifications().getAppointments().getAppointment();
List<BirthdayType> birthdays = printOrder.getNotifications().getBirthdays().getBirthday();
</pre>
<p>The following improved calls show the new names, which say what they
mean and are more relevant to WePrintStuff's business domain.</p>
<pre> List<Appointment> appointments = printOrder.getPrintItems().getAppointmentHolder().getAppointments();
List<Birthday> birthdays = printOrder.getPrintItem().getBirthdayHolder().getBirthdays();
</pre>
<p>I tried to solve some of the class structure issues by using the
enhanced <code><jxb:javaType></code> customization
(<code><xjc:javaType></code>) and an adapter class derived
from <code>XmlAdapter</code>, but JAXB currently does not support
this customization for XML complex types. This issue is documented
in the <a href="https://jaxb.dev.java.net/issues" target="_blank">JAXB issue tracker</a> as issue number 209.
Meanwhile, we'll look at an example of the enhanced
<code><jxb:javaType></code> customization used with an XML
simple type in the upcoming paragraphs.</p>
<p>Moving down the source schema, we see that each print order
received will have an ID of type <code>long</code>. We know,
however, that WePrintStuff employs a natural key
strategy in its persistent store. For print-order records, the key
consists of a client name and an order ID. WePrintStuff uses the
class <code>PrintOrderKey</code> to encapsulate this key
information. We'll apply a binding customization that causes the
JAXB schema compiler to use an adapter class,
<code>IdAdapter</code>, to replace the original print-order ID with
WePrintStuff's <code>PrintOrderKey</code> class. Take a
look:</p>
<pre> <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>
</pre>
<p>We've changed the name of the print-order property to
<code>orderKey</code> and specified the types of the adapter and the
order key. During the unmarshalling process, JAXB calls
<code>IdAdapter</code>'s <code>unmarshal()</code> method, which
receives the value of <code>id</code> and incorporates it into a new
<code>PrintOrderKey</code> containing the order ID and client name.
Here's the code for <code>IdAdapter</code>:</p>
<pre> 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));
}
}
</pre>
<p>The <code><xjc:javaType></code> customization we've just
discussed is an example of a vendor extension. The JAXB reference
implementation (RI) offers additional customizations that are not part of
the JAXB specification. To use these extensions, we must include a
declaration for the JAXB RI vendor extension namespace and specify a
namespace prefix:</p>
<pre> <xs:schema version="1.0" xmlns:xs="https://www.w3.org/2001/XMLSchema"
xmlns:jxb="https://java.sun.com/xml/ns/jaxb"
<!-- JAXB RI vendor extension namespace declaration is required. -->
xmlns:xjc="https://java.sun.com/xml/ns/jaxb/xjc"
<!-- JAXB RI vendor extension namespace prefix is required. -->
jxb:extensionBindingPrefixes="xjc"
jxb:version="2.0">
</pre>
<p>Finally, vendor extensions require that we run the schema
compiler with the <code>-extension</code> switch. For an example of
the standard <code><jxb:javaType></code> customization, which
is not a vendor extension, check out <code>niceVet.xsd</code> and
<code>weprintstuff.print.CustomDataTypeConverter</code>. These files
are included with the <a href="/CoreJavaTechTips/resource/schema-to-java.zip">sample code</a> that comes with this tech
tip. In our example, we've used the
<code><jxb:javaType></code> customization to convert XML <code>dateTime</code>
types to nicely formatted <code>String</code> dates ready for output.</p>
<p>Things are looking better, but we'd also like to handle the
printing of appointment reminders and birthday cards as
polymorphically as we can.<b> </b>Take a look at the following:</p>
<pre> <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>
</pre>
<p>Fortunately for us, we can modify the structure of the source
schema somewhat while retaining the schema's ability to validate the
same set of XML instance documents. In this case, we have added a
totally new complex type, <code>printItemType</code>, to the schema.
Then we modified the complex type <code>AppointmentType</code> with
the XML Schema extension element, causing
<code>AppointmentType</code> to derive from
<code>printItemType</code>.
</p>
<p>At this point, we applied a JAXB customization to
<code>printItemType</code> that directs the schema compiler to use
the specified class that we've written rather than generating one.
Because we've implemented <code>PrintItem</code> ourselves, we can
include behavior: specifically, a <code>print()</code> method that
allows us to handle subclasses, such as <code>Appointment</code> and
<code>Birthday</code>, in a more polymorphic manner. Here's the code
for <code>PrintItem</code>:</p>
<pre> 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());
}
}
</pre>
<p>The <code>Printer</code> interface has a single method:
</p>
<pre> public void print(PrintItem item);
</pre>
<p>Although we're still hampered by the structure of the
schema-derived classes as discussed earlier, the code to print the
items in the print order is now pretty clean. Here's an excerpt from
our <code>main()</code> method:</p>
<pre> // 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();
}
</pre>
<p>Another nice customization feature that JAXB provides is the
ability to map XML simple types to typesafe enumerations. This is
convenient because WePrintStuff currently has printing templates for
certain types of appointments only. We'll modify the schema to
include the supported appointment types and add a customization that
causes the schema compiler to map the elements of
<code>ApptType</code> to a Java typesafe enum class. XML instance
documents with appointment types that WePrintStuff does not support
will be rejected:</p>
<pre> <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>
</pre>
<p>Here is the resultant typesafe enum class, <code>ApptType</code>:</p>
<pre> package weprintstuff.generated;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlType;
@XmlType(name = "ApptType")
@XmlEnum
public enum ApptType {
@XmlEnumValue("Yearly Checkup")
YEARLY_CHECKUP("Yearly Checkup"),
@XmlEnumValue("Well Mom Exam")
WELL_MOM_EXAM("Well Mom Exam"),
@XmlEnumValue("Teeth Cleaning")
TEETH_CLEANING("Teeth Cleaning"),
@XmlEnumValue("Vaccination")
VACCINATION("Vaccination"),
@XmlEnumValue("Senior Pet Checkup")
SENIOR_PET_CHECKUP("Senior Pet Checkup");
private final String value;
ApptType(String v) {
value = v;
}
public String value() {
return value;
}
public static ApptType fromValue(String v) {
for (ApptType c: ApptType.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
</pre>
<p>So far we've made what are known as inline binding
customizations, but JAXB also accepts customizations in the form of
one or more external binding customization files. Inline and external
customizations can be used in combination, but they cannot be used
together on the same element. External binding files<b> </b>are useful when
you cannot modify a given schema or when you want to reuse
customizations across schemas. For example, WePrintStuff has its own
class for U.S. addresses, <code>DomesticAddress</code>, and WePrintStuff
would like to use this class to represent the concept of an address throughout the system.
</p>
<p>Here's the external binding file, <code>binding.xjb</code>:</p>
<pre> <!-- XML Schema namespace is required, as are the JAXB namespace and version. -->
<jxb:bindings version="2.0"
xmlns:jxb="https://java.sun.com/xml/ns/jaxb"
xmlns:xs="https://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>
</pre>
<p>The binding customization file<b> </b>is
just an ASCII text file. A valid binding file must specify the
schema name and node. We identify nodes using XPath expressions.
In the previous listing, we've applied a customization that directs
the schema compiler to use WePrintStuff's
<code>DomesticAddress</code> class rather than generating a class
for the complex type <code>AddressType</code>. This is the same
customization illustrated earlier in our <code>PrintItem</code> base
class example, although in that situation, we declared the
customization inline. The syntax for supplying a binding
customization file<b> </b>to the schema compiler is the following:</p>
<pre> xjc -b bindings schema
</pre>
<p>We can apply multiple binding files to one schema, one binding
file to multiple schemas, or multiple binding files to multiple
schemas. Each binding file must have its own <code>-b</code> switch.</p>
<h2>Generating the Schema-Derived Classes</h2>
<p>We generate our schema-derived classes at the command line with
the following command to the schema compiler, <code>xjc</code>:</p>
<pre> xjc -d ./src -b binding.xjb -extension niceVet.xsd
</pre>
<p>The schema compiler informs us that it has generated our classes
with the following output:</p>
<pre> parsing a schema...
compiling a schema...
weprintstuff\generated\Adapter1.java
weprintstuff\generated\Adapter2.java
weprintstuff\generated\Appointment.java
weprintstuff\generated\ApptType.java
weprintstuff\generated\Birthday.java
weprintstuff\generated\ObjectFactory.java
weprintstuff\generated\Owner.java
weprintstuff\generated\Pet.java
weprintstuff\generated\PrintItems.java
weprintstuff\generated\PrintOrder.java
weprintstuff\generated\package.html
</pre>
<p>The JAXB schema compiler has a number of options. For example, it
will mark generated classes as <code>read only</code> in response to
the <code>-readOnly</code> switch. Invoke <code>xjc</code> with no
options or with the <code>-help</code> switch for more
information.</p>
<h2>The Payoff</h2>
<p>We've modified our source schema, made our customizations, and
generated our schema-derived classes. All that's left to do is run
our printing program, let JAXB bind the XML data supplied by
NiceVet, and print out the appointment reminders and birthday cards.
We'll take a look at our XML instance document:</p>
<pre><?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>
</pre>
<p>And here's another look at the <code>main()</code> method:</p>
<pre> // 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.<b></b>
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();
}
</pre>
<p>Check out our results: No one will ever miss an appointment or a
birthday again.</p>
<pre> Hi Joe!
Our records show that your dog Honcho has a Yearly Checkup appointment scheduled on 09/15/2008.
Please call us 24 hours prior to your appointment if you need to reschedule.
Sincerely, NiceVet
Hi Missy!<b></b> Our records show that your cat Miss Kitty has a Well Mom Exam appointment scheduled on 09/12/2008.
Please call us 24 hours prior to your appointment if you need to reschedule.
Sincerely, NiceVet
Hi Violet!
Our records show that your cat Tom will turn 7 years old on 09/07.
Happy Birthday, Tom, from all of us at NiceVet!
</pre>
<h2>Running the Sample Application</h2>
<p>To run the sample application, download the <a href="/CoreJavaTechTips/resource/schema-to-java.zip">sample code</a> and unzip
it. Navigate
to the directory
<code><sample-install-dir>/schema-to-java</code> and generate
the schema-derived classes as described in the section "Generating
the Schema-Derived Classes."
</p>
<p>Launch the <a href="https://www.netbeans.org/" target="_blank">NetBeans IDE</a> 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 <code>schema-to-java</code>.
Select the Open as Main Project check box. Click Open Project
Folder. Right-click the <code>schema-to-java</code> project and select Run
Project.</p>
<h2>Conclusion</h2>
<p>In Exchanging Data With XML and JAXB, Parts <a href="/CoreJavaTechTips/entry/exchanging_data_with_xml_and">1</a> and 2, we've learned how JAXB can facilitate the flow of data between
business partners. With features that expedite XML processing on
both ends of the exchange, JAXB can enable simpler, more productive
integration between systems.</p>
<h2>References and Resources</h2>
<p><a href="/CoreJavaTechTips/resource/schema-to-java.zip">Sample code for this tip</a><br /> <a href="https://java.sun.com/javaee/5/docs/tutorial/doc/bnazf.html">Java EE 5 Tutorial</a> <br /> <a href="https://weblogs.java.net/blog/kohsuke/" target="_blank">Kohsuke Kawaguchi's Blog</a> - Kohsuke Kawaguchi is a staff engineer at Sun Microsystems, where he works on JAXB, among other projects.<br /> <a href="https://jcp.org/en/jsr/detail?id=222" target="_blank">JSR 222: JAXB Specification</a></p>
<h3>About the Author</h3>
<p><b>Jennie Hall</b> is a lead developer working in the financial sector.</p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/NBDJ93ZPxQs" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/exchanging_data_with_xml_and1
https://blogs.sun.com/CoreJavaTechTips/entry/exchanging_data_with_xml_and
Exchanging Data with XML and JAXB, Part 1
dananourie
2008-08-12T16:35:28-07:00
2008-09-11T13:13:26-07:00
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.
by Jennie Hall
<p>In this tip, you'll learn how to to use the <a href="https://jaxb.dev.java.net/">Java Architecture for XML Binding (JAXB)</a> in <a href="https://java.sun.com/javase/downloads/?intcmp=1281">Java SE 6</a> 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.</p>
<h2>What's JAXB?</h2>
<p> 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.</p>
<p>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.</p>
<h2>The Sample Application</h2>
<p>
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.
</p>
<p>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:</p>
<p><img src="/CoreJavaTechTips/resource/NiceVetObjectModel.gif" /><br /></p>
<p>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.</p>
<h2>Generating the XML</h2>
<p> 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 <code>PrintOrder</code> 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 <code>@XmlRootElement </code>annotation to the <code>PrintOrder</code> class. This will establish <code>printOrder</code> as the root element of our generated XML.</p>
<pre> 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;</pre>
<p>Let's add some more annotations:</p>
<pre> 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;</pre>
<p><code>@XmlType</code> maps the <code>PrintOrder</code> class to a generated schema type named <code>PrintOrderType</code>. The <code>@XmlAccessorType(XmlAccessType.FIELD) </code>annotation tells JAXB to bind all non-static, non-transient fields to XML unless annotated with <code>@XmlTransient</code>, which prevents the mapping of a field or property. With the <code>XmlAccessType.FIELD </code>setting, getter/setter pairs are not bound to XML unless explicitly annotated. The default setting is <code>XmlAccessType.PUBLIC_MEMBER</code> (all public fields and getter/setter pairs are bound unless otherwise annotated); other settings are<code> XmlAccessType.PROPERTY</code> and <code>XmlAccessType.NONE</code>.</p>
<p>Moving down the code listing, we see that the field id maps to an XML attribute on the <code>PrintOrderType </code>element. Some kind of identifier on the print order is required, and we've annotated the id field accordingly.</p>
<p>The <code>PrintOrder</code> class has a list of events which contains both appointments and birthdays. The annotation <code>@XmlJavaTypeAdapter</code> tells JAXB that we are going to use a custom adapter, <code>EventAdapter</code>, to help us map this Java construct to XML. To support this annotation, we need to write an adapter class which extends <code>XmlAdapter</code> and overrides its abstract marshal and unmarshal methods.</p>
<pre> public class EventAdapter extends XmlAdapter<strong><NotificationsType, List<Event>></strong> {
// 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 { ... }
</pre>
<p>EventAdapter's marshal method takes the original Java construct, <code>List<Event></code> events, and converts it to a type which maps more easily to the XML output we want. Let's take a look at <code>NotificationsType</code>: </p>
<pre> 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;
}
</pre>
<p>The <code>@XmlElementWrapper</code> annotation creates a wrapper XML element around each collection, and the <code>@XmlElement </code>annotation maps each member of the collection to an XML element of the appropriate type. Combined with the <code>@XmlElement(name = "notifications")</code> annotation on <code>PrintOrder.events</code>, what we end up with will look something like this: </p>
<pre> <printOrder>
<notifications>
<appointments>
<appointment>
...
</appointment>
</appointments>
<birthdays>
<birthday>
...
</birthday>
</birthdays>
</notifications>
</printOrder>
</pre>
<p>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 <code>@XmlTransient </code>annotation on <code>Event</code>. We still need fields owner and pet, however, so we annotate them with <code>@XmlElement(required = true)</code>.</p>
<pre> import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
// no need to capture this inheritance hierarchy in XML
@XmlTransient
public abstract class Event {
@XmlElement(required = true)
private Owner owner;
@XmlElement(required = true)
private Pet pet;
public Event() {}
public Event(Owner owner, Pet pet) {
this.owner = owner;
this.pet = pet;
}
</pre>
<p>
When objects of type Birthday and Appointment are marshalled to XML, they will include all the relevant data from their supertype, <code>Event</code>. Take a look at the listing below for the <code>Birthday</code> class:</p>
<pre> @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();
}
</pre>
<p><code>@XmlType.propOrder</code> 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 <code>@XmlAccessorOrder</code> annotation. As mentioned in the Java Tutorial, imposing an order improves application portability across JAXB implementations.<br /> <br />
Although the field <code>dateOfBirth</code> is actually on the <code>Pet</code> 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 <code>dateOfBirth</code> field on the <code>Birthday</code> class by annotating the <code>getBirthday()</code> method, which delegates to <code>Pet</code>.</p>
<p>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 <code>PrintOrder</code> 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 <code>java.util.Date</code> instances and output nicely formatted <code>Strings</code>. The package-level annotation goes in the <code>package-info.java</code> file (located in the vet package):</p>
<pre> // 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;
</pre>
<p>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:</p>
<pre> import java.util.Date;
import java.text.SimpleDateFormat;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class DateAdapter extends XmlAdapter<strong><String, Date></strong> {
// 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);
}
</pre>
<p>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.<br /> <br />
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.</p>
<pre> <?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>
</pre>
<p>The generated schema for the XML instance document above is named <code>schema1.xsd</code>, and it lives in <code>/generated/schema</code> under the root directory of the java-to-schema project. It looks like this:</p>
<pre> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="https://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>
</pre>
<p>
If we take a quick peek at the <code>main() </code>method, we can see that we are generating the schema as follows:</p>
<pre> // specify where the generated XML schema will be created
final File dir = new File("generated" + File.separator + <strong>"schema");</strong>
// specify a name for the generated XML instance document
OutputStream os = new FileOutputStream("niceVet.xml");
// create a JAXBContext for the PrintOrder class
JAXBContext ctx = JAXBContext.newInstance(PrintOrder.class);
// generate an XML schema from the annotated object model; create it
// in the dir specified earlier under the default name, schema1.xsd
ctx.generateSchema(new SchemaOutputResolver() {
@Override
public Result createOutput(String namespaceUri, String schemaName) throws IOException {
return new StreamResult(new File(dir, schemaName));
}
});
</pre>
<p>
To get the XML instance document, we first create some data, then we obtain a Marshaller from the <code>JAXBContext</code> and marshal the Java content tree to XML:
</p>
<pre> // create some data
PrintOrder order = new PrintOrder(EventUtil.getEvents());
...
// create a marshaller
Marshaller marshaller = ctx.createMarshaller();
// the property JAXB_FORMATTED_OUTPUT specifies whether or not the
// marshalled XML data is formatted with linefeeds and indentation
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// marshal the data in the Java content tree
// to the XML instance document niceVet.xml
marshaller.marshal(order, os);
</pre>
<h2>Running the Sample Application</h2>
<p><br />
To run the sample application, <a href="/CoreJavaTechTips/resource/java-to-schema.zip">download</a> 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.
</p>
<h2>Conclusion</h2>
<p> 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.</p>
<h2>References and Resources</h2>
<li><a href="/CoreJavaTechTips/resource/java-to-schema.zip">Sample code</a> for this tip</li>
<li><a href="https://java.sun.com/javaee/5/docs/tutorial/doc/bnazf.html">The Java Tutorial</a>.</li>
<li><a href="https://weblogs.java.net/blog/kohsuke/">Kohsuke Kawaguchi's Blog</a>. Kohsuke Kawaguchi is a staff engineer at Sun Microsystems, where among other projects he works on JAXB.</li>
<p>Jennie Hall is a lead developer working in the financial sector.<br /> </p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/icD02axVT9w" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/exchanging_data_with_xml_and
https://blogs.sun.com/CoreJavaTechTips/entry/where_s_the_state
Where's the State?
dananourie
2008-08-08T09:52:57-07:00
2009-03-12T08:25:03-07:00
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? Read about these questions in this blog.
<p> from <a href="https://weblogs.java.net/blog/timboudreau/">Tim Boudreau's Blog</a></p>
<p> 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?
</p>
<p>
These questions make a good start for figuring out how to carve
up a problem domain into objects, if you observe the principle
<i>keep all related state in one place.</i> 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.
</p>
<p>
One big reason why statefulness matters is <i>threading</i>.
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.
</p>
<p>
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
</p>
<pre>assert !EventQueue.isDispatchThread();</pre>
under all circumstances.
<p>
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.
</p> <img height="266" align="right" width="400" src="https://weblogs.java.net/blog/timboudreau/archive/IMG_2949.png" alt="IMG_2949.png" /><br />
<p>
Any time you find yourself writing a setter (or more generally,
mutator) method, it is worth asking yourself
<i>Who does this piece of state really belong to? Am I putting
state in the right place?</i> Blindly following the beans pattern
(add a getter/setter to whatever type you happen to be editing)
can result in state being distributed all over objects in an
application. The result will be harder to maintain, and if it
has any lifespan, less and less maintainable
over time. Like entropy, or the
<a href="https://en.wikipedia.org/wiki/Broken_windows_theory">no
broken windows theory</a>, 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
<a href="https://en.wikipedia.org/wiki/Working_memory">hold so many
things in their minds</a> at a time. The more state is diffused,
the more things someone must pay attention to simultaneously to
solve a problem in that code.
</p>
<p><a href="https://weblogs.java.net/blog/timboudreau/archive/2008/08/wheres_the_stat.html">Read the rest of this blog . . . </a></p>
<p><br /></p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/4-sQuGx8NI0" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/where_s_the_state
https://blogs.sun.com/CoreJavaTechTips/entry/distributing_a_java_web_start
Distributing a Java Web Start Application via CD-ROM
dananourie
2008-07-21T09:49:36-07:00
2009-03-12T08:25:23-07:00
Learn how to distribute a Java Web Start Application via a CD-ROM.
<p>by <a href="https://today.java.net/pub/au/413">Luan O'Carroll</a></p>
<p> </p>
<p>Isn't <a href="https://java.sun.com/products/javawebstart/">Java
Web Start</a> (JWS) supposed to allow web-based distribution of
applications? So why would one want to distribute a Java Web Start
(JWS) application via CD-ROM? Well, for a number of reasons. For
larger applications, a complete installation can still be a major
download even with high-speed broadband. Secondly, not all desktops
are online, and not all can access the internet (for corporate
security reasons, for example). And, lastly, some people just like
CDs.</p>
<p>A client company required that their application be distributed
worldwide, including to places where broadband coverage is sparse.
The application contains information about a large number of
products, including detailed drawings and diagrams. All this
information constituted a major part of the application, and a
complete installation including the JVM amounts to over 40 MB. In
addition to this, the company wanted to be able to distribute the
application on CDs at trade fairs and with promotional materials;
therefore, a CD-based distribution was required. Normally a CD
install is possible using either commercial or open source
installers, of which there are many. However, when an application
is to run with Java Web Start, it needs to be installed in a
specific location and not at the user's discretion, as is the norm
for installers.</p>
<p>This article describes the steps involved in installing an
application that installs both from CD and the internet. The
installation process requires that:</p>
<ol>
<li>The installed application must check for updates and integrate
with the JWS cache.</li>
<li>The installation should work on a machine without an existing
or up-to-date version of Java.</li>
<li>The installed application should not require an internet
connection.</li>
<li>A installation must be easy to use and must provide a simple
user interface.</li>
</ol>
<p>Application installation is normally carried out with a generic
installer application, but a traditional install process would
effectively create a separate application that is unaware of JWS.
Each time an update is released, the user would have to download
and install the new version, whereas a JWS application only
downloads those components that have been updated, making the
process far more efficient and reliable. The article therefore also
describes a JWS application installer.</p>
<h3 id="jws-primer">JWS Primer</h3>
<p>Java Web Start allows Java applications to be launched via a
link to a <a href="https://java.sun.com/products/javawebstart/download-spec.html">JNLP</a>
file. The JNLP file describes the main method or entry point into
the application and it references the resources used by the
application.</p>
<p>When a JWS application is launched, the JVM tries to access the
required resources, updating them if necessary, and copies the file
to its cache. On subsequent attempts to launch the application, JWS
can check this cache and skip download of the resources. If the
client machine is offline or if the server cannot be contacted,
then JWS can run the application in an offline mode.</p>
<p>If the JWS launch file (the JNLP) were placed on CD, JWS would
attempt to contact the server and download any new files. Obviously
this would defeat the purpose of distributing the files via CD if
the client machine is online. Instead, we need some way to update
the JWS cache as though the application had been previously loaded
by JWS.</p>
<h3 id="updating-the-jws-cache">Updating the JWS Cache</h3>
<p>The Java 5 version of JWS includes a little known
<code>-import</code> option that imports a JWS application into the
cache from a specified location.</p>
<p>The CD image at this location is just a copy of what you would
normally place on the web server: the JNLP file, plus the .jars and
resources referred to by that JNLP file. If you use a servlet to
serve up the JNLP, then the CD image will need a self-contained
snapshot of the generated JNLP file.</p>
<p>The CD image can thus be installed into the JWS cache by
calling:</p>
<p><code><JAVA_HOME>/jre/bin/javaws -codebase
<CACHE_IMAGE> -import
<CACHE_IMAGE>/<XXXX>.jnlp</code></p>
<p>where <code><JAVA_HOME></code> is the root of the
(possible new) JVM, <code><CACHE_IMAGE></code> is location of
the JWS application on the CD, and <code><XXXX></code> is
the name of the application JNLP file. Later, we will see how this
command is automated and wrapped in a simple GUI.</p>
<p>In installing the cached application, JWS conveniently prompts
the user to install desktop and menu shortcuts for starting the
application. Once the JWS install has been completed, we can again
call JWS to start the newly installed application.</p>
<p><code><JAVA_HOME>/jre/bin/javaws -import
<CACHE_IMAGE>/<XXXX>.jnlp</code></p>
<p>Again the CD is used, but this time JWS will use the
installation referred to by the JNLP file. If the machine is
connected to the internet, it will check for updates in the normal
way, as part of the process, and then start the application. If
there is no network connection, the application will launch as
delivered on CD.</p>
<p>The next time the user starts the application they can use the
menu or desktop shortcuts and the CD will no longer be needed.
Alternatively, the user can start the application from a link on a
web page that points to the same URL/JNLP file combination; i.e., the
original version from the website.</p>
<h3 id="jvm-complications">JVM Complications</h3>
<p>One gotcha in all of this is that the above commands require the
presence of a JVM, and in some rare cases this may not be installed
or may not be available by default on the system path and therefore
some extra measures are needed to locate a usable JVM. Furthermore,
when a user inserts the CD, the installation should begin, and the
installation should check for the presence of an existing JVM. The
process of checking for a JVM is then as follows:</p>
<ol>
<li>Check for a JVM (for the installer).</li>
<li>Install the JVM if not present.</li>
<li>Launch the installer, showing the usual license
information.</li>
<li>Install the target JVM (if required by the application and
different from 1 above).</li>
<li>Import the JWS cache.</li>
<li>Start the JWS application.</li>
</ol>
<p>Some further complications arise from the fact that the minimum
JVM for the JWS <code>-import</code> option is Java 5, so even if a
JVM is present and usable for the application, this import option
will still require a fairly recent JVM. Secondly, the import process
takes some time and must complete before the application is
launched, and this sort of delayed execution is difficult to
achieve with many of the normal installers.</p>
<p>Given these complications, it was necessary to build a custom
launch application that could perform these steps.</p>
<p><a href="https://today.java.net/pub/a/today/2008/07/10/distributing-web-start-via-cd-rom.html">Read the rest of the article. </a><br /></p>
<p> </p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/lXbbLLWQeYY" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/distributing_a_java_web_start
https://blogs.sun.com/CoreJavaTechTips/entry/launch_java_applications_from_assembly
Launch Java Applications from Assembly Language Programs
dananourie
2008-07-02T15:21:27-07:00
2008-07-02T15:21:27-07:00
Here I deal with the technique for
invoking Java programs from an ASM process through a demo
application that calls a Java method from assembly language code.
<p>by <a href="https://today.java.net/pub/au/491">Biswajit Sarkar</a></p>
<p><a href="https://java.sun.com/j2se/1.4.2/docs/guide/jni">Java
Native Interface</a> (JNI) is a mechanism that can be used to
establish communication between native language programs and the
Java virtual machine. The documentation for JNI and the technical
literature on JNI deal extensively with interactions between the
JVM and C/C++ code. The Java SDK even provides a utility to
generate a header file to facilitate calling C/C++ programs from
Java code. However, there is hardly any mention of Java and
<i>assembly language</i> code working together. In an <a href="https://today.java.net/pub/a/today/2006/10/19/invoking-assembly-language-from-java.html">
earlier article</a> I showed how assembly language programs can be
called from Java applications. Here I deal with the technique for
invoking Java programs from an ASM process through a demo
application that calls a Java method from assembly language code.
The Java method brings up a Swing <code>JDialog</code> to show that
it has, indeed, been launched.</p>
<p> </p>
<h3 id="why-java-with-asm">Why Java with ASM?</h3>
<p>JNI is essential to the implementation of Java, since the JVM
needs to interact with the native platform to implement some of its
functionality. Apart from that, however, use of Java classes can
often be an attractive supplement to applications written in other
languages, as Java offers a wide selection of APIs that makes
implementation of advanced functions very simple.</p>
<p>Some time ago, I was associated with an application to collect
real-time data from a number of sources and save them in circular
buffers so that new data would overwrite old data once the buffer
got filled up. If a designated trigger event was sensed through a
digital input, a fixed number of data samples would be saved in the
buffers so that a snapshot of pre- and post-trigger data would be
available. The original application was written in assembly
language. After the application was used for a few months, it was
felt that it would be very useful to have the application mail the
snapshots to authorized supervisors whenever the trigger event
occurred. Of course, it would have been possible to write this
extension in assembly, but the team felt that in that particular
instance it was easier to write that extension in Java and hook it
up with the ASM program. As I had earlier worked with ASM-oriented
JNI, I knew this could be done and, indeed, the project was
implemented quickly and successfully.</p>
<p>I am sure there are many legacy applications written in assembly
language that could benefit from such add-ons. However, it is not
only for old applications in need of renovation that JNI can prove
useful. Although it may seem unlikely to some of us, assembly
language is still used for writing selected portions of new
programs. In an <a href="https://developers.sun.com/solaris/articles/x86_assembly_lang.html">
article</a> published not very long ago, the author says, "I have
found that many of Sun's partners still use assembly language in
their products to ensure that hot code paths are as efficient as
possible. While compilers are able to generate much more efficient
code today, the resulting code still doesn't always compete with
hand-coded assembly written by an engineer that knows how to
squeeze performance out of each microprocessor instruction.
Assembly language remains a powerful tool for optimization,
granting the programmer greater control, and with judicious use can
enhance performance." Clearly, in such "mixed language"
applications the ability to use Java with ASM can be useful.</p>
<p>Note that the technique shown here can also be used to call Java
code from languages other than ASM. If <code>JInvoke</code> is
rewritten as a .dll, code written in FORTRAN, for instance,
can link to it and call a Java method.</p>
<p>I have used JNI with legacy ASM code in two ways:<br /></p>
<ul>
<li>Functional enhancement: Mail-enabling an existing ASM
application, as mentioned earlier.</li>
<li>Interface enhancement: Adding interactive user interface
(mostly AWT, but some Swing as well).</li>
</ul>
<p>These enhanced applications have run on Windows 2000 and XP. The
Java versions used were 1.3, 1.4, and 1.6. In all cases the
applications worked smoothly.</p>
<p><a href="https://today.java.net/pub/a/today/2007/10/04/launch-java-applications-from-assembly-language-programs.html">Read the rest of this article . . . </a><br /></p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/x91KqYxTWyQ" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/launch_java_applications_from_assembly
https://blogs.sun.com/CoreJavaTechTips/entry/add_logging_at_class_load
Add Logging at Class Load Time with Java Instrumentation
dananourie
2008-06-16T08:17:05-07:00
2008-06-16T08:17:05-07:00
This article shows how to implement a Java agent, which
transparently adds entry and exit logging to all methods in all
your classes with the standard Java Logging API. Additionally, the article demonstrates how to make the
original Hello World program behave like the logging Hello World by
manipulating the byte code when it is loaded.
<p>by <a href="https://today.java.net/pub/au/661">Thorbjørn Ravn Andersen</a></p>
<p>When you're trying to analyze why a program failed, a very valuable
piece of information is what the program was actually doing when it
failed. In many cases, this can be determined with a stack trace, but
frequently that information is not available, or perhaps what you
need is information about the data that was being processed at the
time of failure.</p>
<p>Traditionally this means using a logging framework like <a href="https://logging.apache.org/log4j/">log4j</a> or the <a href="https://java.sun.com/javase/6/docs/api/java/util/logging/package-summary.html">
Java Logging API</a>, and then writing and maintaining all
necessary log statements manually. This is very tedious and error-prone, and well-suited for automation. Java 5 added the <a href="https://java.sun.com/javase/6/docs/api/java/lang/instrument/Instrumentation.html">
Java Instrumentation</a> mechanism, which allows you to provide
"Java agents" that can inspect and modify the byte code of the
classes as they are loaded.</p>
<p>This article will show how to implement such a Java agent, which
transparently will add entry and exit logging to all methods in all
your classes with the standard Java Logging API. The example used
is Hello World:</p>
<pre><code>public class HelloWorld {
public static void main(String args[]) {
System.out.println("Hello World");
}
}
</code>
</pre>
<p>And here is the same program with entry and exit log statements
added:</p>
<pre><code>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()");
}
}
}
</code>
</pre>
<p>The default logger format generates output similar to:</p>
<pre><code>2007-12-22 22:08:52 LoggingHelloWorld main
INFO: > main(args=[])
Hello World
2007-12-22 22:08:52 LoggingHelloWorld main
INFO: < main()
</code>
</pre>
<p>Note that each log statement is printed on two lines. First, a
line with a time stamp, the provided log name, and the method in
which the call was made, and then a line with the provided log
text.</p>
<p>The rest of the article will demonstrate how to make the
original Hello World program behave like the logging Hello World by
manipulating the byte code when it is loaded. The manipulation
mechanism is the Java Instrumentation API added in Java 5.</p>
<p><a href="https://today.java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html">Read the rest of this article</a>. <br /></p>
<p> </p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/KR8Xr6JVKbk" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/add_logging_at_class_load
https://blogs.sun.com/CoreJavaTechTips/entry/source_code_analysis_using_java
Source Code Analysis Using Java 6 APIs
dananourie
2008-05-20T12:06:52-07:00
2008-05-20T12:06:52-07:00
In this article, you'll 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.
<p>
by <a href="https://today.java.net/pub/au/638">Seema Richard</a>, <a href="https://today.java.net/pub/au/647">Deepa Sobhana</a>
</p><p>
Have you ever thought of how tools like Checkstyle or FindBugs perform a static code analysis, or how Integrated Development Environments (IDEs) like NetBeans or Eclipse execute quick code fixes or find the exact references of a field declared in your code? In many cases, IDEs have their own APIs to parse the source code and generate a standard tree structure, called an Abstract Syntax Tree (AST) or "parse tree," which can be used for deeper analysis of the source elements. The good news is that it is now possible to accomplish the said tasks plus a lot more with the help of three new APIs introduced in Java as part of the Java Standard Edition 6 release. The APIs that might be of interest to developers of Java applications that need to perform source code analysis are the Java Compiler API (JSR 199), the Pluggable Annotation Processing API (JSR 269), and the Compiler Tree API.
</p><p>
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.
</p><p> </p>
<pre>public class TestClass implements Serializable {<br /> int num;<br /><br /> @Override<br /> public boolean equals(Object obj) {<br /> if (this == obj)<br /> return true;<br /> if ((obj == null) || (obj.getClass() != this.getClass()))<br /> return false;<br /> TestClass test = (TestClass) obj;<br /> return num == test.num;<br /> }<br />}<br /><br /></pre><p>Let us go on and analyze this class as part of the build process
with the help of these three APIs.</p>
<h3 id="invoking-the-compiler-from-code-the-java-compiler-api">Invoking the Compiler from Code: The Java Compiler API</h3>
<p>We all use the <code>javac</code> command-line tool for
compiling Java source files to class files. Then why do we need an
API to compile Java files? Well, the answer is quite simple: as the
name describes, this new standard API lets us invoke the compiler
from our own Java applications; i.e., you can programmatically
interact with the compiler and thereby make compilation part of
application-level services. Some typical uses of this API are
listed below.</p>
<ul><li>
<p>The compiler API helps application servers to minimize the time
taken to deploy applications, for example, by avoiding the overhead
of using an external compiler for compiling the servlet sources
generated from the JSP pages.</p>
</li><li>
<p>Developer tools like IDEs and code analyzers can invoke the
compiler from within the editor or build tools that significantly
reduce the compile time.</p>
</li></ul>
<p>The Java compiler classes are packaged under the
<code>javax.tools</code> package. The <code>ToolProvider</code>
class of this package provides a method called
<code>getSystemJavaCompiler()</code> that returns an instance of
some class that implements the <code>JavaCompiler</code> interface.
This compiler instance can be used to create a compilation task
that will perform the actual compilation. The Java source files to
be compiled will be then passed to the compilation task. For this,
the compiler API provides a file manager abstraction called
<code>JavaFileManager</code>, which allows Java files to be
retrieved from various sources, such as the file system, databases, memory, and so on. In this sample, we use <code>StandardFileManager</code>, a
file manager based on <code>java.io.File</code>. The standard file
manager can be acquired by calling the
<code>getStandardFileManager()</code> method of the
<code>JavaCompiler</code> instance. The code snippet for the
above-mentioned steps is shown below:</p>
<pre>//Get an instance of java compiler<br />JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();<br /><br />//Get a new instance of the standard file manager implementation<br />StandardJavaFileManager fileManager = compiler.<br /> getStandardFileManager(null, null, null);<br /> <br />// Get the list of java file objects, in this case we have only <br />// one file, TestClass.java<br />Iterable compilationUnits1 = <br /> fileManager.getJavaFileObjectsFromFiles("TestClass.java");<br /><br /><br /><br /></pre>
<p>A diagnostic listener can be optionally passed to the
<code>getStandardFileManager()</code> method to produce diagnostic
reports of any non-fatal problems. In this code snippet, we pass
<code>null</code> values, since we are not collecting the
diagnostics from the tool. For details of the other parameters
passed to these methods, please refer to the <a href="https://java.sun.com/javase/6/docs/api/javax/tools/JavaCompiler.html">
Java 6 API.</a> The <code>getJavaFileObjectsfromFiles()</code>
method of the <code>StandardJavaFileManager</code> returns all the
<code>JavaFileObject</code> instances that correspond to the
supplied Java source files.</p><p><a href="https://today.java.net/pub/a/today/2008/04/10/source-code-analysis-using-java-6-compiler-apis.html">Read the rest of this article</a><br /></p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/2Ac3ZW4JCX8" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/source_code_analysis_using_java
https://blogs.sun.com/CoreJavaTechTips/entry/swingset3_nimbus_and_java_se
Nimbus Look and Feel in Java SE 6 Update 10 Beta
dananourie
2008-04-24T09:41:41-07:00
2008-05-27T10:21:44-07:00
Discover the changes and improvements included in Nimbus in the Java SE 6 Update 10.
<p><i>By Ethan Nicholas</i> <br /></p>
<!-- BEGIN SEPARATOR -->
<div class="hr"><hr /></div>
<!-- END SEPARATOR -->
<p>When the venerable <a href="https://java.sun.com/docs/books/tutorial/figures/uiswing/lookandfeel/MetalLAF.gif">Metal
look and feel</a> for Swing first debuted, its main aesthetic
competition was the <a href="https://upload.wikimedia.org/wikipedia/en/9/90/Am_windows95_desktop.png">Windows
95 interface</a>. Given the state of graphical user interfaces a decade
ago, Metal was an attractive and elegant alternative to the other common
interfaces of the time.</p>
<p>The updated <a href="https://java.sun.com/docs/books/tutorial/figures/uiswing/lookandfeel/OceanLAF.gif">Ocean</a>
theme in Java SE 5 helped to keep Metal a viable choice up to the
present day, but it's time for Swing's cross-platform look and feel to get an
overhaul.</p>
<p>Enter the <a href="https://nimbus.dev.java.net/">Nimbus Look and Feel</a>. A
brand new, modern look and feel based on <a href="https://javadesktop.org/articles/synth/">Synth</a>, Nimbus provides a
polished look to applications which choose to use it. And because Nimbus is
drawn entirely using Java 2D vector graphics, rather than static bitmaps, it's
tiny (only 56KB!) and can be rendered at arbitrary resolutions.</p>
<!-- BEGIN G20 COMPONENT V.9 -->
<div class="g20 g20r1 g20v9">
<div class="g20w1" style="width: 665px;">
<div class="g20w2">
<div style="background: transparent url(https://java.sun.com/developer/technicalArticles/javase/java6u10/images/metal.gif) no-repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 654px; height: 400px;" class="imgbox imgcenter"><img height="400" border="0" width="654" style="display: none;" alt="SwingSet3 in Metal" src="https://java.sun.com/developer/technicalArticles/javase/java6u10/images/metal.gif" /></div>
<div class="small" style="text-align: center;">Figure 3: SwingSet3 in Metal</div>
</div>
</div>
</div>
<!-- END G20 COMPONENT V.9 -->
<span class="sp20"> </span><br />
<!-- BEGIN G20 COMPONENT V.9 -->
<div class="g20 g20r1 g20v9">
<div class="g20w1" style="width: 665px;">
<div class="g20w2">
<div style="background: transparent url(https://java.sun.com/developer/technicalArticles/javase/java6u10/images/nimbus.gif) no-repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 654px; height: 400px;" class="imgbox imgcenter"><img height="400" border="0" width="654" style="display: none;" alt="SwingSet3 in Nimbus" src="https://java.sun.com/developer/technicalArticles/javase/java6u10/images/nimbus.gif" /></div>
<div class="small" style="text-align: center;">Figure 4: SwingSet3 in Nimbus</div>
</div>
</div>
</div>
<!-- END G20 COMPONENT V.9 -->
<span class="sp10"> </span><br />
<p>For compatibility reasons, Metal is still the default Swing look and feel, but
updating applications to use Nimbus couldn't be simpler. It only takes a single
line of code:</p>
<!-- BEGIN PC1 COMPONENT V.2 -->
<div class="pc1 pc1v2">
<div class="pc1w1"><div class="pc1w2"><div class="pc1w3 cornerBL"><div class="pc1w4">
<pre>UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");</pre>
</div></div></div></div>
</div>
<!-- END PC1 COMPONENT V.2 -->
<p>You can also force Nimbus to be the default look and feel by specifying
<code>-Dswing.defaultlaf=com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel</code>.
on the command line. A more permanent way to set the property is to add</p>
<!-- BEGIN PC1 COMPONENT V.2 -->
<div class="pc1 pc1v2">
<div class="pc1w1"><div class="pc1w2"><div class="pc1w3 cornerBL"><div class="pc1w4">
<pre>swing.defaultlaf=com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel</pre>
</div></div></div></div>
</div>
<!-- END PC1 COMPONENT V.2 -->
<p>to the file <code><JAVA_HOME>/lib/swing.properties</code>. You will have to create
the swing.properties file if it does not already exist.</p>
<p>For further reading about Nimbus, take a look at <a href="https://java.sun.com/javase/downloads/ea/6u10/nimbus.jsp">the Nimbus early
access page.</a></p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/fvQ5f0QOh-I" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/swingset3_nimbus_and_java_se
https://blogs.sun.com/CoreJavaTechTips/entry/jslider_appearance_improvements
JSlider Appearance Improvements
dananourie
2008-04-04T13:34:02-07:00
2008-04-04T13:34:02-07:00
Learn how you can change any of the three
defaults in JSlider: the direction of the scrollbar, and whether tick marks should
appear, and what to show next to them.
<p>by John Zukowsi </p>
<p>The <code>JSlider</code> component is a popular component for selecting a value from
a numerical range. While some people might think of a <code>JScrollBar</code> for
numerical input, that really serves the purpose of scrolling around a
viewport, not data input. By default, the input range of a <code>JSlider</code> is
<code>0</code> to <code>100</code>, with an initial value of <code>50</code>. 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.</p>
<p>First off is the basic <code>JSlider</code>, 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 <code>SliderSample</code>
program actually has four sliders; two horizontal and two vertical.</p>
<pre>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);
}
}</pre>
<p>Compiling and running the program shows a screen with four sliders on
it. While you cannot tell from the screen, the ones in the <code>EAST</code> and
<code>SOUTH</code> regions of the <code>BorderLayout</code> are actually inverted. So, for the
horizontal sliders, <code>0</code> is on the left for the top one and <code>100</code> is on the
left for the inverted bottom one. For the vertical sliders, <code>0</code> is on
the bottom for the left one and on the top for the inverted right one.</p>
<p><img src="https://blogs.sun.com/CoreJavaTechTips/resource/first.png" width="300" height="200" /></p>
<p>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 <code>getValueIsAdjusting()</code>
method reports true while the user still has the knob selected with the
mouse.</p>
<pre> 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());
}
}
} </pre>
<p>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 <code>setPaintTicks(true)</code>, 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.</p>
<pre>
js1.setPaintTicks(true);
js1.setMajorTickSpacing(10);
js1.setMinorTickSpacing(5);
js3.setPaintTicks(true);
js3.setMajorTickSpacing(10);
js3.setMinorTickSpacing(5); </pre>
<p><img src="https://blogs.sun.com/CoreJavaTechTips/resource/ticks.png" width="300" height="200" /></p>
<p>Showing tick marks certainly helps, but showing labels makes things
most useful. Adding <code>setPaintLabels(true)</code> 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 <code>0</code>, the associated
label is string "0," and so on for "10,", "20,", all the way to "100."</p>
<p><img src="https://blogs.sun.com/CoreJavaTechTips/resource/labels.png" width="400" height="300" /></p>
<p>Instead of showing the cardinal numbers, you can provide a label table:
<code>setLabelTable(Dictionary labels)</code>. In the table, you would map integer
values to different components to show as the label. In generic-speak,
that would be <code>Dictionary<Integer, JComponent></code>, though the method
isn't explicitly defined to require the key-value pair to be such.
Typically, the mapping is <code>Integer</code> object to <code>JLabel</code>. As the Swing
component set predates the <code>Collections</code> framework, the method takes
a <code>Dictionary</code>, not a <code>Map</code>, so remember to use a <code>Hashtable</code> 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.</p>
<pre>
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); </pre>
<p><img src="https://blogs.sun.com/CoreJavaTechTips/resource/eleven.png" width="400" height="300" /></p>
<p>The definition for the <code>DiamondIcon</code> will be shown at end with the
full class definition.</p>
<p>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.</p>
<p><img src="https://blogs.sun.com/CoreJavaTechTips/resource/elevenBottom.png" width="400" height="300" /></p>
<p>One last thing you can do to affect the display. You can hide the
track that the knob slides on. Just call the <code>setPaintTrack(</code>) method,
with a setting of false. Here, the bottom slider has its track hidden.</p>
<p><img src="https://blogs.sun.com/CoreJavaTechTips/resource/hidden.png" width="400" height="300" /></p>
<p>There isn't that much more you can do to customize the look of the
<code>JSlider</code>, 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.</p>
<p>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.</p>
<pre>
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);
}
}
}
</pre>
</p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/vgK7AQUqpOs" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/jslider_appearance_improvements
https://blogs.sun.com/CoreJavaTechTips/entry/using_generics_with_wildcards_and
Using Generics With Wildcards and Extends
dananourie
2008-03-10T16:56:23-07:00
2008-06-26T14:08:57-07:00
Learn how to use generics with Wildcards and Extends.
<p>By John Zukowski
</p>
<p>Java 2 Platform, Standard Edition 5.0 (J2SE 5.0) introduced
generics to the Java programming language and platform. In the
simplest case and typical usage, generics allow for the
identification of what you want to store in a collection. So instead
of saying that your program has a <code>List</code> of
<code>Objects</code>, you can now specify that you have a
<code>List</code> of <code>String</code> objects or some other class
type. Then, if you accidentally try to add something to the
<code>List</code> that is of the wrong type, the compiler notifies
you of the error and it can be fixed at compile time, rather than
having to wait until you run the program and the program reaches the
point in code where the fetch operation produces a runtime casting
exception. </p>
<p>This brings up a second benefit of generics. Iterators now become
typesafe. The <code>next()</code> method of the
<code>Iterator</code> interface returns the typesafe version of the
next element of the collection.
</p>
<p>But this is not a tip about the use of generics, which a <a href="https://java.sun.com/developer/JDCTechTips/2005/tt0315.html#1" title="2005 tip">2005 Core Java Technologies Tip</a> explained. Most
people don't fully understand the use of the <code>extends</code>
keyword when using generics. A typical example shown to explain the
use of <code>extends</code> has to do with drawing shapes. Instead,
this tech tip will use an example that uses Swing components so that
you do not have to create extra new classes. In a very limited case,
the class hierarchy for Swing button components is shown here, with
<code>Object</code> as the real root: </p>
<pre>Component
|- Container
|- JComponent
|- AbstractButton
|- JButton
|- JMenuItem
|- JCheckBoxMenuItem
|- JMenu
|- JRadioButtonMenuItem
|- JToggleButton
|- JCheckBox
|- JRadioButton
</pre>
<p>One thing that all <code>AbstractButton</code> subclasses share
in common is a <code>getText()</code> method. So in the spirit of
generics, you can define a method that takes a <code>List</code> of
<code>AbstractButton</code> items and return a <code>List</code> of
the <code>String</code> labels of those buttons. Here's the first
version of such a method:
</p>
<pre> 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;
}
</pre>
<p>And here is how you might use the method. First, define a
<code>List</code> of <code>AbstractButton</code> types, fill it up,
and call the method:
</p>
<pre> 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<string> labels = getLabels(buttonList);
System.out.println(labels);
</string></pre>
<p>"Hola, Mundo" is the Spanish translation of "Hello, World,"
according to Google. The results of the <code>println()</code> call
is as follows:
</p>
<pre>[Hello, World, Hola, Mundo]
</pre>
<p>With a <code>List</code> of <code>AbstractButtons</code>,
everything functions fine, but this breaks down when the
<code>List</code> is of something else, specifically a subclass. One
would logically think that with a <code>List</code> of
<code>JButton</code> items, everything would work fine, because
<code>JButton</code> is a subclass of <code>AbstractButton</code>.
Shouldn't you be able to call
<code>getLabels(List<AbstractButton>)</code> with a
<code>List</code> of a subclass of <code>AbstractButton</code>?
</p>
<pre> List<JButton> buttonList = ...
// ... Fill list ...
List<String> labels = getLabels(buttonList);
</pre>
<p>Well, that isn't the case. Because this is a compile-time check,
and because the definition of <code>getLabels()</code> is defined to
accept only an <code>AbstractButton List</code>, you cannot pass
anything else to it. The compilation-time error message follows:
</p>
<pre>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
</pre>
<p>And this is where the <code>extends</code> keyword comes in
handy. Instead of defining the <code>getLabels()</code> method to
accept only an <code>AbstractButton</code> list, define it to accept
any <code>List</code> of <code>AbstractButton</code> subclasses:
</p>
<pre> public static List<String> getLabels(
List<? extends AbstractButton> list) {
</pre>
<p>The wildcard <code>?</code> here says that the method doesn't
care what the exact class type is, as long as it is a subclass of
<code>AbstractButton</code>. Here's a complete example that puts all
the pieces together:
</p>
<pre>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<string> 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;
}
}
</string></pre>
<p>Now, when you are defining your own classes and methods with
generics and are thinking of accepting an abstract class as the
generic argument, or any superclass, remember to use wildcards so
that the same method works best with subclasses too.
</p>
<p>For more information on generics, see two earlier tutorials by
Gilad Bracha: <a href="https://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf">a 2004
tutorial (PDF)</a> and the generics lesson in the online <a href="https://java.sun.com/docs/books/tutorial/extra/generics/">Java
Tutorial</a>.
</p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/3XlMSIyZ2vQ" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/using_generics_with_wildcards_and
https://blogs.sun.com/CoreJavaTechTips/entry/getting_to_know_boxlayout
Getting to Know BoxLayout
dananourie
2008-02-12T15:37:33-08:00
2008-02-12T15:37:33-08:00
Learn how and when to use the BoxLayout manager, plus how to make use of the Box struts and glue components.
<p>One of the standard layout managers that come with the Java platform is
<code>BoxLayout</code>. 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 <code>Box</code> 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.</p>
<h3>Typical Usage</h3>
<p>BoxLayout is unlike most layout managers which just require you to create
the layout manager, and associate the layout manager with the <code>Container</code>.
Instead, the <code>BoxLayout</code> constructor requires you to pass the <code>Container</code>
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 <code>Box</code> container more popular, since all you have to
do is ask for a horizontally or vertically laid out <code>Box</code> through one of
its <code>static</code> methods:</p>
<pre> Box vertical = Box.createVerticalBox();
Box horizontal = Box.createHorizontalBox();</pre>
<p>Both are using <code>BoxLayout</code> 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 <code>BoxLayout</code> (and thus <code>Box</code>) to
<code>GridLayout</code> requires a quick comment. When placing a bunch of components
in a <code>GridLayout</code> controlled container, all the components are expected
to be the same size. With <code>BoxLayout</code>, that isn't the case, and the
maximum preferred size of the component is honored.</p>
<h3>Struts and Glue</h3>
<p>The Box class offers the creation of two supporting components, one
a strut, or fixed-size filler area, the other glue for an expandable area.
The use of these allows you to place components within a container, either
a fixed-distance apart with a strut, or a growing/shrinking area based upon
available space, with glue. The same task can be done with <code>GridBagConstraints</code>
and <code>GridBagLayout</code>, though not as easily.</p>
<p>To demonstrate, this first program creates a 25 pixel strut between the
top two components and a 10 pixel strut between the bottom two.</p>
<pre>
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);
}
}</pre>
<p>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 <code>JButton</code> to avoid an explanation
of component alignment until a little later.</p>
<p><img src="https://blogs.sun.com/CoreJavaTechTips/resource/vBoxBefore.png"><br>
<img src="https://blogs.sun.com/CoreJavaTechTips/resource/vBoxAfter.png"></p>
<p>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.</p>
<pre>
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);
}
}</pre>
<p><img src="https://blogs.sun.com/CoreJavaTechTips/resource/hBoxBefore.png"><br>
<img src="https://blogs.sun.com/CoreJavaTechTips/resource/hBoxAfter.png"></p>
<p>Trying not to confuse you too much, but the example is back to
using <code>JButton</code> components.</p>
<h3>Alignment</h3>
<p>Life gets interesting with <code>Box/BoxLayout</code> 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?</p>
<p>This is where the different alignments of a component come into play.
Each Swing component has an <code>X-alignment</code> setting and a <code>Y-alignment</code> setting
thanks to its <code>get/setAlignmentX()</code> and <code>get/setAlignmentY()</code> methods. The
range of each setting is from <code>0</code> to <code>1.0</code>, inclusive, where <code>0</code> represents left
or top alignment and <code>1</code> represents right or bottom alignment, depending
upon direction of <code>BoxLayout</code>. There are constants available in the
<code>Component</code> 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.</p>
<p>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.</p>
<pre>
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);
}
}</pre>
<p><img src="https://blogs.sun.com/CoreJavaTechTips/resource/alignX.png"></p>
<p>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.</p>
<p>Here's the program to demonstrate:</p>
<pre>
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);}
}
</pre>
<p>And, the corroborating screen:</p>
<p><img src="https://blogs.sun.com/CoreJavaTechTips/resource/alignDiffX.png"></p>
<p>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. </p>
<p>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.</p>
<p>If you are still confused, feel free to modify the earlier programs
and try even more combinations of <code>x</code> and <code>y</code> alignment. Of course, if
all this baffles you, there is always <code>GridBagLayout</code>.</p>
<p>******</p>
<a href="https://slurl.com/secondlife/Sun%20Microsystems/127/26/62">SDN Chat: Meet the Writers of java.sun.com</a><br>
Please join us in <a href="https://slurl.com/secondlife/Sun%20Microsystems/127/26/62">Sun's Developer Playground</a> in <a href="https://www.secondlife.com">Second Life</a> on Thursday, <b>February 14 at 10am PST</b> 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!
</p><br><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/GqN4wdJ1nD8" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/getting_to_know_boxlayout
https://blogs.sun.com/CoreJavaTechTips/entry/sorting_strings
Sorting Strings
dananourie
2008-01-23T13:41:51-08:00
2008-01-23T13:41:51-08:00
Sorting strings with the Java platform can be thought of
as an easy task, but there is much more thought that should
be put into it when developing programs for an international
market.
by John Zukowski
<p>
Sorting strings with the Java platform can be thought of
as an easy task, but there is much more thought that should
be put into it when developing programs for an international
market. If you're stuck in the English-only mindset, and you
think your program works fine because it shows that the string
tomorrow comes after today, you might think all is great. But,
once you have a Spanish user who wants mañana to be sorted
properly, if all you use is the default <code>compare()</code> method of String
for sorting, the ñ character will come after the z character
and will not be the natural Spanish ordering, between the n character
and o character. That's where the Collator class of the <code>java.text</code>
package comes into play.
</p>
<p>
Imagine a list of words
<ul>
<li> first
<li> mañana
<li> man
<li> many
<li> maxi
<li> next
</ul>
</p>
<p>
Using the default sorting mechanism of <code>String</code>, its <code>compare()</code> method,
this will result in a sorted list of:
<ul>
<li> first
<li> man
<li> many
<li> maxi
<li> mañana
<li> next
</ul>
</p>
<p>
Here, mañana comes between maxi and next. In the Spanish world, what
should happen is mañana should come between many and maxi as the ñ character
(pronounced eñe) comes after the n in that alphabet. While you could write
your own custom sort routine to handle the ñ, what happens to your program
when a German user comes around and wants to use their own diacritical
marks, or what about just a list of design patterns with façade? Do
you want façade before or after factory? (Essentially treating the
ç with the little cedilla hook the same as c or different.)
</p>
<p>
That's where the <code>Collator</code> class comes in handy. The <code>Collator</code> class
takes into account language-sensitive sorting issues and doesn't just
try to sort words based upon their ASCII/Unicode character values.
Using <code>Collator</code> requires understanding one additional property before
you can fully utilize its features, and that is something called
strength. The strength setting of the <code>Collator</code> determines how
strong (or weak) a match is used for ordering. There are four possible
values for the property: PRIMARY, SECONDARY, TERTIARY, and IDENTICAL.
What actually happens with each is dependent on the locale. Typically
what happens is as follows. In reverse order, IDENTICAL strength means
just that, the characters must be identical for them to be treated the
same. TERTIARY typically is for ignoring case differences. SECONDARY
is for ignoring diacritical marks, like n vs. ñ. PRIMARY is like
IDENTICAL for base letter differences, but has some differences when
handling control characters and accents. See the <code>Collator</code> javadoc
for more information on these differences and decomposition mode rules.
</p>
<p>
To work with <code>Collator</code>, you need to start by getting one. You can either
call <code>getInstance()</code> to get one for the default locale, or pass the
specific <code>Locale</code> to the <code>getInstance()</code> method to get a locale for
the one provided. For instance, to get one for the Spanish language,
you would create a Spanish <code>Locale</code> with new <code>Locale("es")</code> and then
pass that into <code>getInstance()</code>:
</p>
<pre>
Collator esCollator =
Collator.getInstance(new Locale("es"));
</pre>
<p>
Assuming the default <code>Collator</code> strength for the locale is sufficient,
which happens to be SECONDARY for Spanish,
you would then pass the <code>Collator</code> like any <code>Comparator</code> into the
<code>sort()</code> routine of <code>Collections</code> to get your sorted <code>List</code>:
</p>
<pre>
Collections.sort(list, esCollator);
</pre>
<p>
Working with the earlier list, that now gives you a proper
sorting with the Spanish alphabet:
</p>
<ul>
<li> first
<li> man
<li> many
<li> mañana
<li> maxi
<li> next
</ul>
</p>
<p>
Had you instead used the US Locale for the <code>Collator</code>, mañana would
appear between man and many since the ñ is not its own letter.
</p>
<p>
Here's a quick example that shows off the differences.
</p>
<pre>
import java.awt.*;
import java.text.*;
import java.util.*;
import java.util.List; // Explicit import required
import javax.swing.*;
public class Sort {
public static void main(String args[]) {
Runnable runner = new Runnable() {
public void run() {
String words[] = {"first", "mañana", "man",
"many", "maxi", "next"};
List<String> 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);
}
}
</pre>
<p><center>
<img src="https://blogs.sun.com/CoreJavaTechTips/resource/sorting.png">
</p></center>
<p>
One last little bit of information about collation. The
<code>Collator</code> returned by the <code>getInstance()</code> call is typically an instance
of <code>RuleBasedCollator</code> for the supported languages. You can use
<code>RuleBasedCollator</code> to define your own collation sequences. The javadoc
for the class describes the rule syntax more completely, but lets say
you had a four character alphabet and wanted the order of the letters
to be CAFE instead of ACEF, your rule would look something like:
</p>
<pre>
String rule =
"< c, C < a, A < f, F < e, E";
RuleBasedCollator collator = new RuleBasedCollator(rule);
</pre>
<p>
This defines the explicit order as cafe, with the different letter
cases shown. Now, for a list of words of ace, cafe, ef, and face,
the resultant sort order is cafe, ace, face, and ef with the new rules:
</p>
<pre>
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<String> list = Arrays.asList(words);
Collections.sort(list, collator);
System.out.println(list);
}
}
</pre>
<p>
After compiling and running, you see the words sorted with the new
rules:
</p>
<pre>
> javac Rule.java
> java Rule
[cafe, ace, face, ef]
</pre>
<p>
After reading the rule syntax some more in the javadocs, try to expand the
alphabet and work with the different diacritical marks, too.
</p>
<p>
Now, when developing programs for the global world, your programs
can be better prepared to suit the local user. Be sure to keep strings
in resource bundles, too, as shown in an earlier tip:
</p>
<p>
<a href"https://java.sun.com/developer/TechTips/1998/tt0521.html#tip2">Earlier tip</a>
</p>
<p>
*********<p>
<img src="https://www.sun.com/images/l2/l2_try-glassfish.gif" align="left">
<a href="https://java.sun.com/javaee/community/campaigns/iphone-012008/welcome.jsp">Connect and Participate With GlassFish</a><br>
Try GlassFish for a chance to win an iPhone. This sweepstakes ends on March 23, 2008. Submit your entry today. </p>
<br clear="all">
<p>
<a href="https://java.sun.com/developer/media/deepdivebluray.jsp">Foote on Blu-ray Disc Java</a> 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. <A href="https://hdcookbook.dev.java.net">Download the code</a>
</p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/ARHlGmOf_Bo" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/sorting_strings
https://blogs.sun.com/CoreJavaTechTips/entry/quiz_answers
Quiz Answers
dananourie
2008-01-08T09:38:25-08:00
2008-01-08T09:38:25-08:00
Answers to Quiz
<b>Answers</b>
</p>
<p>
1. Given two objects <code>one</code> and <code>two</code> of <code>BigDecimal</code> type, how do you multiply the two factors to calculate a product in object <code>three</code>? Answer: D<br>
To generate the product of two variables of <code>BigDecimal</code> type, use the <code>multiply()</code> method of <code>BigDecimal</code>. This was shown in <a href="https://java.sun.com/mailers/techtips/corejava/2007/tt0707.html#2" target="_blank"></a>"The Need for <code>BigDecimal</code>"</a> (July 2007), which also discussed formatting and rounding issues.
</p>
<p>
2. In order to write the line <code>System.out.println("Pi = " + PI)</code>, what must the import statement be so that the compiler will locate <code>PI</code> in the <code>Math</code> class? Answer: C<br>
Use static import statements to tell the compiler about constants and methods you wish to use without explicitly specifying the class they come from. See <a href="https://java.sun.com/developer/JDCTechTips/2004/tt1005.html#1" target="_blank"></a>"Using Static Imports for Constants and Methods"</a> (October 2004) for more information on working with static imports.
</p>
<p>
3. When using an enhanced <code>for</code> loop (also known as a <code>foreach</code> statement), what interface must the element following the colon (:) implement in order for the construct to compile and execute appropriately? Answer: B<br>
The argument must implement the <code>Iterable</code> interface, which consists of a single method, to get an iterator:
</p>
<p>
<code> Iterator<T> iterator()</code>
</p>
<p>
<a href="https://java.sun.com/developer/JDCTechTips/2005/tt0505.html#2" target="_blank">"The Enhanced <code>For</code> Loop"</a> (May 2005) started the discussion of using the enhanced <code>for</code> loop. The use of <code>Iterable</code> was discussed later in <a href="https://java.sun.com/mailers/techtips/corejava/2007/tt0907.html#1" target="_blank">"Using Enhanced For-Loops With Your Classes"</a> (September 2007).
</p>
<p>
4. When two Swing components overlap in their display area, how do you control which component is drawn on top? Answer: C<br>
Z-order represents the layering of the components on the screen, where x and y coordinates are for horizontal and vertical positioning, respectively. By calling the <code>setComponentZOrder()</code> method of the <code>Container</code> for each component that overlaps, you can explicitly control which components are drawn on top of which other components. See <a href="https://java.sun.com/developer/JDCTechTips/2005/tt0118.html#1" target="_blank">"Let There Be Z-Order" tip</a> (January 2005) for additional information on controlling the z-order layering.
</p>
<p>
5. What is the best way to monitor the progress of image reading when using the Java Image I/O API? Answer: C<br>
Although you can certainly display progress with a <code>Progress Monitor</code>, the best way to set up notification of such progress is by using a <code>IIOReadProgressListener</code>. <a href="https://java.sun.com/mailers/techtips/corejava/2007/tt0207.html#2" target="_blank">"Monitoring Image I/O Events"</a> (February 2007) covered this notification as well as region-update notifications.
</p>
<p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/8fnKpvkyWD4" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/quiz_answers
https://blogs.sun.com/CoreJavaTechTips/entry/january_2008_core_java_tech
January 2008 Core Java Tech Tips Quiz
dananourie
2008-01-08T09:37:29-08:00
2008-01-08T09:39:52-08:00
We've made it through another year, and it's time for another tech tips quiz.
by John Zukowski
<p>
We've made it through another year, and it's time for another tech tips quiz. The last two -- <a href="https://java.sun.com/mailers/techtips/corejava/2006/tt0620.html#2" target="_blank">June 2006</a>
and <a href="https://java.sun.com/developer/JDCTechTips/2005/tt0913.html#2" target="_blank">September 2005</a> -- 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.
</p>
<p>
1. Given two objects <code>one</code> and <code>two</code> of <code>BigDecimal</code> type, how do you multiply the two factors to calculate a product in object <code>three</code>?
</p>
<p>
a. <code>BigDecimal three = one * two;</code><br>
b. <code>BigDecimal three = one.*(two);</code><br>
c. <code>BigDecimal three = one.times(two);</code><br>
d. <code>BigDecimal three = one.multiply(two);<code><br>
</p>
<p>
2. In order to write the line <code>System.out.println("Pi = " + PI)</code>, what must the import statement be so that the compiler will locate <code>PI</code> in the <code>Math</code> class?
</p>
<p>
a. <code>import java.lang.Math;</code><br>
b. <code>import java.lang.Math.PI;</code><br>
c. <code>import static java.lang.Math.PI;</code><br>
d. <code>import final java.lang.Math.PI;</code><br>
</p>
<p>
3. When using an enhanced <code>for</code> loop (also known as a <code>foreach</code> statement), what interface must the element following the colon (:) implement in order for the construct to compile and execute appropriately?
</p>
<p>
a. <code>Enumeration</code><br>
b. <code>Iterable</code><br>
c. <code>Iterator</code><br>
d. <code>Collection</code><br>
</p>
<p>
4. When two Swing components overlap in their display area, how do you control which component is drawn on top?
</p>
<p>
a. The component added to the container first is drawn on top.<br>
b. The component added to the container last is drawn on top.<br>
c. You call the <code>setComponentZOrder()</code> method of the container.<br>
d. You call the <code>setComponentZOrder()</code> method for each component.<br>
</p>
<p>
5. What is the best way to monitor the progress of image reading when using the Java Image I/O API?
</p>
<p>
a. Create a <code>FilteredReader</code> subclass to count the bytes.<br>
b. Attach a <code>ProgressMonitorListener</code> to the <code>ImageReader</code> to report progress.<br>
c. Attach an <code>IIOReadProgressListener</code> to the <code>ImageReader</code> for progress reporting.<br>
d. Register a <code>Runnable</code> with the <code>ImageReader</code> and tell it how frequently to execute.<br>
</p>
<p>Check your answers <a href="https://blogs.sun.com/CoreJavaTechTips/entry/quiz_answers">here</a>.<img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/nTdQHhqtjJg" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/january_2008_core_java_tech
https://blogs.sun.com/CoreJavaTechTips/entry/placing_components_on_tabs2
Placing Components on Tabs
dananourie
2008-01-02T11:36:08-08:00
2008-01-02T11:36:08-08:00
Learn how to create a <code>JTabbedPane</code> component container that accepts components placed within a panel for each tab.
<br>by John Zukowski
<p>The <code>JTabbedPane</code> component is a special Swing
container that accepts components to be placed within a panel for
each tab. Select a tab to see that tab's components. Technically
speaking, one component is associated with each tab. However, that
component is typically a panel that contains others. Identification
for each tab is done with title text and an icon, settable per tab.
The first Core Java Technologies Tech Tip to discuss the
<code>JTabbedPane</code> component was published in July 2001, with
a <a
href="https://java.sun.com/developer/JDCTechTips/2001/tt0712.html#tip
1" target="_blank">simple introductory tip</a>. </p>
<p>The methods of <code>JTabbedPane</code> to support the use of
components on tabs are as follows: </p>
<ul>
<li><code>public void setTabComponentAt(int index, Component component)</code>
<li><code>public Component getTabComponentAt(int index)</code>
<li><code>public int indexOfTabComponent(Component tabComponent)</code>
</ul>
<p>The first method associates the component with a tab; the second
gets it back; and the last one tells you which tab is associated
with the component, if any. Typically, you use only the first
method, but the others are available. </p>
<p>To get started with the task at hand, it is helpful to have an
Icon implementation that draws a little x on it. You could just use
the letter x as the button label, but you should avoid that option.
Typically, when a user sees an x in a box, this
signals a command to close a window, so it serves as a good
indicator for closing a tab. </p>
<pre>
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;
}
}
</pre>
<p>Before creating the special tab component, let's put together the
program's framework, one that creates a frame with a
<code>JTabbedPane</code> on it and adds a number of tabs:</p>
<pre>
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CloseTabs {
public static void main(String args[]) {
Runnable runner = new Runnable() {
public void run() {
JFrame frame = new JFrame("JTabbedPane");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTabbedPane jtp = new JTabbedPane();
frame.add(jtp, BorderLayout.CENTER);
for (int i=0; i<5; i++) {
JButton button = new JButton("Card " + i);
jtp.add("Btn " + i, button);
// new CloseTabButton(jtp, i);
}
frame.setSize(400, 200);
frame.setVisible(true);
}
};
EventQueue.invokeLater(runner);
}
}
</pre>
<p> This program creates five tabs, each with a <code>JButton</code>
on it. The tab title is "Btn" followed by the tab position. </p>
<p><center>
<img src="https://blogs.sun.com/CoreJavaTechTips/resource/original.png">
</center></p>
<p>The program will run fine without doing anything else, but it
doesn't have the close button on each tab. To add a close button to
the tab, you need to retain the tab's title text and icon, while
adding a button with the <code>CloseIcon</code> previously shown.
(The
example does not use the tab's icon, but the class definition
includes one so that it can be reused beyond this example.) As the
final part of the definition, you need an
<code>ActionListener</code> for the button to handle when it is
pressed, so that when the user presses the button, the tab is
removed from the pane. </p>
<pre>
class CloseTabButton extends JPanel implements ActionListener {
private JTabbedPane pane;
public CloseTabButton(JTabbedPane pane, int index) {
this.pane = pane;
setOpaque(false);
add(new JLabel(
pane.getTitleAt(index),
pane.getIconAt(index),
JLabel.LEFT));
Icon closeIcon = new CloseIcon();
JButton btClose = new JButton(closeIcon);
btClose.setPreferredSize(new Dimension(
closeIcon.getIconWidth(), closeIcon.getIconHeight()));
add(btClose);
btClose.addActionListener(this);
pane.setTabComponentAt(index, this);
}
public void actionPerformed(ActionEvent e) {
int i = pane.indexOfTabComponent(this);
if (i != -1) {
pane.remove(i);
}
}
}
</pre>
<p>You can now reuse the <code>CloseTabButton</code> in your own
tabbed panes. </p>
<p>
<center>
<img src="https://blogs.sun.com/CoreJavaTechTips/resource/close.png">
</center></p>
<p>
For more information on the use of tabbed panes, see the <a href="https://java.sun.com/tutorial/uiswing/components/tabbedpane.html" target="_blank">How to Use Tabbed
Panes</a> lesson in The Java Tutorial.
</p>
<img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/ygaecGq52pg" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/placing_components_on_tabs2
https://blogs.sun.com/CoreJavaTechTips/entry/tech_days_comes_back_to
Tech Days Comes Back to the USA
dananourie
2007-12-12T13:45:35-08:00
2007-12-12T13:45:35-08:00
On January 9 & 10, the Tech Days Conference will be in Atlanta Georgia. (FREE!)
On January 9 & 10, the Tech Days Conference will be in Atlanta Georgia. (FREE!)
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
The sessions that will be covered:
</p>
<p>
<b> Java Development Tracks</b>
<ul>
<li> Java EE 6 and GlassFish
<li> Java SE 6 Top 10 features, Java SE 7 and OpenJDK
<li> Next Generation Web and Java Server Faces
<li> Consumer JRE: Java on Every Desktop OR Java Performance Myths
<li> Java ME: Extreme GUI Makeover for Mobile Phones
<li> SOA using OpenESB and JCAP
<li> Java Scripting: JavaFX Script and JRuby </ul>
</p>
<p>
<b>Solaris Development Track</b>
<li> What Makes Solaris Interesting?
<li> Sun Studio Tools
<li> OpenSolaris Security Frameworks
<li> Integrating ZFS Into Your Applications
<li> Security Features in Solaris
<li> How To Develop Solaris Parallel Applications
<li> Solaris Networking For Developers </ul>
</p>
<p>
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.
</p>
<p>
<a href="https://developers.sun.com/events/techdays/2008/US_ATL.jsp">Sun Tech Days</a>
<img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/A5K0qw8fGSc" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/tech_days_comes_back_to
https://blogs.sun.com/CoreJavaTechTips/entry/new_certification_courses
New Certification Courses
dananourie
2007-12-10T10:46:11-08:00
2007-12-10T10:46:11-08:00
Read about and sign up for these two new certification courses.
New Certification Courses
<a href="https://www.sun.com/training/catalog/courses/CX-310-065.xml">Sun Certified Programmer for the Java Platform, Standard Edition 6</a>-- 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.</p>
<p>
<a href="https://www.sun.com/training/catalog/courses/CX-310-066.xml">Sun Certified Programmer for the Java Platform, Standard Edition 6 Upgrade Exam (CX-310-066)</a>-- 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.
</p>
<p>
******
<p>
<a href="https://slurl.com/secondlife/Sun%20Microsystems/172/162/91" target="_blank"><img src="https://www.sun.com/images/l2/l2_dananouri_sl.gif" alt="Sun Microsystems Developer Playground - Dana Nourie" width="100" height="88" border="0" style="float: left; margin-right: 10px" /></a>
<b><a href="https://slurl.com/secondlife/Sun%20Microsystems/172/162/91" target="_blank">Sun Microsystems Developer Playground</a></b><br />
Join Dana Nourie December 18th, Tues 9 AM PST and 7 PM PST, in <a href="https://www.secondlife.com" target="_blank">Second Life</a> at the <a href="https://slurl.com/secondlife/Sun%20Microsystems/172/162/91" target="_blank">Sun Microsystems Developer Playground</a> to discover how easy it is to learn the Java platform through web programming. Watch a demo and have your questions answered.
</p>
******<img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/W9QHZ9G4il4" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/new_certification_courses
https://blogs.sun.com/CoreJavaTechTips/entry/get_netbeans_6
Using Callable to Return Results From Runnables
dananourie
2007-12-03T14:59:53-08:00
2007-12-04T10:45:06-08:00
Learn about the Callable interface, and its call method to return an Object.
by John Zukowski
<p>The <code>Runnable</code> interface has been around since the
beginning of time for the Java platform. It allows you to define a
task to be completed by a thread. As most people probably know
already, it offers a single method <code>run()</code> that accepts
no arguments and returns no values, nor can it throw any checked
exceptions. If you need to get a value back from the now-completed
task, you must use a method outside the interface and wait for some
kind of notification message that the task completed. For example,
the following demonstrates what you might do for just such a
scenario:
<pre>
Runnable runnable = ...;
Thread t = new Thread(runnable);
t.start();
t.join();
String value = someMethodtoGetSavedValue()
</pre>
<p>Nothing is inherently wrong with this code, but it can be done
differently now, thanks to the <code>Callable</code> interface
introduced in J2SE 5.0. Instead of having a <code>run()</code>
method, the <code>Callable</code> interface offers a
<code>call()</code> method, which can return an Object or, more
specifically, any type that is introduced in the genericized form:
<pre>
public interface Callable<V> {
V call() throws Exception;
}
</pre>
<p>Because you cannot pass a <code>Callable</code> into a
<code>Thread</code> to execute, you instead use the
<code>ExecutorService</code> to execute the <code>Callable</code>
object. The service accepts <code>Callable</code> objects to run by
way of the <code>submit()</code> method:
<pre>
<T> Future<T> submit(Callable<T> task)
</pre>
<p>As the method definition shows, submitting a Callable object to
the <code>ExecutorService</code> returns a <code>Future</code>
object. The <code>get()</code> method of <code>Future</code> will
then block until the task is completed. This is the equivalent of
the <code>join()</code> call in the first example. Actually, it is
the equivalent of both the <code>join()</code> call and the get
value call as <code>get()</code> returns the value calculated by the
<code>Callable</code> instance.
<p>To demonstrate, the following example creates separate
<code>Callable</code> instances for each word passed in on the
command line and sums up their length. Each <code>Callable</code>
will just calculate the sum of its individual word. The set of
<code>Future</code> objects are saved to acquire the calculated
value from each. If the order of the returned values needed to be
preserved, a <code>List</code> could be used instead.
<pre>
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);
}
}
</pre>
<p>The <code>WordLengthCallable</code> saves each word and uses the
word's length as the value returned by the <code>call()</code>
method. This value could take some time to generate but in this case
is known immediately. The only requirement of <code>call()</code> is
the value is returned at the end of the call. When the
<code>get()</code> method of <code>Future</code> is later called,
the <code>Future</code> will either have the value immediately if
the task runs quickly, as in this case, or will wait until the value
is done generating. Multiple calls to <code>get()</code> will not
cause the task to be rerun in the thread.
<p>Because the goal of the program is to calculate the sum of all
word lengths, it doesn't matter in which order the
<code>Callable</code> tasks finish. It is perfectly OK if the last
task completes before the first three. The first <code>get()</code>
call to <code>Future</code> will just wait for the first task in the
<code>Set</code> to complete. This does not block other tasks from
running to completion separately. It is just waiting for that one
thread or task to complete.
<p>This particular example uses a fixed-size thread pool for the
<code>ExecutorService</code>, but any available service will do.
<p>For more information on the use of executors and thread pools,
see the <a
href="https://java.sun.com/tutorial/essential/concurrency/executors.h
tml">Executors lesson</a> in the Java Tutorial. The
SwingWorker class is another example of a
<code>Runnable</code> object that works with a <code>Future</code>,
though in a slightly different way. See the <a
href="https://java.sun.com/tutorial/uiswing/concurrency/worker.html">
Worker Threads and SwingWorker lesson</a> for more information on
that.
</p>
************<br>
<p>
<a href="https://www.netbeans.org"><img src="https://blogs.sun.com/JavaFundamentals/resource/dlNB.gif" align="left" border="0">NetBeans 6.0</a> 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. <a href="https://www.netbeans.org">Download NetBeans 6.0</a>
</p><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/FGpYQlwr4VY" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/get_netbeans_6
https://blogs.sun.com/CoreJavaTechTips/entry/using_the_mbeanregistration_interface_to
Using the MBeanRegistration Interface to Manage MBean Life Cycles
dananourie
2007-11-28T12:29:36-08:00
2007-11-28T12:43:49-08:00
This technical tip focuses using the <code>MBeanRegistration</code> interface to manage Management Bean (MBean) life cycles.
<p>
By <a href="https://blogs.sun.com/jmxetc" target="_blank">Daniel Fuchs</a>
<p>The <a href="https://java.sun.com/javase/technologies/core/mntr-mgmt/javamanagement/">Java Management Extensions (JMX)</a> have been present in the JDK since J2SE 5.0. In JDK 6, Sun has added a new advanced JMX sample to the JDK <code>sample</code> directory. The new sample -- <code>$JDK_HOME/sample/jmx/jmx-scandir</code> -- builds on the <a href="https://java.sun.com/javase/technologies/core/mntr-mgmt/javamanagement/best-practices.jsp">JMX best practices</a> and discusses some usual JMX patterns, and some pitfalls to avoid, when you design a management interface with JMX.
<p>In particular, the <code>jmx-scandir</code> sample shows all the different use cases that can be solved by implementing the <code><a href="https://java.sun.com/javase/6/docs/api/javax/management/MBeanRegistration.html">MBeanRegistration</a></code> interface.
<p>This technical tip will focus on one such use case: using the <code>MBeanRegistration</code> interface to manage Management Bean (MBean) life cycles. Readers who are not familiar with JMX and the notion of MBeans should follow the <a href="https://java.sun.com/docs/books/tutorial/jmx/index.html">JMX trail</a> from the <a href="https://java.sun.com/docs/books/tutorial/">Java Tutorials</a> before going any further.
<p><b>The MBeanRegistration Interface</b>
<p>The <code><a href="https://java.sun.com/javase/6/docs/api/javax/management/MBeanRegistration.html">MBeanRegistration</a></code> 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 <a href="https://java.sun.com/javase/6/docs/api/javax/management/MBeanServer.html"><code>MBeanServer</code></a>. When an MBean implements <code>MBeanRegistration</code>, methods defined in that interface will be called by the <code>MBeanServer</code> before and after the MBean is registered and unregistered from the <code>MBeanServer</code>.
<p>An MBean that implements the <code>MBeanRegistration</code> interface will obtain a reference to the <code>MBeanServer</code> in which it is being registered, will have a chance to obtain -- and possibly change -- its own <code><a href="https://java.sun.com/javase/6/docs/api/javax/management/ObjectName.html">ObjectName</a></code> before being registered, and will get an opportunity to accept or reject its own registration or unregistration.
<p>In the following code extract, the <code>Rack</code> MBean implements the <code>MBeanRegistration</code> interface:
<pre>
------------------------------------------------------------------
package jmxtechtip;
public interface RackMBean {
/... defines methods exposed for management .../
}
------------------------------------------------------------------
package jmxtechtip;
public class Rack implements RackMBean, MBeanRegistration {
/... implements RackMBean methods .../
/... implements MBeanRegistration methods .../
}
------------------------------------------------------------------
</pre>
<p>The MBean interface does not extend <code>MBeanRegistration</code>. Only the concrete MBean class implements it. Indeed, it would be a violation to expose the methods defined in <code>MBeanRegistration</code> through the management interface, since the <code>MBeanRegistration</code> methods are designed to be called only by the <code>MBeanServer</code> at registration time.
<p>This technical tip will show you how to implement the methods of <code>MBeanRegistration</code> to register and unregister dependent MBeans.
<p><b>The Racks and Cards Use Case</b>
<p>Let's assume you are designing a JMX application that will expose a piece of equipment hardware. Your equipment has a rack -- represented by a <code>RackMBean</code>. The rack itself has a number of slots where cards can be plugged in. When plugged in, each card is represented by a <code>CardMBean</code> or by a subclass of <code>CardMBean</code>.
<p>In this example, assume that the <code>RackMBean</code> is completely responsible for creating and registering <code>CardMBean</code> instances. The class <code>Card</code> can be extended to provide <code>Card</code>-specific implementation. The class <code>Rack</code> can be extended to override its <code>createCard(...)</code> method.
<p>Here are the aims you are trying to achieve:
<p>* In the class <code>Rack</code>, when a <code>RackMBean</code> is being registered, discover which slots already contain cards, and create and register a <code>CardMBean</code> for each of them.
<p>* In the class <code>Rack</code>, when a <code>RackMBean</code> is being unregistered, unregister all its corresponding instances of <code>Card</code>.
<p>* In the class <code>Card</code>, ensure that a <code>CardMBean</code> can only be registered by a <code>Rack</code>, and ensure that a <code>CardMBean</code> can only be unregistered by a <code>Rack</code>, and not by, for example, a client JMX application calling <code>MBeanServer.unregisterMBean(...)</code> directly for a <code>CardMBean</code>.
<p>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.
<p><b>Implementing the <code>MBeanRegistration</code> Interface</b>
<p>The following code snippet shows how the <code>Rack</code> MBean could implement the <code>MBeanRegistration</code> interface:
<pre>
/**
* 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<slots.length; i++) {
final Card card;
synchronized (slots) {
card = slots[i];
}
if (card == null) continue;
// Avoid calling an MBeanServer operation from
// within a synchronized block.
card.unregisterSelf();
synchronized (slots) {
slots[i] = null;
}
}
}
/**
* Allows the MBean to perform any operations needed after
* having been unregistered in the MBean server.
*
* In this implementation, you simply reset the internal
* server variable.
*/
*public void postDeregister() {*
// In the general case, an MBean will be garbage collected
// after it's been unregistered. Here you make the assumption that
// this object might be reused by the application code that created
// it, and you thus reset its internal variables to the value
// they had before its registration in the MBean server.
//
// This is an implementation choice and not a general rule.
rackName = null;
server = null;
}
// ------------------------------------------------------------
// *End of MBeanRegistration*
// ------------------------------------------------------------
/**
* Discovers what kind of card is in the given slot.
* If no card is plugged in, returns null.
**/
private String discoverCardType(int slot) { ... }
/**
* Check that the provided rackName corresponds to a rack that can
* be modeled by this class.
**/
private boolean isValidRack(String rackName) { ... }
/**
* Create a new instance of Card for the given cardType at the
* given slot.
* Can be overridden by subclasses to create subclasses of Card.
**/
protected Card createCard(int i, String cardType) {
return new Card(i, cardType);
}
// ------------------------------------------------------------
// Static stuff
// ------------------------------------------------------------
/**
* Creates an ObjectName for the card contained in rack 'rackName'
* at slot 'slot'.
**/
public static ObjectName createCardName(String rackName, int slot) throws MalformedObjectNameException {
final String domain = Card.class.getPackage().getName();
final String type = Card.class.getSimpleName();
final ObjectName name = new ObjectName(domain + ":type=" + type + "," +
RACK_KEY + "=" + rackName + ",slot=" + slot);
return name;
}
/**
* Returns the rack name embedded in the given ObjectName.
**/
public static String getRackName(ObjectName name) {
final String rackName = name.getKeyProperty(RACK_KEY);
if (rackName == null)
throw new IllegalArgumentException("missing rack name: " + name);
return rackName;
}
// A logger for this class
private final static Logger LOG = Logger.getLogger(Rack.class.getName());
/** This key is used in ObjectNames to indicate a rack name **/
public static String RACK_KEY = "rack";
}
</pre>
<p>In <code>preRegister <#preRegisterRack></code>, the <code>Rack</code> MBean obtains a reference to the <code>MBeanServer</code> in which it is registered, and a reference to its own <code>ObjectName</code>. It can check that the supplied <code>ObjectName</code> is valid -- in this example, it is considered valid if it contains the <code>rack</code> key -- and that it can indeed model the rack that it is asked to model. Here, this is supposed to be done by the <code>isValidRack(...)</code> method, whose implementation is not shown. If these conditions are not met, the <code>Rack</code> MBean will throw an exception, thus refusing to be registered.
<p>In <code>postRegister <#postRegisterRack></code>, the <code>Rack MBean</code> checks whether registration was successful. If it wasn't, it stops here. Otherwise, it will discover which slots already have a card plugged in, and for each of those, it will create and register a <code>CardMBean</code>. However, instead of using plain <code>MBeanServer.registerMBean(...)</code>, it calls a package-protected method on the created <code>Card</code> instance -- in this example, <code>Card.registerSelf() <#registerSelf></code>.
<p>This method has been created with logic such that a CardMBean can only be registered in the <code>MBeanServer</code> by invoking its <code>registerSelf(...)</code> method.
<p>Note that <code>postRegister</code> isn't supposed to throw an exception. You have thus protected all <code>Card</code> MBean registrations by a <code>try { } catch { }</code> block. This is OK because according to the application logic, <code>Card</code> MBean registration is not expected to fail at this point. If it does, you will simply log a warning message. Had you wanted the <code>Rack</code> MBean registration to fail whenever a <code>Card</code> MBean couldn't be registered, you would have implemented the <code>Card</code> registration logic in <code>preRegister <#preRegisterRack></code> instead. However, that logic would have been more complex, since you would have had to implement code to <code>rollback</code> registration of already registered <code>Card</code> MBeans in case one of them failed to be registered.
<p>In <code>preDeregister <#preDeregisterRack></code>, the <code>Rack</code> MBean unregisters all <code>Card</code> MBeans it has previously registered. Here again, instead of using plain <code>MBeanServer.unregisterMBean(...)</code>, it calls a package-protected method on the registered <code>Card</code> instance -- in this example, <code>Card.unregisterSelf() <#unregisterSelf></code>.
<p>This method has been created with logic such that a <code>Card</code> MBean can only be unregistered from the <code>MBeanServer</code> by invoking its <code>unregisterSelf(...)</code> method. Simply calling <code>MBeanServer.unregisterMBean(...)</code> would fail. This ensures that only the creator of the <code>Card</code> MBean will be able to register and unregister it. If a JMX client attempts to directly call <code>MBeanServer.unregisterMBean(...)</code> on a <code>Card</code> MBean, it will fail.
<p>Now let's see how the <code>Card</code> MBean uses its <a href="https://java.sun.com/javase/6/docs/api/javax/management/MBeanRegistration.html"><code>MBeanRegistration</code></a> interface. The following code snippet show how the <code>Card</code> MBean was implemented:
<pre>
/**
* A Card is plugged in a Rack slot.
*/
public class Card implements CardMBean, MBeanRegistration {
// This MBean will accept to be registered or unregistered
// only if 'isRegistrationAllowed' is 'true'.
private volatile boolean isRegistrationAllowed = false;
private volatile ObjectName name;
private volatile MBeanServer server;
public Card(int occupiedSlot, String cardType) { ... }
// This method sets 'isRegistrationAllowed' to 'true', calls
// server.registerMBean(this, name), and finally sets
// 'isRegistrationAllowed' back to false.
//
*void registerSelf(MBeanServer server, ObjectName name) throws JMException {*
*isRegistrationAllowed = true;*
try {
this.server = server;
this.name = name;
this.name = server.registerMBean(this, name).getObjectName();
} finally {
*isRegistrationAllowed = false;*
}
}
// This method sets 'isRegistrationAllowed' to 'true',
// calls server.unregisterMBean(name), and finally sets
// 'isRegistrationAllowed' back to false.
//
*void unregisterSelf() throws JMException {*
*isRegistrationAllowed = true;*
try {
if (server != null && server.isRegistered(name))
server.unregisterMBean(name);
this.server = null;
this.name = null;
} finally {
*isRegistrationAllowed = false;*
}
}
/**
* Allows the MBean to perform any operations it needs before being
* registered in the MBean server. *
* *In this example, the CardMBean will refuse to be registered if
* 'isRegistrationAllowed' is false -- which means that Card MBeans
* can only be registered by {@link #registerSelf registerSelf()}.*
*
* {@code registerSelf()} is a package method which is only called by
* {@link Rack}.
*/
*public final ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {*
if (*!isRegistrationAllowed*)
throw new IllegalStateException("Illegal registration attempt");
assert server == this.server && name == this.name;
return name;
}
/**
* Allows the MBean to perform any operations needed after having
* been registered in the MBean server or after the registration has
* failed.
* *The CardMBean does not need to do anything here.*
* @param registrationDone -- Indicates whether or not the MBean
* has been successfully registered in the MBean server. The value
* 'false' means that the registration has failed.
*/
*public void postRegister(Boolean registrationDone) {}*
/**
* Allows the MBean to perform any operations it needs before
* being unregistered by the MBean server.
* *The CardMBean will refuse to let itself be unregistered if
* 'isRegistrationAllowed' is false, which means that Card MBeans
* can only be unregistered by {@link #unregisterSelf unregisterSelf()}.*
*
* {@code unregisterSelf()} is a package method which is only called by
* {@link Rack}.
*
* @throws Exception -- This exception will be caught by the MBean
* server and rethrown as an MBeanRegistrationException.
*/
*public final void preDeregister() throws Exception {*
if (*!isRegistrationAllowed*)
throw new IllegalStateException("Illegal unregistration attempt"); }
/**
* Allows the MBean to perform any operations needed after having been
* unregistered in the MBean server.
* *The CardMBean does not need to do anything here.*
*/
*public void postDeregister() { }*
}
</pre>
<p>The logic in <code>preRegister <#preRegisterCard></code>, <code>preDeregister <#preDeregisterCard></code>, <code>registerSelf <#registerSelf></code>, and <code>unregisterSelf <#unregisterSelf></code> prevents the <code>Card</code> MBean from being registered or unregistered by anybody but the <code>Rack</code> MBean that created it. The <code>registerSelf</code> and <code>unregisterSelf</code> methods are kept package-protected so that only the <code>Rack</code> MBean can call them. The <code>preRegister</code> and <code>preDeregister</code> methods are declared final, so that subclasses cannot change that logic.
<p>Note: If you expected that subclasses of <code>Card</code> would need to perform some additional specific actions when being registered or unregistered, you would introduce a new protected hook specific to that purpose, as described in <a href="https://www.martinfowler.com/bliki/CallSuper.html" target="_blank">Martin Fowler's note</a>.
<p>However, using a simple <code>volatile boolean isRegistrationAllowed</code> flag still leaves some room for thread contention. Indeed, when the <code>Rack</code> MBean calls <code>unregisterSelf <#unregisterSelf></code>, another thread could sneak in and call <code>MBeanServer.unregisterMBean</code> right after the <code>isRegistrationAllowed</code> flag is set, but before <code>unregisterSelf</code> completes.
<p>A better solution would thus be to store the <code>isRegistrationAllowed</code> flag in a <a href="https://java.sun.com/javase/6/docs/api/java/lang/ThreadLocal.html"><code>ThreadLocal</code></a>.
<p>The following code snippet shows what would need to change in the <code>Card</code> MBean implementation:
<pre>
// initialization of isRegistrationAllowed flag:
// private volatile boolean isRegistrationAllowed = false;
// => replaced by:
private final static ThreadLocal<Boolean> isRegistrationAllowed = new ThreadLocal<Boolean>() {
@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(...);
...
</pre>
<b>Conclusion</b>
<p>This technical tip has shown how an MBean can make use of the <a href="https://java.sun.com/javase/6/docs/api/javax/management/MBeanRegistration.html"><code>MBeanRegistration</code></a> 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.
<p>The advanced JMX sample for JDK 6 -- <code>$JDK_HOME/sample/jmx/jmx-scandir</code> -- shows many other possibilities for using the <a href="https://java.sun.com/javase/6/docs/api/javax/management/MBeanRegistration.html"><code>MBeanRegistration</code></a> interface.
<p><b>About the Author</b>
<p><a href="https://blogs.sun.com/jmxetc">Daniel Fuchs</a> works on the Sun Microsystems Java DMK, JMX, Java SE Team in Grenoble, France.
</p>
<p>
*****************************
<a href="https://slurl.com/secondlife/Sun%20Pavilion/148/53/64"><img src="https://www.sun.com/images/l2/l2_dananouri_sl.gif" width="100" height="88" border="0" alt="Sun Microsystems Developer Playground" style="float:left;margin:0px 10px 10px 0px"></a>
<div><br />
<b><a href="https://slurl.com/secondlife/Sun%20Pavilion/148/53/64">Sun Microsystems Developer Playground</a> </b><br />
Join Dana Nourie November 29 at 9-10 AM PDT in <a href="https://www.secondlife.com">Second Life</a> at the <a href="https://slurl.com/secondlife/Sun%20Pavilion/148/53/64">Sun Microsystems Developer Playground</a> to chat about how you can learn the Java platform.
</p>
<br><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/MAuSviipFuw" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/using_the_mbeanregistration_interface_to
https://blogs.sun.com/CoreJavaTechTips/entry/getting_to_know_system_tray
Getting to Know System Tray
dananourie
2007-11-05T10:27:09-08:00
2007-11-28T12:55:37-08:00
Learn how to add applications to the Microsoft Windows taskbar, the Gnome notification area, or KDE's system tray.
by John Zukowski
<p>
The <a href="https://java.sun.com/javase/">Java SE 6</a> 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: <a href="https://java.sun.com/developer/JDCTechTips/2005/tt0505.html#1">Communicating With Native Applications Using JDIC</a>. Although its package changed to just <code>java.awt</code>, 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.
</p>
<p>
Access to the system tray requires only two new classes: <code>SystemTray</code> to represent the desktop's system tray and <code>TrayIcon</code> for its icon. Why not use <code>Image</code> or <code>Icon</code> for that? <code>TrayIcon</code> has an attached <code>PopupMenu</code> and tool tip text. Here's how all the pieces fit together.
</p>
<p>
The <code>SystemTray</code> class builds on the concept of the platform having a single instance of the tray. Calling the <code>getSystemTray()</code> method of <code>SystemTray</code> will return that instance. But some platforms might not support a system tray, so it is best to check first through the <code>isSupported()</code> method. Otherwise, an <code>UnsupportedOperationException</code> 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:
</p>
<p>
<pre>
if (SystemTray.isSupported()) {
SystemTray tray = SystemTray.getSystemTray();
}
</pre>
</p>
<p>
Most of the common platforms -- Microsoft Windows, Solaris Operating System, and Linux -- will support a system, but some platforms may not.
</p>
<p>
After getting the <code>SystemTray</code>, <code>TrayIcon</code> 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 <code>Image</code>.
</p>
<p>
Getting started with a basic framework to use <code>SystemTray</code> gives you the following program. The program puts a single <code>TrayIcon</code> on the <code>SystemTray</code> with a single <code>MenuItem</code> on its <code>PopupMenu</code>. In the installed JDK directory, there is a demo subdirectory. You can get an icon from that subdirectory. Any supported format will do.
</p>
<p>
<pre>
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);
}
}
</pre>
</p>
<p>
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 <code>Action/ActionListener</code> added to the <code>MenuItem</code>, it would have been activated upon selection.
</p>
<p>
Besides the ability to create a big frame for your application when you select the menu item, one other important method of <code>TrayIcon</code> is worth mentioning: <code>displayMessage()</code>. 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 <code>MessageType</code> to indicate the type of message. Valid types are <code>ERROR</code>, <code>WARNING</code>, <code>INFO</code>, or <code>NONE</code>. All but <code>NONE</code> have their own icon added to the pop-up window. The icon is a smaller version of the one shown in a <code>JOptionPane</code>.
</p>
<p>
The following program adds a few more menu items to the earlier example to demonstrate the <code>displayMessage()</code> method and all its message types. The final menu item with Close uses the <code>remove()</code> method of <code>SystemTray</code> to remove the <code>TrayIcon</code> from the <code>SystemTray</code> and shuts down.
</p>
<p>
<pre>
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);
}
}
</pre>
</p>
<p>
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.
</p>
<p>
*******
</p>
<p>
jMaki is a lightweight framework that makes it easy to build Ajax-based web applications. Learn more in this <a href="https://java.sun.com/developer/media/deepdivejmaki.jsp">Deep Dive</a> with jMaki Project lead, Greg Murray.
</p>
<p>
*******
</p>
<p>
<a href="https://slurl.com/secondlife/Sun%20Pavilion/148/53/64"><img src="https://www.sun.com/images/l2/l2_dananouri_sl.gif" width="100" height="88" border="0" alt="Sun Microsystems Developer Playground" style="float:left;margin:0px 10px 10px 0px"></a>
<div><br />
<b><a href="https://slurl.com/secondlife/Sun%20Pavilion/148/53/64">Sun Microsystems Developer Playground</a> </b><br />
Join Dana Nourie November 29 at 9-10 AM PDT in <a href="https://www.secondlife.com">Second Life</a> at the <a href="https://slurl.com/secondlife/Sun%20Pavilion/148/53/64">Sun Microsystems Developer Playground</a> to chat about how you can learn the Java platform.
</p>
<br><img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/ohyxVLRQnfw" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/getting_to_know_system_tray
https://blogs.sun.com/CoreJavaTechTips/entry/socket_logging
Socket Logging
dananourie
2007-10-29T10:05:28-07:00
2007-10-29T10:05:28-07:00
Learn how to set up a logging handler and configure the server.
by John Zukowski
<p>
A couple of recent Core Java Technologies Tech Tips were related to the Java Logging API, found in the <code>java.util.logging package</code>. You may have read the recent tip titled <a href="https://java.sun.com/mailers/techtips/corejava/2007/tt0807.html#1">Logging Localized Messages</a> or the 2002 tip titled <a href="https://java.sun.com/developer/JDCTechTips/2002/tt1022.html#1">Filtering Logged Messages</a>. The logging examples are always to the local file system or console. However, you can install any logging <code>Handler</code>, including the default <code>SocketHandler</code>, 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.
</p>
<p>
Here's what a typical logging program looks like. Basically, just get the <code>Logger</code> and log a message to it. You control the severity of the message and and the content of the message itself.
</p>
<p>
<pre>
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");
}
}
</pre>
</p>
<p>
By default, the messages go to whatever is configured in the <code>logging.properties</code> file found for the Java Runtime Environment (JRE). The <code>handlers</code> line of that file specifies which <code>Handler</code> to use for logging messages. That default is the <code>ConsoleHandler</code>:
</p>
<p>
<pre>
handlers= java.util.logging.ConsoleHandler
</pre>
</p>
<p>
But the file also includes a default <code>FileHandler</code> definition:
</p>
<p>
<pre>
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
</pre>
</p>
<p>
And changing the <code>handlers</code> line to the following will cause messages to go the <code>FileHandler</code> in addition to the <code>ConsoleHandler</code>.
</p>
<p>
<pre>
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
</pre>
</p>
<p>
The system comes with two additional system-defined <code>Handler</code> types. The <code>MemoryHandler</code> class represents an in-memory circular buffer and <code>SocketHandler</code> for logging messages to the network. The constructor <code>public SocketHandler(String host, int port)</code> then allows you to define where to send network log messages, or you can place the necessary settings in the <code>logging.properties</code> file and use the no-argument version of the constructor.
</p>
<p>
If you would prefer not to use the <code>logging.properties</code> file, you can add the <code>SocketHandler</code> to the <code>LogTest</code> program. Simply create the <code>Handler</code> and add it to the <code>Logger</code>
with the <code>addHandler()</code> call. Assuming that something is listening at the other end, you'll get messages sent using the default <code>XMLFormatter</code>.
</p>
<p>
<pre>
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");
}
}
</pre>
</p>
<p>
This sends messages to port 5000 on <code>localhost</code> (the user's computer). Of course, by default, nothing is listening on the other end. You must create a server.
</p>
<p>
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.
</p>
<p>
<pre>
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) {
}
}
}
}
}
}
</pre>
</p>
<p>
The program here comes from the <a href="https://java.sun.com/developer/JDCTechTips/2004/tt0914.html#2">SSL Servers</a> tip, but the author has modified that code by removing the SSL piece and using a regular <code>ServerSocketFactory</code>. Also, instead of echoing the message back, that message goes to the console.
</p>
<p>
Now, if you compile and run the <code>LogServer</code> in one window and run the <code>LogTest</code> program in another window, you'll notice the messages received by the <code>LogServer</code>. You can run <code>LogTest</code> multiple times to repeatedly send the same two messages. The server simply continues to accept messages.
</p>
<p>
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.
</p>
<p>
For more information on creating a server, see the <a href="https://java.sun.com/tutorial/networking/sockets/">All About Sockets</a> lesson of the Java SE Tutorial's Custom Networking trail.
</p>
<p>
********
</p>
<p>
<a href="https://java.sun.com/developer/community/askxprt/">Ask the Experts Transcript: NetBeans IDE 6.0</a>.
Is there an obfuscator function in NetBeans? How can I run/invoke JavaFx code from a NetBeans module?
What is the current status of PHP support in NetBeans? Get answers to these and a wide variety of other questions about
NetBeans in this transcript.
</p>
<p>
********
</p>
<p>
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 <a href="https://jdk6.dev.java.net/6uNea.html">Early Access versions</a> and provide us your <a href="https://jdk6.dev.java.net/6uNea.html#Feedback">feedback</a>.
For a list of features:
<a href="https://jdk6.dev.java.net/testing.html">https://jdk6.dev.java.net/testing.html</a>
You can find more information at:
<a href="https://jdk6.dev.java.net/ProjectHamburg.html">https://jdk6.dev.java.net/ProjectHamburg.html </a> <img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/GmHMq7PStHU" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/socket_logging
https://blogs.sun.com/CoreJavaTechTips/entry/writing_tostring_methods_tech_days
Writing toString Methods & Tech Days
dananourie
2007-10-08T12:54:19-07:00
2007-10-08T13:43:40-07:00
Learn the in's and out's of writing toString methods, and discover the Tech Days program.
by Glen McCluskey
<p>
One of the standard methods defined in <code>java.lang.Object</code> is
<code>toString</code>. 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 <code>toString</code>.
</p>
<p>
Let's first consider some sample code:
<pre>
class MyPoint {
private final int x, y;
public MyPoint(int x, int y) {
this.x = x;
<code> 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);
}
}
</pre>
<p>
The TSDemo1 program defines a class <code>MyPoint</code> to represent <code>X,Y</code>
points. It does not define a <code>toString</code> 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:
<pre>
MyPoint@111f71
MyPoint@111f71
MyPoint@111f71 testing
null testing
</pre>
<p>
You might wonder how it's possible to print an arbitrary class
object. The library methods such as <code>System.out.println</code> know
nothing about the <code>MyPoint</code> 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?
</p>
<p>
The answer is that println calls the
<code>java.io.PrintStream.print(Object)</code> method, which then calls the
<code>String.valueOf</code> method. The <code>String.valueOf</code> method is very simple:
</p>
<p>
<pre>
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
</pre>
</p>
<p>
When <code>println</code> is called with a <code>MyPoint</code> object reference, the
<code>String.valueOf</code> method converts the object to a string.
<code>String.valueOf</code> first checks to make sure that the reference is
not null. It then calls the <code>toString</code> method for the object. Since
the <code>MyPoint</code> class has no <code>toString</code> method, the default one in
<code>java.lang.Object</code> is used instead.
</p>
<p>
What does the default <code>toString</code> method actually return as a string
value? The format is illustrated in the second print statement
above. The name of the class, an "@", and the hex version of the
object's hashcode are concatenated into a string and returned. The
default hashCode method in <code>Object</code> is typically implemented by
converting the memory address of the object into an integer.
So your results might vary from those shown above.
</p>
<p>
The third and fourth parts of the TSDemo1 example illustrate
a related idea: when you use "+" to concatenate a string to an
object, <code>toString</code> 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 <code>javap</code> command as follows:
</p>
<p>
javap -c . TSDemo1
</p>
<p>
If you look at the bytecode, you'll notice that part of it
involves creating a <code>StringBuffer</code> object, and then using
<code>StringBuffer.append(Object)</code> to append the mp object to it.
<code>StringBuffer.append(Object)</code> is implemented very simply:
</p>
<p>
<pre>
public synchronized StringBuffer append(Object obj) {
return append(String.valueOf(obj));
}
</pre>
</p>
<p>
As mentioned earlier, <code>String.valueOf</code> calls <code>toString</code> on the object
to get its string value.
</p>
<p>
O.K., so much for invoking the default <code>toString</code> method. How do
you write your own <code>toString</code> methods? It's really very simple.
Here's an example:
</p>
<p>
<pre>
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);
}
}
</pre>
</p>
<p>
When you run the TSDemo2 program, the output is:
37 47
37
</p>
<p>
The <code>toString</code> 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 <code>toString</code> output. All you see is
a cryptic "37 47". The other problem is that the X,Y values in
<code>MyPoint</code> objects are private. There is no other way to get at them
except by picking apart the string returned from <code>toString</code>. The
second part of the TSDemo2 example shows the code required to
extract the <code>X</code> value from the string. Doing it this way is
error-prone and inefficient.
</p>
<p>
Here's another approach to writing a <code>toString</code> method, one that
clears up the problems in the previous example:
</p>
<p>
<pre>
class MyPoint {
private final int x, y;
public MyPoint(int x, int y) {
this.x = x;
this.y = y;
}
public String toString() {
return "X=" + x + " " + "Y=" + y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
public class TSDemo3 {
public static void main(String args[]) {
MyPoint mp = new MyPoint(37, 47);
// call MyPoint.toString()
System.out.println(mp);
// get X,Y values via accessor methods
int x = mp.getX();
int y = mp.getY();
System.out.println(x);
System.out.println(y);
}
}
The output is:
X=37 Y=47
37
47
</pre>
</p>
<p>
This example adds some descriptive text to the output format, and
defines a couple of accessor methods to get at the X,Y values. In
general, when you write a <code>toString</code> method, the format of the
string that is returned should cover all of the object contents.
Your <code>toString</code> 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 <code>toString</code> to build up the return value is not
necessarily the most efficient approach. You might want to use
<code>StringBuffer</code> instead.
</p>
<p>
Primitive types in the Java programming language, such as int,
also have <code>toString</code> methods, for example <code>Integer.toString(int)</code>.
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 <code>toString</code> method for arrays. The code looks like
this:
</p>
<p>
<pre>
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));
}
}
</pre>
</p>
<p>
The TSDemo4 program creates a <code>toString</code> method, and then passes
the <code>toString</code> method an arbitrary Object reference. If the
reference is null or does not refer to an array, the program
calls the <code>String.valueOf</code> method. Otherwise, the <code>Object</code> refers to
an array. In that case, TSDemo4 uses reflection to access the
array elements. <code>Array.getLength</code> and <code>Array.get</code> are the key methods
that operate on the array. After an element is retrieved, the
program calls <code>toString</code> recursively to obtain the string for the
element. Doing it this way ensures that multidimensional arrays
are handled properly.
</p>
<p>
The output of the TSDemo4 program is:
<pre>
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]
</pre>
Obviously, if you have a huge array, and you call <code>toString</code>, it
will use a lot of memory, and the resulting string might not be
particularly useful or readable by a human.
</p>
<p>
For more information about using <code>toString</code> methods, see
Section 2.6.2, Method Invocations, in "The Java(tm) Programming
Language Third Edition" by Arnold, Gosling, and Holmes
https://java.sun.com/docs/books/javaprog/thirdedition/. Also see
item 9, Always override toString, in "Effective Java Programming
Language Guide"
by Joshua Bloch (https://java.sun.com/docs/books/effective/).
</p>
<p>
**********
</p>
<p>
<a href="https://developers.sun.com/events/techdays/">Sun Tech Days</a>
</p>
<p>
The Sun Tech Days program educates developers on many technologies in a two-day format, and includes hands-on labs, university training, community programs and technical sessions. Attend an upcoming free session:
</p>
<p>
Taipei, Taiwan Oct. 19<br>
Shanghai, China Oct. 23-25<br>
Beijing, China Nov. 1-3<br>
Tokyo, Japan Nov. 6-8<br>
Frankfurt, Germany Dec. 3-5 <br>
</p>
<p>
See the <a href="https://developers.sun.com/events/techdays/">Sun Tech Days</a> website for more information about a Tech Days near you.
<img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/y4B2aW3omlg" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/writing_tostring_methods_tech_days
https://blogs.sun.com/CoreJavaTechTips/entry/the_preferences_api
The Preferences API
John O'Conner
2007-09-19T11:30:29-07:00
2007-09-19T12:17:42-07:00
There is more to the Preferences API than just getting and setting
user specific settings. There are system preferences, import and export
preferences, and event notifications associated with preferences. There
is even a way to provide your own custom location for storage of
preferences.
<p>The author of this tip is John Zukowski, president and principal
consultant of JZ Ventures, Inc.</p>
<p>
The Preferences API was first covered here shortly after it was introduced
with the 1.4 version of the standard platform: the July 15, 2003 article,
the <a href="https://java.sun.com/developer/JDCTechTips/2003/tt0715.html#1"> Preferences API</a>.
</p>
<p>
That article described how to get and set user specific preferences.
There is more to the Preferences API than just getting and setting
user specific settings. There are system preferences, import and export
preferences, and event notifications associated with preferences. There
is even a way to provide your own custom location for storage of
preferences. The first three options mentioned will be described here.
Creating a custom preferences factory will be left to a later tip.
</p>
<p><b>System Preferences</b></p>
<p>
The Preferences API provides for two separate sets of preferences. The
first set is for the individual user, allow multiple users on the same
machine to have different settings defined. These are called user
preferences. Each user who shares the same machine can have his or her
own unique set of values associated with a group of preferences. Something
like this could be like a user password or starting directory. You don't
want every person on the same machine to have the same password and home
directory. Well, I would hope you don't want that.
</p>
<p>
The other form of preferences is the system type. All users of a machine
share the same set of system preferences. For instance, the location
of an installed printer would typically be a system preference. You
wouldn't necessarily have a different set of printers installed for
different users. Everyone running on one machine would know about all
printers known by that machine.
</p>
<p>
Another example of a system preference would be the high score of a game.
There should only be one overall high score. That's what a system preference
would be used for. In the previous tip you saw how <code>userNodeForPackge()</code>
-- and subsequently <code>userRoot()</code> -- 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 <code>systemNodeForPackage()</code> -- or <code>systemRoot()</code> for the root.
Other than the method call to get the right preference node, the API usage is
identical.
</p>
<p>
The example is a simple game, using the game term loosely here. It picks
a random number from 0 to 99. If the number is higher than the previously
saved number, it updates the "high score." The example also shows the
current high score. The Preferences API usage is rather simple. The
example just gets the saved value with <code>getSavedHighScore()</code>, providing
a default of -1 if no high score had been saved yet, and
<code>updateHighScore(int value)</code> to store the new high score. The <code>HIGH_SCORE</code>
key is a constant shared by the new Preferences API accesses.
</p>
<pre>
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);
}
</pre>
<p>
Here's what the whole program looks like:
</p>
<pre>
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);
}
}
</pre>
<p>
And, here's what the screen looks like after a few runs. The 61 score is
not apt to be your high score, but it certainly could be.
</p>
<p>
<img src="https://blogs.sun.com/CoreJavaTechTips/resource/high.png" alt="high.png">
</p>
<p>
You can try running the application as different users to see that they all
share the same high score.
</p>
<p><b>Import and Export</b></p>
<p>
In the event that you wish to transfer preferences from one user to another
or from one system to another, you can export the preferences from that one
user/system, and then import them to the other side. When preferences are
exported, they are exported into an XML formatted document whose DTD is
specified by <code>https://java.sun.com/dtd/preferences.dtd</code>, though you don't
really need to know that. You can export either a whole subtree with
the <code>exportSubtree()</code> method or just a single node with the <code>exportNode()</code>
method. Both methods accept an <code>OutputStream</code> argument to specify where to
store things. The XML document will be UTF-8 character encoded. Importing
of the data then happens via the <code>importPreferences()</code> method, which takes
an <code>InputStream</code> argument. From an API perspective, there is no difference
in importing a system node/tree or a user node.
</p>
<p>
Adding a few lines of code to the previous example will export the newly
updated high score to the file high.xml. Much of the added code is
responsible for launching a new thread to save the file and for handling
exceptions. There are only three lines to export the single node:
</p>
<pre>
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();
</pre>
<p>
When exported, the file will look something like the following:</p>
<pre>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE preferences SYSTEM
"https://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>
</pre>
<p>
Notice the root element has a type attribute that says "<code>system</code>". This
states the type of node it is. The node also has a name attribute
valued at "<code><unnamed></code>". Since the <code>High</code> class was not placed in a package,
you get to work in the unnamed system node area. The entry attribute
provide the current high score value, 95 in the example here, though
your value could differ.
</p>
<p>
While we won't include any import code in the example here, the
way to import is just a static method call on Preferences, passing
in the appropriate input stream:
</p>
<pre>
FileInputStream fis = new FileInputStream("high.xml");
Preferences.importPreferences(fis);
fis.close();
</pre>
<p>
Since the XML file includes information about whether the preferences
are system or user type, the import call doesn't have to explicitly
include this bit of information. Besides the typical <code>IOExceptions</code> that
can happen, the import call will throw an <code>InvalidPreferencesFormatException</code>
if the file format is invalid. Exporting can also throw a
<code>BackingStoreException</code> if the data to export can't be read correctly
from the backing store.
</p>
<p>
<b>Event Notifications</b>
</p>
<p>
The original version of the <code>High</code> game updated the high score
preference, then explicitly made a call to update the label on the
screen. A better way to perform this action would be to add
a listener to the preferences node, then a value change can
automatically trigger the label to update its value. That way,
if the high score is ever updated from multiple places, you won't need
to remember to add code to update the label after saving the updated
value.
</p>
<p>
The two lines:
</p>
<pre>
updateHighScore(next);
updateHighScoreLabel(next);
</pre>
<p>
can become one with the addition of the right listeners.</p>
<pre>
updateHighScore(next);
</pre>
<p>
There is a <code>PreferenceChangeListener</code> and its associated <code>PreferenceChangeEvent</code>
for just such a task. The listener will be notified for all changes to the
associated node, so you need to check for which key-value pair was modified,
as shown here.
</p>
<pre>
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);
</pre>
<p>
The <code>PreferenceChangeEvent</code> has three important properties: the key, new
new value, and the node itself. The new value doesn't have all the convenience
methods of Preferences though. For example, you can't retrieve the
value as an int. Instead you must manually convert the value
yourself. Here's what the modified <code>High</code> class looks like:
</p>
<pre>
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();
}
}
</pre>
<p>
In addition to the <code>PreferenceChangeListener/Event</code> class pair, there is a
<code>NodeChangeListener</code> and <code>NodeChangeEvent</code> combo for notification of
preference changes. However, these are for notification nodes
additions and removals, not changing values of specific nodes. Of course, if you
are writing something like a Preferences viewer, clearly you'd want to know
if/when nodes appear and disappear so these classes may be of interest, too.
</p>
<p>
The whole Preferences API can be quite handy to store data beyond the life
of your application without having to rely on a database system. For more information
on the API, see the article <a href="https://java.sun.com/developer/technicalArticles/releases/preferences/">Sir, What is Your Preference?</a>
</p>
<img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/8lZehm-dPYs" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/the_preferences_api
https://blogs.sun.com/CoreJavaTechTips/entry/using_enhanced_for_loops_with
Using Enhanced For-Loops with Your Classes
John O'Conner
2007-09-18T16:20:56-07:00
2007-09-18T16:20:56-07:00
The enhanced for-loop is a popular feature introduced with the Java SE platform in version 5.0. Its simple structure allows one to simplify code by presenting for-loops that visit each element of an array/collection without explicitly expressing how one goes from element to element.
<p>The author of this tip is John Zukowski, president and principal
consultant of JZ Ventures, Inc.</p>
<p>
The enhanced for-loop is a popular feature introduced with the Java SE
platform in version 5.0. Its simple structure allows one to
simplify code by presenting for-loops that visit each element of
an array/collection without explicitly expressing how one goes from
element to element.
</p>
<p>
Because the old style of coding didn't become
invalid with the new for-loop syntax, you don't have to use an
enhanced for-loop when visiting each element of an
array/collection. However, with the new style, one's code would
typically change from something like the following:
</p>
<pre>
for (int i=0; i < array.length; i++) {
System.out.println("Element: " + array[i]);
}
</pre>
<p>to the newer form</p>
<pre>for (String element : array) {
System.out.println("Element: " + element);
}
</pre>
<p>
Assuming "array" is defined to be an array of <code>String</code> objects,
each element is assigned to the element variable as it loops
through the array. These basics of the enhanced for-loop were covered
in an earlier Tech Tip: <a href="https://java.sun.com/developer/JDCTechTips/2005/tt0505.html#2">The Enhanced For Loop</a>, from May 5, 2005.
<p>
If you have a class called <code>Colony</code> which contains a group of <code>Penguin</code>
objects, without doing anything extra to get the enhanced for-loop to
work, one way you would loop through each penguin element would be to
return an <code>Iterator</code> and iterate through the colony. Unfortunately, the
enhanced for-loop does not work with <code>Iterator</code>, so the following won't even compile:
</p>
<pre>
// Does not compile
import java.util.*;
public class BadColony {
static class Penguin {
String name;
Penguin(String name) {
this.name = name;
}
public String toString() {
return "Penguin{" + name + "}";
}
}
Set<Penguin> set = new HashSet<Penguin>();
public void addPenguin(Penguin p) {
set.add(p);
}
public Iterator<Penguin> getPenguins() {
return set.iterator();
}
public static void main(String args[]) {
Colony colony = new Colony();
Penguin opus = new Penguin("Opus");
Penguin chilly = new Penguin("Chilly Willy");
Penguin mumble = new Penguin("Mumble");
Penguin emperor = new Penguin("Emperor");
colony.addPenguin(opus);
colony.addPenguin(chilly);
colony.addPenguin(mumble);
colony.addPenguin(emperor);
Iterator<Penguin> it = colony.getPenguins();
// The bad line of code:
for (Penguin p : it) {
System.out.println(p);
}
}
}
</pre>
<p>
You cannot just pass an <code>Iterator</code> into the enhanced for-loop. The 2nd
line of the following will generate a compilation error:
</p>
<pre>
Iterator<Penguin> it = colony.getPenguins();
for (Penguin p : it) {
</pre>
<p>
The error:</p>
<pre>
BadColony.java:36: foreach not applicable to expression type
for (Penguin p : it) {
^
1 error
</pre>
<p>
In order to be able to use your class with an enhanced for-loop, it
does need an <code>Iterator</code>, but that <code>Iterator</code> must be provided via the
<code>Iterable</code> interface:
</p>
<pre>
public interface java.lang.Iterable {
public java.util.Iterator iterator();
}
</pre>
<p>
Actually, to be more correct, you can use a generic <code>T</code>,
allowing the enhanced for-loop to avoid casting, returning the
designated generic type, instead of just a plain old <code>Object</code>.
</p>
<pre>
public interface java.lang.Iterable<T> {
public java.util.Iterator<T> iterator();
}
</pre>
<p>
It is this <code>Iterable</code> object which is then provided to the enhanced for
loop. By making the <code>Colony</code> class implement <code>Iterable</code>, and having
its new <code>iterator()</code> method return the <code>Iterator</code> that <code>getPenguins()</code>
provides, you'll be able to loop through the penguins in the
colony via an enhanced for-loop.
</p>
<p>By adding the proper implements clause:</p>
<pre>
public class Colony implements Iterable<Colony.Penguin> {
</pre>
<p>You then get your enhanced for-loop for the colony:</p>
<pre>
for (Penguin p : colony) {
</pre>
<p>
Here's the updated <code>Colony</code> class with the corrected code:</p>
<pre>
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);
}
}
}
</pre>
<p>
Running the code produces the following output:</p>
<pre>
> java Colony
Penguin{Chilly Willy}
Penguin{Mumble}
Penguin{Opus}
Penguin{Emperor}
</pre>
<p>
Keep in mind that the individual penguins are internally kept in a Set
type collection so the returned order doesn't necessarily match the
insertion order, which in this case it doesn't.
</p>
<p>
Remember to genericize the implements clause for the class "<code>implements
Iterable<T></code>" and not just say "<code>implements Iterable</code>". With the latter,
the enhanced for-loop will only return an <code>Object</code> for each element.
</p>
<p>
For more information on the enhanced for-loop, please see the
<a href="https://java.sun.com/j2se/1.5.0/docs/guide/language/foreach.html"> Java Programming Language guide from JDK 1.5</a>
</p>
<img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/EJVVR_evuRg" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/using_enhanced_for_loops_with
https://blogs.sun.com/CoreJavaTechTips/entry/the_attach_api
The Attach API
John O'Conner
2007-08-23T10:03:59-07:00
2007-08-27T11:19:13-07:00
The Attach API allows you to attach to a target virtual machine (VM). By attaching to another VM, you can monitor what's going on and potentially detect problems before they happen.
<p>by John Zukowski</p>
<p>
When working with the Java platform, you typically program with the
standard <code>java.*</code> and <code>javax.*</code> 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 <code>tools.jar</code> file, found in the lib directory under your JDK installation directory. You'll find
support for extending the javadoc tool and an API called the Attach
API.</p>
<p>As the name may imply, the Attach API allows you to attach to a target
virtual machine (VM). By attaching to another VM, you can monitor
what's going on and potentially detect problems before they
happen. The Attach API classes are found in the <code>com.sun.tools.attach</code>
and <code>com.sun.tools.attach.spi</code> packages, though you'll typically never
directly use the <code>com.sun.tools.attach.spi</code> classes. </p>
<p>Even including the one class in the .spi package that you won't use,
the whole API includes a total of seven classes. Of that, three are
exception classes and one a permission. That doesn't leave much to
learn about, only <code>VirtualMachine</code> and its associated
<code>VirtualMachineDescriptor</code> class.</p>
<p>The <code>VirtualMachine</code> class represents a specific Java virtual machine
(JVM) instance. You connect to a JVM by providing the <code>VirtualMachine</code>
class with the process id, and then you load a management agent to do
your customized behavior:</p>
<pre>
VirtualMachine vm = VirtualMachine.attach (processid);
String agent = ...
vm.loadAgent(agent);
</pre>
<p>
The other manner of acquiring a <code>VirtualMachine</code> is to ask for the list
of virtual machines known to the system, and then pick the specific
one you are interested in, typically by name:
</p>
<pre>
String name = ...
List<VirtualMachineDescriptor> vms = VirtualMachine.list();
for (VirtualMachineDescriptor vmd: vms) {
if (vmd.displayName().equals(name)) {
VirtualMachine vm = VirtualMachine.attach(vmd.id());
String agent = ...
vm.loadAgent(agent);
// ...
}
}
</pre>
<p>
Before looking into what you can do with the agent, there are
two other things you'll need to consider. First, the <code>loadAgent</code> 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:
</p>
<pre>
vm.loadAgent (agent, "a=1,b=2,c=3");
</pre>
<p>The agent would then split them out with code similar to the
following, assuming the arguments are passed into the agent's args
variable.
</p>
<pre>
String options[] = args.split(",");
for (String option: options)
System.out.println(option);
}
</pre>
<p>The second thing to mention is how to detach the current virtual machine
from the target virtual machine. That's done via the <code>detach</code>
method. After you load the agent with <code>loadAgent</code>, you should call
the <code>detach</code> method.
</p>
<p>A JMX agent exists in the <code>management-agent.jar</code> file that comes with
the JDK. Found in the same directory as <code>tools.jar</code>, the JMX management
agent allows you to start the remote JMX agent's MBean Server and get
an <code>MBeanServerConnection</code> to that server. And, with that, you can list
things like threads in the remote virtual machine.
</p>
<p>The following program does just that. First, it attaches to the
identified virtual machine. It then looks for a running remote JMX
server and starts one if not already started. The <code>management-agent.jar</code>
file is specified by finding the java.home of the remote virtual
machine, not necessarily the local one. Once connected, the
<code>MBeanServerConnection</code> is acquired, from which you query the
<code>ManagementFactory</code> for things like threads or <code>ThreadMXBean</code> as the case may be. Lastly, a list of thread names and their states are displayed.
</p>
<pre>
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());
}
}
}
}
</pre>
<p>To compile this program, you need to make sure you include <code>tools.jar</code>
in your <code>CLASSPATH</code>. Assuming <code>JAVA_HOME</code> is set to the Java SE 6 installation directory, of which the Attach API is a part, the following line will compile your program:
</p>
<pre>
> javac -cp %JAVA_HOME%/lib/tools.jar Threads.java
</pre>
<p>
From here, you could run the program, but there is nothing to attach
to. So, here's a simple Swing program that displays a frame. Nothing
fancy, just something to list some thread names you might recognize.
</p>
<pre>
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);
}
}
</pre>
<p>
Run the <code>MyFrame</code> program in one window and be prepared to run the
Threads program in another. Make sure they share the same runtime
location so the system can connect to the remote virtual machine.
</p>
<p>
Launch <code>MyFrame</code> with the typical startup command:
</p>
<pre>
> java MyFrame
</pre>
<p>Then, you need to find out the process of the running application.
That's where the <code>jps</code> command comes in handy. It will list the process
ids for all virtual machines started for the JDK installation directory
you are using. Your output, specifically the process ids, will
probably be different:
</p>
<pre>
> jps
5156 Jps
4276 MyFrame
</pre>
<p>Since the <code>jps</code> command is itself a Java program, it shows up in the
list, too. Here, 4276 is what is needed to pass into the <code>Threads</code>
program. Your id will most likely be different. Running <code>Threads</code> then
dumps the list of running threads:
</p>
<pre>
> java -cp %JAVA_HOME%/lib/tools.jar;. Threads 4276
JMX server connection timeout 18 / TIMED_WAITING
RMI Scheduler(0) / TIMED_WAITING
RMI TCP Connection(1)-192.168.0.101 / RUNNABLE
RMI TCP Accept-0 / RUNNABLE
DestroyJavaVM / RUNNABLE
AWT-EventQueue-0 / WAITING
AWT-Windows / RUNNABLE
AWT-Shutdown / WAITING
Java2D Disposer / WAITING
Attach Listener / RUNNABLE
Signal Dispatcher / RUNNABLE
Finalizer / WAITING
Reference Handler / WAITING
</pre>
<p>
You can use the JMX management agent to do much more than just list
threads. For instance, you can call the <code>findDeadlockedThreads</code> method
of <code>ThreadMXBean</code> to find deadlocked threads. </p>
<p>Creating your own agent is actually rather simple. Similar to how
applications require a <code>main</code> method, agents have an <code>agentMain</code> method. This isn't a part of any interface. The system just knows to look for one with the right argument set as parameters.
</p>
<pre>
import java.lang.instrument.*;
public class SecretAgent {
public static void agentmain(String agentArgs,
Instrumentation instrumentation) {
// ...
}
}
</pre>
<p>
To use the agent, you must then package the compiled class file into
a JAR file and specify the <code>Agent-Class</code> in the manifest:
</p>
<pre>
Agent-Class: SecretAgent
</pre>
<p>The <code>main</code> method of your program then becomes a little shorter than
the original <code>Threads</code> program since you don't have to connect to the
remote JMX connector. Just be sure to change the JAR file reference
there to use your newly packaged agent. Then, when you run the
<code>SecretAgent</code> program, it will run the <code>agentmain</code> method right at startup, even before the application's <code>main</code> method is called. Like <code>Applet</code>, there are other magically named methods for doing things that are not part of any interface.
</p>
<p>
Use the <code>Threads</code> 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.
</p>
<p>
See the <a href="https://java.sun.com/javase/6/docs/jdk/api/attach/spec/index.html">Attach API documentation</a> for more information. Consider also reading up on the Java Virtual Machine Tool Interface (JVM TI). It requires the use of the Attach API.
</p>
<img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/mquB1Vyn3xQ" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/the_attach_api
https://blogs.sun.com/CoreJavaTechTips/entry/logging_localized_message
Logging Localized Message
John O'Conner
2007-08-20T09:27:58-07:00
2007-08-20T09:29:10-07:00
While the Logging 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.
<p>by John Zukowski</p>
<p>The Logging API was last covered in the October 22, 2002 tip <a href="https://java.sun.com/developer/JDCTechTips/2002/tt1022.html#1">Filtering
Logged Messages</a>. 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:
</p>
<pre>
logger.log(level, message);
</pre>
<p>First, the message argument doesn't have to be a hard-coded
string. Second, the message can take arguments. Internally, relying on
<code>MessageFormat</code>, the logger will take any arguments passed in after the
log message string and use them to fill in any blanks in the
message. The first argument after the message string argument to <code>log()</code>
will have index 0 and is represented by the string <code>{0}</code>. The next
argument is <code>{1}</code>, then <code>{2}</code>, and so on. You can also provide additional formatting details, like <code>{1, time}</code> would show only the time portion of a Date argument.
</p>
<p>
To demonstrate, here's how one formatted log message call might look:
</p>
<pre>
String filename = ...;
String message = "Unable to delete {0} from system.";
logger.log(level, message, filename);
</pre>
<p>
Now, for filename GWBASIC.EXE, the message displayed would be
"Unable to delete GWBASIC.EXE from system."
</p>
<p>
On its own, this isn't too fancy a deal. Where this extra bit of
formatting really comes in handy is when you treat the message
argument as a lookup key into a resource bundle. When you fetch a
Logger, you can either pass in only the logger name, or both the name
and a resource bundle name.
</p>
<p>
By combining messages fetched from a resource bundle with local
arguments, you get all the benefits of localized, parameterized
messages, not just in your programs, but in your log messages as
well.
</p>
<p>
To treat the message argument as a lookup key to a resource bundle,
the manner of fetching the Logger needs to change slightly. If you
want to use resource bundles, avoid creating a <code>Logger</code> object like
this:
</p>
<pre>
private static Logger logger =
Logger.getLogger("com.example");
</pre>
<p>
Instead, add an optional second argument to the <code>getLogger()</code> call. The
argument is the resource bundle that contains localized messages. Then,
when you make a call to log a message, the "message" argument
is the lookup key into the resource bundle, whose name is passed to the
<code>getLogger()</code> call.
</p>
<pre>
private static final String BUNDLE_NAME = "com.example.words";
private static Logger logger =
Logger.getLogger("com.example", BUNDLE_NAME);
</pre>
<p>
The <code>BUNDLE_NAME</code> resource bundle must include the appropriate message for
the key provided to the logging call:
</p>
<pre>
logger.log(level, "messageKey");
</pre>
<p>
If "messageKey" is a valid key in the resource bundle, you
now have the associated message text logged to the Logging
API. That message text can include those {0}-like arguments to
get your message arguments passed into the logger.
</p>
<pre>
String filename = ...;
logger.log(level, "messageKey", filename);
</pre>
<p>While you don't see the <code>{0}</code> formatting string in "messageKey", since
its value was acquired from the resource bundle, you could get your
output formatted with <code>MessageFormat</code> again.
</p>
<p>
Let us put all the pieces together. We'll create a small application
that shows localized logging.
</p>
<p>
To keep things simple, these resource bundles will be
<code>PropertyResourceBundle</code> objects instead of <code>ListResourceBundle</code> objects.
</p>
<p>Create file <code>messages.properties</code> in the local directory to include the
messages for the default locale, assumed to be US English.
</p>
<pre>
Message1=Hello, World
Message2=Hello, {0}
</pre>
<p>The second language will be Spanish. Place the following in the file
<code>messages_ES.properties</code>:
</p>
<pre>
Message1=Hola, mundo
Message2=Hola, {0}
</pre>
<p>
Now, we have to create the application. Notice that the
<code>getAnonymousLogger()</code> method also includes a second version that
accepts a resource bundle name. If you want to use a named logger,
feel free to pass in the name and use <code>getLogger()</code> instead.
</p>
<pre>
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");
}
}
</pre>
<p>
The LocalLog program's log messages are "Message1" and
"Message2". When run with the default locale, you'll get messages
similar to the following:
</p>
<pre>
> java LocalLog
Aug 4, 2007 12:00:35 PM LocalLog main
SEVERE: Hello, World
Aug 4, 2007 12:00:35 PM LocalLog main
SEVERE: Hello, John
</pre>
<p>
To run the program with a different locale, set the <code>user.language</code> system
property on the command line:
</p>
<pre>
>java -Duser.language=ES LocalLog
ago 4, 2007 12:01:18 p.m. LocalLog main
GRAVE: Hola, mundo
ago 4, 2007 12:01:18 p.m. LocalLog main
GRAVE: Hola, John
</pre>
<p>
Notice that your log message contains the localized
resource bundle message, and the logger also uses localized
date strings and localized severity level text.
</p>
<p>Keep this feature in mind to create localized log messages. You can
use resource bundles to provide both localized log messages and user
interface text. Those reading log files should be able to see translated
text, too.
</p>
<p>
For additional information on resource bundles, see the <a href="https://java.sun.com/developer/JDCTechTips/2004/tt1214.html#1">Resource Bundle
Loading tip</a> and the <a href="https://java.sun.com/docs/books/tutorial/i18n/resbundle/index.html">Isolating Locale-Specific Data lesson</a> in The Java Tutorial.
</p>
<img src="https://feeds.feedburner.com/~r/corejavatechtips/~4/AySIQa-pbPM" height="1" width="1"/>
https://blogs.sun.com/CoreJavaTechTips/entry/logging_localized_message