Explore Java SE 6 Features

Contributed and maintained by


This hands-on lab takes you through the key features new in JavaSE 6, the current major release of Java SE platform. Java SE 6 features and enhancements to the platform are summarized in the Umbrella JSR(JSR 270)
The key features includes:

Yet, due to the limited time, the lab will focus on some of the features listed above. In this lab, you will also learn how to use Netbeans as your Java development environment.

Expected duration: 120 minutes

Prerequisites

This hands-on lab assumes you have some basic knowledge of, or programming experience with, the following technologies.

Software Needed

Before you begin, you need to install the following software on your computer. 


OS platforms you can use


Resources


Where to send questions or feedback's on this lab


If you have any questions or feedback on this lab, please send them to the following email alias - please be advised that this alias is set up for addressing issues/questions directly related to this lab not for answering general Java SE 6 questions.

Lab Exercises


You will need an internet connection for Exercise 0 in which you will do further installation and configuration.  From Exercise 1, you can proceed without an internet connection.

Exercise 0: Install and Configure the Lab Environment

In this exercise, we are going to use JDK 6 and Netbeans as our Java development environment. You are also going to install JavaScript plug-ins for Netbeans.  Make sure you have downloaded and installed the software mentioned above before you start.

    1. Install JDK 6
    2. Install Netbeans 5.5
    3. Setting the PATH variable
    4. Install addtional NetBeans module

(0.1) Install JDK 6


In this step, you are going to install JDK 6 on Solaris x86 system.

1. Open a command line console. Move jdk-6-solaris-i586.sh to the directory where you want to install JDK.
2. Type the name of JDK installation file to install, you may need to chmod of it.

$ chmod +x jdk-6-solaris-i586.sh
$ ./jdk-6-solaris-i586.sh

3. You will be show the software license. Read through, hit <space> to scroll. Type 'yes' to install or 'no' to exit.

. . . . . .
For inquiries please contact: Sun Microsystems, Inc., 4150
Network Circle, Santa  Clara, California 95054, U.S.A.

Do you agree to the above license terms? [yes or no]


4. The JDK will create a directory call jdk1.6.0 in your current directory and installs the the JDK in there.

(0.2) Install Netbeans 5.5


1. Open a command line console, type in the name of the netbeans installation file, again, you may need to chmod it. In the example below, the name of the netbeans file is 'netbeans-5_5-solaris-x86.bin'

$ chmod +x netbeans-5_5-solaris-x86.bin
$ ./netbeans-5_5-solaris-x86.bin


2. You will see the NetBeans welcome screen. Click 'Next >to proceed.


Figure-0.20: Netbeans 5.5 welcome screen

3. Next comes the license screen. Click on 'I accept the terms in the license agreement' if you agree with the license. Click 'Next >' to proceed.


Figure-0.21: Netbeans 5.5 license information

4. Select the installation directory. Use the 'Browse' button to browse to the location where you wish to install NetBeans. Click 'Next >' to proceed


Figure-0.22: Choose Netbeans 5.5 installation directory

5. Now select the JDK you wish to use NetBeans with. There may be more than one version available as shown below. If your JDK is not listed in the 'List of suitable JDK' window, use the 'Browse' button to browse to the <JDK_6_Installation> directory of the JDK. Click 'Next >' to proceed


Figure-0.23: Select JDK Home Directory

6. A confirmation screen will appear with the location of where NetBeans 5.0 will be installed and the total disk space it will occupy. Click 'Next >' to proceed to the next screen.


Figure-0.24: Confirm Screen of installation information

7. NetBeans will now be installed


Figure-0.25: Netbeans installing screen

8. You have successfully installed NetBeans 5.0. Click on 'Finish' to dismiss the screen


Figure-0.26: Netbeans installation finish screen

(0.3) Setting PATH Variable


Add the following to your PATH variable
<Netbeans_Installation>/bin
<JDK_6_Installation>/bin

1. For Solaris/Linux system,  type the following. (assume you are using bash shell)

$ export PATH=<Netbeans_Installation>/bin:<JDK_6_Installation>/bin:$PATH

Replace <Netbeans_Installation> and <JDK_6_Installation> with the respective directories.

2. For Windows users, type the following

> set PATH=<Netbeans_Installation>/bin;<JDK_6_Installation>/bin;%PATH%

Replace <Netbeans_Installation> and <JDK_6_Installation> with the respective directories.

(0.4) Install addtional Netbeans module


In the this lab, we are going to use the management plugin and javascript editor plugin of Netbeans, here are the steps to follow in order to install it.

1. Select Netbeans menu item 'Tools'->'Update Center', you'll get the update center wizard, select 'Check the Web for Available Updates and New Modules', and select all the update servers, click 'Next>'. If you connect to internet through a proxy, click 'Proxy Configuration...' to set the proxy.


Figure-0.40: Netbeans Update Center Wizard

2. Once connected to the update servers, Netbeans lists all the available plugins/updates that haven't been installed on your system yet. Select 'JMX', click 'Add>' button, then click 'Next>

           
Figure-0.41: Select plugins you want to install

3. After accept the agreement shown in the next screen, Netbeans starts to install the plugin you just selected. When you're done, click 'Next>'


Figure-0.42: Installing plugin from update center

4. Netbeans gives you a report on what plugins have just been installed. Click 'Finish', Netbeans will automatically update itself to apply the new installed plugins.


Figure-0.43: Report of plugins which have just been installed

Now you should have the menu item 'Management' on your menu bar. Now let's install the JavaScript Editor

5. This time, we install JavaScript Editor plugin from a module file of netbeans plugin(.nbm) on the local disk. Select menu item 'Tools'->'Update Center', select checkbox 'Install Manually Downloaded Modules', click 'Next>'


Figure-0.44: Install netbeans plugin from .nbm file

6. Add 'com-liguorien-jseditor.nbm' to 'Modules to Install', the .nbm file is located in <Unzip_Labfile_Directory>, click 'Next>'


Figure-0.45: Select the installation file

7. The rest steps are exactly the same as step 2 to step 4


Exercise 1: Scripting Language Support

One of the often requested feature on the Java platform is integration with the various scripts languages. Script writers benefit from the vast Java libraries. They can now develop powerful scripts quickly by making use of these libaries. Java novices can use the script environment to learn about Java APIs and test interactions between classes. This benefit is mutual. Java applications can be made more flexible and extensible by allowing their behaviour to be modified or functions to be extended by scripts.

There have been may projects on the Internet to script java. Some of the notable ones include:

Java SE 6 now provides a scripting framework. What the framework does is that it allows Java to integrated with any scripting engine. This integration is bi-directional. What this means is that not only can you access Java classes from your favourite scripting environment, you can invoke and export Java objects into scripts from within a Java program.

In this exercise, we will look at how you can access Java classes from scripts, how you can access scripts from Java code. Also we will see the usage of jrunscript script interpreter.

  1. Using jrunscript
  2. Invoking scripts from Java applications
  3. Integrating scripts into your Java application

(1.1) Using jrunscript

jrunscript is a script language independent shell. jrunscript will default to JavaScript. You can specify a different script through the '-l' option.

1. Open a terminal, set PATH variable as here

2. Change to <Unzip_Labfile_Directory>/exerices/ex1/interactive_shell directory

3. Type 'jrunscript'(without the quotes) to enter into the interative shell. You will see the following:

$ jrunscript
js>

4. jrunscript initializes the script engine with built-in functionsThese include simple file system utility functions, process utilities (exec, exit etc.), a few XML utilities and some simple IO functions. See here for a complete list. Try a few of these commands by following the session below:

js> date
sun.org.mozilla.javascript.internal.InterpretedFunction@1787038
js> date()
January 15, 2007 3:34:22 PM CST
js> ls()
js> cat("test.txt")
hello world
js>

5. Creating a Java object. We will now create a java.io.File object. Type in the following:

js> var file = new File("test.txt")
js>

6. You can access static and non-static members (if any) from the object itself. Type in the following:

js> File.separator
/
js>

7. We can also access all public method from a Java object. Try the following and also any other methods in File class.

js> file.getName()
test.txt
js> file.canRead()
true
js> file.getAbsolutePath()
/export/home/joey/HOL/javase6/exercises/ex1/interactive_shell/test.txt
js>

8. JavaBean properties can be access by dropping the the 'get' or 'is' from the method name. For example, getAbsolutePath() will become absolutePath and isDirectory() becomes directory. Try the following and also any other methods in File class.

js> file.directory
false
js> file.absolutePath
/home/HOL/javase6/exercises/ex1/interactive_shell/test.txt
js>

9. You can examine a method's singnature and overloads by entering the name of the method. In the following example, we enter listFiles to list out all the overloaded listFiles methods:

js> file.listFiles
java.io.File[] listFiles(java.io.FileFilter)
java.io.File[] listFiles()
java.io.File[] listFiles(java.io.FilenameFilter)

js>

10. Another useful feature is the ability to print out all methods from a Java object using the for loop. In JavaScript we will use the for/in loop. Type in the following:

js> for (method in file) { println(method); }
exists
usableSpace
parentFile
mkdir
totalSpace
setExecutable
toString
canExecute
wait
setReadable
setReadOnly
getCanonicalPath
. . . . . .

11. Create a JFrame object from javax.swing package as shown

js> var frame = new JFrame("my frame")
script error: sun.org.mozilla.javascript.internal.EcmaError: ReferenceError: "JFrame" is not defined. (<STDIN>#1) in <STDIN> at line number 1
js>

We have to import the javax.swing package before using. However, importing packages in JavaScript (or Rhino) is slightly different from Java. To import any package, you need to preceed the package's name with the JavaScript variable call Packages. Rhino also provides a JavaScript funtion call importPackage(). importPackage() is similar to the import statement in Java. So if we wish to import javax.swing, we would have to type the following:

js> importPackage(Packages.javax.swing)
js> var frame = new JFrame("my frame")
js> var button = new JButton("Press me")
js> var panel = new JPanel()

js> panel.add(button)
javax.swing.JButton[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@1fee6fc,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=Press me,defaultCapable=true]
js> frame.add(panel, "North")
js> frame.pack()
js> frame.setVisible(true)
js>

At this a JFrame similar to the one shown below will be displayed. The JFrame is not shown untill the setVisible(true) method is called. This way of using JavaScript is very useful in debugging Java code.


Figure-1.10: JFrame created by JavaScript through jrunscript

Note that the default imported Java packages java.io, java.net and java.lang are NOT imported by default in JavaScript.

12. Many of the Java APIs and functionalities like event listeners work with interfaces and threads. We can implement Java interfaces using JavaScript. To implement a Java interface, we do the following:

Here is an example of creating a thread. Type in the following:

js> importPackage(Packages.java.lang)
js> var runit = { run: function() { println("Running... "); } }
js> var run = Runnable(runit)
js> var thread = new Thread(run)
js> thread.start()
js> Running...

Try it yourself Add an ActionListener to the variable button so that  when presed, the listener should close the JFrame. You can use the JFrame and JButton object created from the previous example.

13. The JavaScript environment that comes with Java SE 6 comes with two additional JavaScript objects. They are jlist and jmap. These are convenience holder objects for  java.util.List and java.util.Map. Below are examples of how you would use them. Type in the following:

js> var props = jmap(System.properties)
js> println(props["java.class.path"])
/usr/jdk/instances/jdk1.6.0/lib/tools.jar:/usr/jdk/instances/jdk1.6.0/classes
js> importPackage(Packages.java.util)
js> var arrayList = new ArrayList(3)
js> arrayList.add("Java")
true
js> arrayList.add("SQL")
true
js> var list = jlist(arrayList)
js> println(list[0])
Java
js> println("length = " + list.length)
length = 2
js> println(list[2])
undefined
js>

            
14. Type CTRL+D or exit(0) to exit from jrunscript


(1.2) Invoking JavaScripts from Java applications

The scripting API in Java SE 6 provides classes for Java programs to load and execute scripts. Developers can pass Java objects from the Java program into these scrpts; the scripts can then manipulate these Java objects. This is a very useful feature. Imagine we have a word processor that is written in Java. Using the scripting features in Mustang, we can now build a framework into our word processors so that new meun items can be add with scripts. One good example fo this is StarOffice/OpenOffice.

The javax.script package contains all the scripting classes. These classes provides the following functionalities:

Now we will look at how to invoke a script programatically

1. Let's create a project in Netbeans first. Launch Netbeans, select menu 'File'->'New Project...', you'll get the following frame:


Figure-1.20: Netbeans New Project frame

2. Select 'General' in 'Categories', and 'Java Application' in 'Projects', click 'Next>'

3. In the 'Name and Location' step, specify Project Name as 'ScriptSample', select 'Set as Main Project' and 'Create Main Class' checkboxes, accept the default configurations. Click 'Finish'


Figure-1.21: Netbeans Name and Location frame for creating ScriptSample project

4. Netbeans has created a new project named 'ScriptSample' and the class named 'ScriptSample.Main' for you. In the 'projects' tag on the left side, extend project 'ScriptSample'->'Source Packages'->'ScriptSample', then click 'Main.java', you will have the source code of Main.java opened in the editor frame as below.


Figure-1.22: Source code of Main.java in ScriptSample project

5. Now let's try to launch JavaScripts within Java code. First we are going to create an instance of ScriptEngineManager. add the following code to the main() method

public static void main(String[] args) {
    ScriptEngineManager manager = new ScriptEngineManager();
}

6. Lookup an instance of a script engine. JavaScript is the default supported script engine. You could get a specific script engine by either file extension, mime type or name.

public static void main(String[] args) {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("js");
}

7.  Use various eval() overloads, you can launch javascript within the java code. add the following code to the main() method.

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
try {
    engine.eval(new FileReader("<Unzip_Labfile_Directory>/exercises/ex1/myscript.js"));
} catch (FileNotFoundException ex) {
    ex.printStackTrace();
} catch (ScriptException ex) {
    ex.printStackTrace();
}  

Replace <Unzip_Labfile_Directory> with the actually directory you unzip the lab file. Here we launch a very simple javascript myscript.js as below

var now
println(now)

8. Launch ScriptSample.Main by either selecting menu item 'Run'->'Run Main Project' or clicking 'Run Main Project' button on the toolbar. You'll get the follow output in the output window at the bottom.


Figure-1.23: Output for evaluating myscript.js

You'll see the output is 'undefined', since in myscript.js, we just print out the now variable which is undefined yet.

9. Expose Java objects into the scripting script. In the following code snippet, we bind the name 'now' to a java.util.Date object. The script will be able to access the Date object as variable 'now' in scripts. Add the following code in main() method

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
try {
    SimpleBindings bindings = new SimpleBindings();
    //Create a Date object
    Date date = new Date();
    //Bind date to now
    bindings.put("now", date);
    engine.eval(new FileReader("/export/home/joey/HOL/javase6/exercises/ex1/myscript.js"));
} catch (FileNotFoundException ex) {

10. Create a binding that is automatically visible to every script executed by the engine.

SimpleBindings bindings = new SimpleBindings();
//Create a Date object
Date date = new Date();
//Bind date to now
bindings.put("now", date);
engine.put("now", new Date());
//"now" will be visible when myscript.js is executed
engine.eval(new FileReader("/export/home/joey/HOL/javase6/exercises/ex1/myscript.js"));

11. Launch ScriptSample.Main by either selecting menu item 'Run'->'Run Main Project' or clicking 'Run Main Project' button on the toolbar. You'll get the follow output in the output window at the bottom.


Figure-1.24: Output of ScriptSample

Now, you'll see that altought we didn't change any code of myscript, the Date instance has been bound to the 'now' variable in the script.
       

(1.3) Integrating scripts into your Java application


1. Open script_integration project in Netbeans. script_integration is located under <Unzip_Labfile_Directory>/exercises/ex1.
//TODO Create an instance of script engine manager
ScriptEngineManager manager = new ScriptEngineManager();
//TODO Get an instance of the JavaScript script engine               
ScriptEngine engine = manager.getEngineByName("js");

//TODO Executes the script
engine.eval(new FileReader(fn));




              2. Now we will create a JFrame with a single JButton. We will then install an event handler for the button in JavaScript.
//TODO Create a script binding object
SimpleBindings bindings = new SimpleBindings();


//TODO Bind this object to FRAME
bindings.put("FRAME", this);
engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);

Assume that engine is the name of your script engine that you have created previously. If that is not the name of your script engine, then change the variable engine to your script engine variable name
//TODO Invoke the registerListener function
if (!(engine instanceof Invocable)) {
    JOptionPane.showMessageDialog(this, "The " + engine.getFactory().getEngineName()
            + " script engine does not support the Invocable interface.\nCannot register listener"
            , "Cannot register", JOptionPane.ERROR_MESSAGE);
    return;
}
       
try {
    Invocable inv = (Invocable)engine;
    inv.invokeFunction("registerListener", (Object)null);
} catch (Exception e) {
    JOptionPane.showMessageDialog(this, e, "Exception", JOptionPane.ERROR_MESSAGE);
}

importPackage(Packages.java.awt);
importPackage(Packages.java.awt.event);

function registerListener() {
    FRAME.addActionListener(listener);
}

function unregisterListener() {
    FRAME.removeActionListener(listener);
}

var action = { actionPerformed:
    function(aEvt) {
        print ("Event handled from JavaScript");
        var button = aEvt.source;
        if (button.foreground == Color.BLUE) {
            button.setForeground(Color.RED);
            button.setText("Pressed");
        } else {
            button.setForeground(Color.BLUE);
            button.setText("Press Me");
        }
    }
}

var listener = new ActionListener(action);


      

Summary


Java scripting supports really opens up the Java platform to developers who do not know Java but want to use the vast libraries that the Java platform offers. Java also benefits from scripting byallowing developers to extend the Java platform and applications with scripts.


                                                                                                                        return to the top


Exercise 2: Desktop Feature Enhancement


Mustang introduces numerous enhancements to the desktop.  In this exercise we will look at the following new enhancements
  1. Desktop Integration
  2. Splash Screen Suport
  3. Tray Icon

(2.1) Desktop Integration


The Desktop API allows a Java application to launch associated applications registered on the native desktop to handle a URI or  a file.  The Desktop class supports the following operations:

Here are some pointers on using the Desktop API

1. Let's create a project in Netbeans first. Launch Netbeans, select menu 'File'->'New Project...', you'll get the following frame:


Figure-2.10: Netbeans New Project frame

2. Select 'General' in 'Categories', and 'Java Application' in 'Projects', click 'Next>'

3. In the 'Name and Location' step, specify Project Name as 'Desktop', select 'Set as Main Project' and 'Create Main Class' checkboxes, accept the default configurations. Click 'Finish'


Figure-2.11: Name and Location step for creating a new project with Netbeans

4. Netbeans has created a new project named 'Desktop' and a class named 'Desktop.Main' for you. In the 'projects' tag on the left side, extend project 'Desktop'->'Source Packages'->'Desktop', then click 'Main.java', you will have the source code of Main.java opened in the editor frame as below.


Figure-2.12: Open Desktop.Main source code in Netbeans

5. Now, we're going to use the Desktop API in Main.java. First, we need to check if the Desktop API is support on a particular operating system. Add the following code in the main() method of Main.java. (Modified code is highlighted in red)

public static void main(String[] args) {
    if (!Desktop.isDesktopSupported()) {
        System.err.println("Your operating system does not support Desktop API");
        System.exit(-1);
    }
}

6. Next, we need to get an instance of Desktop if it is supported. Add the follow code as being highlighted in red

if (!Desktop.isDesktopSupported()) {
    System.err.println("Your operating system does not support Desktop API");
    System.exit(-1);
}
       
Desktop desktop = Desktop.getDesktop();


7. Using the following code to check if a particular operation is supported

Desktop desktop = Desktop.getDesktop();
       
if (!desktop.isSupported(Desktop.Action.PRINT)) {
    System.err.println("It appears that your desktop does not support the print operation");
}


Other operations include Desktop.Action.BROWSE, Desktop.Action.MAIL, Desktop.Action.EDIT, Desktop.Action.OPEN. All the operations which could be supported by the desktop API can be found here

8. Now let's launch the default mail client with a target email address. Add the code to the main() method as following.

if (!desktop.isSupported(Desktop.Action.PRINT)) {
    System.err.println("It appears that your desktop does not support the print operation");
}


if (desktop.isSupported(Desktop.Action.MAIL)) {
    try {
        desktop.mail(new URI("mailto:handsonlabs-javase6@yahoogroups.com"
));
    } catch (IOException ex) {
        ex.printStackTrace();
    } catch (URISyntaxException ex) {
        ex.printStackTrace();
    }
}


9. Launch the Desktop project by selecting menu item 'Run'->'Run Main Project', or clicking the 'Run Main Project' button on the tool bar. You'll see your system default mailer to be opened with the email address filled. For Windows platform, it could be Outlook; for Solaris and Linux, it could be Evolution or ThunderBird as shown below:


Figure-2.13: System default mail launched by Desktop API

Try it yourself Add code to the sample code above to open a webpage with system default browser.


(2.2) Splash Screen Support


Splash screens are the log that you see when an application starts up. When you start NetBeans for this lab, you may have noticed an orange window appears with the NetBeans icon display and a progress bar at the bottom indicating the progress of the startup. Prior to Java SE 6, a developer who wishes to create splash screens for their Java application have to create a JWindow component, then manually load the image.

Java SE 6 changed all this with splah screen support. You specify which image you want to use as your splash screen, and Java SE 6 will do the rest. There are two ways of specify the splash screen. Let's try them both.

1. When launching your Java application, you can specify the splash screen by command line java -splash:<splash screen image>. Now we are going to add a splash creen to the jdk demos.

2. Open a terminal, set PATH variable as here

3. Change the directory to <Unzip_Labfile_Directory>/exerices/ex2

4. Type the following

$ java -splash:splash.jpg -jar <JDK_6_Installation>/demo/jfc/FileChooserDemo/FileChooserDemo.jar

You will see that splash.jpg shows before the FileChooserDemo is launched.

5. Also, if you want to pack your application as a jar file, put the following properteies in the MANIFEST.MF will set the splash screen to your application

Manifest-Version: 1.0
Main-Class: org.foo.MyApplication
SplashScreen-Image: splash.gif

Some times, you need to decorate the splash screen, like using the progress bar, updating text. The SplashScreen API provides additional methods for decorating the splash screen with additional text or animations. Now let's add a splash screen to Netbeans sample application-Anagram Game.

6. Launch your Netbeans, select Menu 'File'->'New Project', you will get the 'Choose Project' screen. As following, select 'Sample'->'General' in the 'Categories' table, 'Anagram Game' in the 'Projects' table, then click 'Next>'


Figure-2.20: Netbeans 'Choose Project' Screen

7. Accept the default setting of project location, or put the new created project to the location as you wish, click 'Finish'


Figure-2.21: Netbeans 'Name and Location' Screen

8. In the 'projects' tag on the left side, extend project 'Anagram Game'->'Source Packages'->'com.toy.anagrams.ui', then click 'Anagrams.java', you will get the Netbeans GUI Builder Matisse's design view as below

Deselect  Show
Figure-2.23: Matisse design view for Anagram Game

9. Click the 'Source' button on top of the design view frame, switch to the source code.


Figure 2.24: Soure code of Anagram Game project

10. Let the program sleep 10 seconds before the UI starts, so that there will be enough time for the splash screen show up. Add the following code(in red) to the main() method

public static void main(String[] args) {
    try {
        Thread.sleep(10000);
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }
    new Anagrams().setVisible(true);
}


11. Now, use Netbeans to launch the application, and set the splash screen. In the project tag, right click at 'AnagramGame', select 'Properties' from the context menu. You'll get the 'Project Properties' dialog for Anagram Game as shown below. Select 'Run', fill the 'VM Options' by '-splash:<Unzip_Labfile_Directory>/exercises/ex2/splash.jpg' (replace <Unizp_Labfile_Directory> with the directory path you actually unzip the lab file).


Figure-2.25: Project Properties dialog of Anagram Game project

8. Select menu item 'Run'->'Run Main Project' or click the 'Run Main Project' button on the tool bar to launch the Anagram Game you created. Now you'll see the splash screen lasts for 10 seconds before the UI of Anagram Game actually shows up.


Figure-2.26: Splash screen for Anagram Game without any prompt information

9. Now let's add some text to the splash screen, so that the users would have some ideas of what's going on before they get to see the application. First, we need to get the instance of the splash screen. Add the following code to the main() method of Anagram Game as below. (Modified code is highlighted in red)

try {
    SplashScreen splash = SplashScreen.getSplashScreen();
    Thread.sleep(10000);
} catch (InterruptedException ex) {
    ex.printStackTrace();
}


10. Then get the Graphics or Graphic2D instance from the splash screen. Add the following code to the main() method as below. (Modified code is highlighted in red)

try {
    SplashScreen splash = SplashScreen.getSplashScreen();
    Graphics2D g = splash.createGraphics();
    Thread.sleep(10000);
} catch (InterruptedException ex) {
    ex.printStackTrace();
}


11. Now, with the Graphics2D context, you could add some prompt text to the splash screen, or do more complicated rendering as you wish. Add the following code to the main() method as below. (Modified code is highlighted in red)

try {
    SplashScreen splash = SplashScreen.getSplashScreen();
    Graphics2D g = splash.createGraphics();
    g.setColor(Color.RED);
    g.drawString("Anagram Game v1.1 starting...", 20, 20);
    splash.update();
    Thread.sleep(10000);
} catch (InterruptedException ex) {
    ex.printStackTrace();
}


12. Select menu item 'Run'->'Run Main Project' or click the 'Run Main Project' button on the tool bar to launch project Anagram Game again, you'll see now there's some prompt text on the top left of the screen.


Figure-2.27: Splash Screen with prompt text on the top left


(2.3) Tray Icon


A tray icon is an icon that is added to the system tray when the representing application is running. Most modern operating system supports this type of application. Typical tray icon applications includes weather, RSS, email notifications, etc. Until recently, it is quite difficult to create Java based tray icons applications. Java SE 6 now supports tray icon development with the TrayIcon API.

Now let's add a Tray Icon for the anagram game project we created in exercise 2.2.

1. First, we need to check if Tray Icon is supported on your platform. Add the following code to the main() method of Anagrams.java class we created in exercise 2.2. (Modified code is highlighted in red)

. . . . . .
new Anagrams().setVisible(true);

if (SystemTray.isSupported()) {
}


2. Next, get the System Tray Instance, if it is supported.

if (SystemTray.isSupported()) {
    SystemTray tray = SystemTray.getSystemTray();
}


3. Now, we are going to create a TrayIcon representing Anagram Game. A TrayIcon object represents a tray icon that can be added to the system tray A  TrayIcon can have a tooltip (text), an image, a popup menu, and a set of listeners associated with it. First, let's create the image and popup menu. Add the following code to the main() method.

if (SystemTray.isSupported()) {
    SystemTray tray = SystemTray.getSystemTray();

    Image trayImg = Toolkit.getDefaultToolkit().getImage("/usr/share/pixmaps/webeyes.png");
      
    PopupMenu popup = new PopupMenu();
    MenuItem defaultItem = new MenuItem("Exit");
    defaultItem.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            System.exit(0);
        }
    });
           
    popup.add(defaultItem);
Here, out popup menu only has one menu item to do exit, you may add more items to the popup menu by yourself. We suppose that you're working on a Solaris 10 X86 platform, so that you could find webeyes.png in the specific directory, if not, please specify the image you want to set for your TrayIcon.

4. Then we need to reate a TrayIcon object with the image, popup menu created above, and set tooltip at the meanwhile. Set the image autosize to true, so that the image shows on the task bar will be autosized according to the panel it is located at. Add the following code.

popup.add(defaultItem);
           
TrayIcon anagramIcon = new TrayIcon(trayImg, "Anagram Game", popup);
anagramIcon.setImageAutoSize(true);

try {
    tray.add(anagramIcon);
} catch (AWTException ex) {
   ex.printStackTrace();
}


5. Launch the anagram game by selecting Netbeans menu item 'Run'->'Run Main Project' or clicking the 'Run Main Project' button on the toolbar. You will now see, when the anagram game is launched, there's a icon located at the status bar of your desktop. Move your mouse into the icon, you'll get the tooltip. Right click the icon, you'll get the popup menu, which contains the exit menu item.



Summary

In this exercise, you have learned the desktop feature enhancements of Java SE 6, including Desktop Integration, Splash Screen and Tray Icon.
 

                                                                                                                        return to the top


Exercise 3: Writing and Processing Custom Annotations

Note: This exercise is based on ideas developed by Kohsuke Kawaguchi on args4j. You can find his original blog on args4j here

Annotations is one of the new features that was introduced to the Java. Annotations allows you to annotate or mark all or parts of you Java classes with information that can be used by tools to generate other wrapper or utility classes, configure database resources or perhaps just embedding information lice copyright, license or the author's name in the class file. Annotations will be used extensively in EJB 3 and JavaEE 5 to help simplify the platform.

In this exercise we will learn the following

(3.1) Need to know


Most of the Java applications that we develop require us to set some options at the command line like so:

java org.fred.MyJavaApp -filename myfile.txt -config config.properties

where -filename and -config are options for MyJavaApp. However parsing commad line options, though simple, is laborious and boring. In this exercise we are going to use annotations to help use automatically generate an options processor to parse the command line options. Here is what we are going to do:

1. This is the command line, with the options that we are going to process:

java org.fred.MyJavaApp -filename myfile.txt -config config.properties -server myserver

2. Assume that we are going to parse -filename -config and -server option in your Java application. Write a Java class with 3 public members of type String, each corresponding to a parameter like so

public class MyOptions {
   public String fileName
   public String configFile;
   public String server;
}
We will refere to MyOptions as an option bean

3. We now map each of these fields to the option switch by our custom annotation call option.option has one field call name which is used to indicate the command line switch that we are interested in. We annotate MyOptions like so:

public class MyOptions {
   @Option(name="-filename") public String fileName
   @Option(name="-config") public String configFile;
   @Option(name="-server") public String server;
}
The Option now maps the values of the option to a field; for example, the value of -filename switch, the value myfile.txt in the above example, will be assigned to fileName member.

4. MyOption class will be compiled with our custom options processor:

javac -d classes -processor options.OptionProcessor MyOptions.java
options.OptionsProcessor is our custom options processor.

5. OptionsProcessor will generate a class call MyOptionsProcessor which will be used to process our 3 option switches. MyOptionsProcessor has a static method call process() which takes in the command line options that we which to process. It then returns an instance of MyOptions will all its fields populated with values from the switches. The process() method has the following signature:

public static MyOptions process(String[] args)
The name of the processor class and the return type of process() is dependent on the option bean class. If the option bean class is call SuperDuperOptions, then the processor class will be called SuperDuperOptionsProcessor and process() will now return an instance of SuperDuperOptions.

6. You can now use MyOptionsProcessor in your application like so:

public static void main(String[] args) {
   MyOptions opts = MyOptionsProcessor.process(args);
   //Do something with the options
   ....

7. Limitations: the following are some features that we will not be considering in this exercise

(3.2) Steps to follow


Open args_processing project. This project can be found under <Unzip_Labfile_Directory>/exercises/ex3.

1. Develop a custom annotation called Option


package options;

import java.lang.annotation.*;

@Retention(value=RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface Option {
    String name();
}

The Option annotation has one field call name. The annotation is only used to annotate fields (indicated by @Target(ElementType.FIELD) and will only be visible in the source (indicated by @Retention(RetentionPolicy.SOURCE).

2. Develop the annotation processor
public class OptionProcessor extends AbstractProcessor
NetBeans will highlight the class OptionProcessor in red complain about methods not implemented, ignore this error for now.
@SupportedAnnotationTypes({"options.Option"})
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class OptionProcessor extends AbstractProcessor

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);
Here is an explanation of the parameters and return values:
Parameters:
annotations - the annotation types requested to be processed
roundEnv - environment for information about the current and prior round
Returns
whether or not the set of annotations are claimed by this processor

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    Messager messager = processingEnv.getMessager();
       
    values = new HashMap<String, String>();
       
    //Loop through the annotations that we are going to process
    //In this case there should only be one: Option
    for (TypeElement te: annotations) {
           
        //Get the members that are annotated with Option
        for (Element e: roundEnv.getElementsAnnotatedWith(te))
            //Process the members
            processAnnotation(e, messager);
    }
       
    if (values.size() > 0)
        try {
            //Generate the option process class
            generateOptionProcessor(processingEnv.getFiler());
        } catch (Exception e) {
            messager.printMessage(Diagnostic.Kind.ERROR, e.getMessage());
        }
  
    return (true);
}


After type (or pasting) the code into OptionProcessor class, NetBeans may compain of missing class. Fix this by importing the appropriate packages.
The member processingEnv, of type ProcessingEnvironment, is a member inherited from AbstractProcessor.processingEnv provides access to the processing environment. In the process method, we see that we are requesting a Messager from processingEnv. A Messager is used by the annotation processing to report errors, warning and other messages.
private void processAnnotation(Element element, Messager msg) {

   //Get the Option annotation on the member
   Option opt = element.getAnnotation(Option.class);

   //Get the class name of the option bean
   className = element.getEnclosingElement().toString();

   //Check if the type in the member is a String. If not we igonre it
   if (!element.asType().toString().equals(String.class.getName())) {
      msg.printMessage(Diagnostic.Kind.WARNING
            , element.asType() + " not supported. " + opt.name() + " not processed");
      return;
   }

   //Save the option switch and the member's name in a hash set
   //Eg. -filename (option switch) mapped to fileName (member)
   values.put(opt.name(), element.getSimpleName().toString());
}
3. Packaging the processor

4. Testing the annotation processor




import options.Option;

public class MyOptions {
    @Option(name="-file")
    public String fileName;
   
    @Option(name="-server")
    public String server;
   
    @Option(name="-config")
    public String configFile;
}

public class Main {
   
    public static void main(String[] args) {
        //MyOptionsProcessor will be generated by OptionProcessor
        MyOptions options = MyOptionsProcessor.process(args);
       
        System.out.println("filename = " + options.fileName);
        System.out.println("server = " + options.server);
        System.out.println("config = " + options.configFile);
    }
}


/* Generated on Thu Jan 25 00:11:56 CST 2007 */
public class MyOptionsProcessor {
    public static MyOptions process(String[] args) {
        MyOptions options = new MyOptions();
        int idx = 0;
        while (idx < args.length) {
            if (args[idx].equals("-file")) {
                options.fileName = args[++idx];
                idx++;
                continue;
            }
            if (args[idx].equals("-server")) {
                options.server = args[++idx];
                idx++;
                continue;
            }
            if (args[idx].equals("-config")) {
                options.configFile = args[++idx];
                idx++;
                continue;
            }
            System.err.println("Unknown option: " + args[idx++]);
        }
        return (options);
    }
}





Summary


In this exercise, you have exercised various features of "FireBug" debugger.  You also learned how to use HTTP POST method instead of HTTP GET method.

                                                                                                                     return to the top



Exercise 4: Java Management eXtentions(JMX) Enhancement

Java SE 6 introduces numerous JMX enhancements. This lab consists of the following exercises:


(4.1) Writing MXBeans

MXBeans provide a convenient way to bundle related values together in an MBean without requiring clients to be specially configured to handle the bundles when interacting with that MBean. This is an incremental change, because MXBeans already existed in J2SE 5.0, in the package java.lang.management. What's new is that users can now define their own MXBeans, in addition to the standard set defined in java.lang.management.

The key idea behind MXBeans is that types such as java.lang.management.MemoryUsage that are referenced in the MXBean interface, java.lang.management.MemoryMXBean in this case, get mapped into a standard set of types, the so-called Open Types that are defined in the package javax.management.openmbean. The exact mapping rules appear in the MXBean specification, but to oversimplify we could say that simple types like int or String are unchanged, while complex types like MemoryUsage get mapped to the standard type CompositeDataSupport.

In this particular case, we are going to use both MBean and MXBean to take snapshot for a resource of type Queue<String>, so that we could see the advantage of MXBean in handling bundled values.

1. Open MXBean NetBeans project.

2. Extend MXBean project in the 'Projects' tab window, exend 'Source Packages'->'mxbeans', double click 'Main.java', and the source code of Main.java is now opened in the source editor window of Netbeans as following

package mxbean;

import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;

public class Main {
   
    public Main() {
    }
   
    public static void main(String[] args) {
       
        Queue<String> queue = new ArrayBlockingQueue<String>(10);
        queue.add("Request-1");
        queue.add("Request-2");
        queue.add("Request-3");
       
        // Wait forever
        System.out.println("Waiting...");
        try {
            Thread.sleep(Long.MAX_VALUE);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

3. In case we're going to take snapshot for the Queue, we need a QueueInfo class representing the snapshot.

package mxbean;

import java.util.Date;

public class QuequeInfo {
    private final Date timestamp;
    private final int size;
    private final String head;
   
    /** Creates a new instance of QuequeInfo */
    public QuequeInfo(Date now, int qsize, String qhead) {
        timestamp = now;
        size = qsize;
        head = qhead;
    }
   
    public Date getTimestamp() {
        return timestamp;
    }
   
    public int getSize() {
        return size;
    }
   
    public String getHead() {
        return head;
    }
}

4. Now, we need to create a Standard MBean to see if you can monitor the Queue by taking snapshot, and do clear for the Queue.




5. Netbeans created two Java files for you: the MBean interface named 'QueueSamplerMBean' and the MBean implementation named 'QueueSampler'. add the following code to the QueueSamplerMBean.java
package mxbean;

/**
 * Interface QueueSamplerMBean
 * QueueSampler Description
 * @author root
 */
public interface QueueSamplerMBean
{
    public QueueInfo getQueueInfo();
    /**
     * newOperation0 Description
     */
    public void clearQueue();
   
}


6. Now, we are going to implement the MBean interface, modify QueueSampler.java as following

package mxbean;
import java.util.Date;
import java.util.Queue;
import javax.management.*;

/**
 * Class QueueSampler
 * QueueSampler Description
 * @author root
 */
public class QueueSampler implements QueueSamplerMBean {
    private Queue<String> queue;
   
    public QueueSampler(Queue<String> aqueue) {
        queue = aqueue;
    }
   
    /**
     * newOperation0 Description
     */
    public void clearQueue() {
        synchronized(queue) {
            queue.clear();
        }
    }
   
    public QueueInfo getQueueInfo() {
        synchronized(queue) {
            return new QueueInfo(new Date(), queue.size(), queue.peek());
        }
    }
}


You'll see, considering of the thread safe, the code lock the queue when taking snapshot.

7. Now we have created the MBean, it's time for us to register it to the platform MBeanServer. Add the following code to Main.java

public static void main(String[] args) {
    // Create the Queue Sampler MBean
    Queue<String> queue = new ArrayBlockingQueue<String>(10);
    queue.add("Request-1");
    queue.add("Request-2");
    queue.add("Request-3");
    
    // Get the Platform MBean Server
    MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
    try {
        // Construct the ObjectName for the MBean we will register
        ObjectName name = new ObjectName("com.example.mxbeans:type=QueueSampler");
        QueueSampler mxbean = new QueueSampler(queue);
      
        // Register the Queue Sampler MXBean
        mbs.registerMBean(mxbean, name);
    } catch (MalformedObjectNameException ex) {
        ex.printStackTrace();
    } catch (NullPointerException ex) {
        ex.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }

    // Wait forever
    System.out.println("Waiting...");
    try {
        Thread.sleep(Long.MAX_VALUE);
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }
}

 
8. Now, we have craeted the MBean and registered it to the MBeanServer. Let's launch Main.java and connect JConsole to it. Select Netbeans menu item 'Run'->'Run Main Project with JConsole...' or click the 'Run Main Project with Local Management' button on the toolbar, you'll get JConsole launched, and have it connected to MXBean project instance already. If you don't have the managment plugin installled in Netbeans, you could launch JConole by commandline.



9. The above snapshot shows the overview information of JVM instance, on which MXBean project is running. Let's take a look at the our own MBeans. Switch to the 'MBeans' tag, extend 'mxbean'->'QueueSampler'->'Attributes' as following



We all notice that in the case of QueueSampler is an MBean, JConsole has no idea of QueueInfo, so it shows 'Unavailable' for attribute QueueInfo. We can solve this problem by redefining QueueSampler as MXBean.

10. Rename MBean interface QueueSamplerMBean to MXBean interface QueueSamplerMXBean.

11. It's done. Launch MXBean project, and connect JConsole with it again, as step 8 shows. Now you'll see that JConsole recongines the type of QueueInfo attribute.



Note that the type of QueueInfo is listed as javax.management.openmbean.CompositeData

12.  Double click QueueInfo value: javax.managment.openmbean.CompositeDataSupport, you'll get detailed information contained in QueueInfo. By clicking 'Refresh' button, you'll see the timestamp updates.



So, we could tell from the exercise above, that the creation of MXBean is pretty much the same as Standard MBean, expect that it requires the interface named as '<SomeThing>MXBean'.

(4.2) Using Descriptors


Descriptors give you a convenient way to attach arbitrary extra metadata to your MBeans. This is an incremental change because Descriptors have always existed in the JMX API, but only in conjunction with Model MBeans. In Java SE, Descriptors are available with all types of MBeans.

For most constructors in the classes MBean*Info (MBeanInfo, MBeanAttributeInfo, etc), a parallel constructor has been added with the same parameters plus an additional Descriptor parameter. Likewise for OpenMBean*InfoSupport. The MBean*Info and OpenMBean*InfoSupport classes contain now a getDescriptor() method.

Open MBeans return information about default and legal values from the getDefaultValue(), getLegalValues(), getMaxValue(), getMinValue() methods of OpenMBeanParameterInfo and OpenMBeanAttributeInfo. This information is now also present in the corresponding Descriptors, and other types of MBean can also return the information in their Descriptors.

A new annotation DescriptorKey can be used to add information to the Descriptors for a Standard MBean (or MXBean) via annotations in the Standard MBean (or MXBean) interface. This makes it possible for a tool that generates Standard MBeans from an existing management model to include information from the model in the generated MBean interfaces, rather than in separate files.

For example, with this definition...

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Units {
       @DescriptorKey("units")
       String value();
}

...you can define an annotation @Units that you can then use like this...

public interface CacheMBean {
       @Units("Whatsits")
       public int getUsed();
}

1. Now let's start to create some descriptors for project 'MXBean' we use in 4.1 in Netbeans. Open MXBean project in Netbeans, the project is located at <Unzip_Labfile_Directory>/exercises/ex4.

2. Right click 'MXBean' in the 'Projects' tab window, select 'New'->'File/Folder...' in the context menu.

3. Select 'Java Classes' in the 'Categories' list, 'Java Annotation Type' in the 'File Types' list as below, click 'Next>'



4. Set 'Author' as 'Class Name', and 'mxbean' as 'Package', click 'Finish'



5. Open Author.java in the editor by double clicking 'Author.java' node under 'MXBean'->'Source Packages'->'mxbean', modify the code as following:

package mxbean;

import java.lang.annotation.*;
import javax.management.DescriptorKey;

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Author {
    @DescriptorKey("author")
    String value();
}


6.  Create another two annotations: Version.java and DisplayName.java in the same way as above steps. Editor the two annotations as following:

package mxbean;

import java.lang.annotation.*;
import javax.management.DescriptorKey;

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Version {
    @DescriptorKey("version")
    String value();
}


package mxbean;

import java.lang.annotation.*;
import javax.management.DescriptorKey;

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DisplayName {
    @DescriptorKey("displayName")
    String value();
}


7. Open QueueSamplerMXBean.java in the editor by double clicking it. Annotate the MXBean interface as following:

package mxbean;

@Author("Mr Bean")
@Version("1.0")
public interface QueueSamplerMXBean
{
    @DisplayName("GETTER: QueueSample")
    public QueueInfo getQueueInfo();
 
    @DisplayName("OPERATION: clearQueue")
    public void clearQueue();
}


8. Launch the application, and connect JConsole to it. Either select Netbeans menu item 'Run'->'Run Main Project with JConsole...' or click 'Run Main Project with Local Management' button on the tool bar, you'll get JConsole connected with Main.java. Go to the MBeans tab, expand the mxbeans node and select the QueueSampler node, you should see the fields "author" and "version" in the MBeanInfo Descriptor table as following.



9. Extend 'Attributes'->'QueueInfo', you should see 'displayName: GETTER: QueueInfo' in the 'Descriptor' table



10. Extend 'Operations'->'clearQueue', you should see 'displayName: OPERATION: clearQueue' in the 'Descriptor' table



11. Close JConsole, Main.java exits as well.

Summary


In this exercise, we learnd how to create MXBean and how to create Descriptors for MBeans(MXBeans).

                                                                                                                     return to the top



How to send questions/feedback's on this lab


Congratulations!  You have successfully finished the lab.  If you have anyquestions or  feedback's on this lab, please send them to handsonlabs-javase6@yahoogroups.com.  Thanks!

Volunteers who can help others on this lab (via email) are needed.  If you have a few spare cycles and willing to help others, please add yourself to the list by sending an email to handsonlabs-javase6-subscribe@yahoogroups.com