Monday Feb 22, 2010

Display the tooltip of a JPanel node of a JTree

If you need a JTree composing of nodes each of that consists of icon, text and check box, which Java Swing component will you choose to adopt? JPanel will be the answer. 

Following is a example of such a JTree:


import java.awt.\*; 
import java.awt.event.\*; 
import java.util.EventObject; 
import javax.swing.\*; 
import javax.swing.tree.\*; 

public class PanelNodeTip { 
    private JScrollPane getContent() { 
        Icon icon = UIManager.getIcon("Tree.closedIcon"); 
        DefaultMutableTreeNode root = 
            new DefaultMutableTreeNode( 
                     new PanelNode("Root", false, icon, "Root Tip")); 
        root.add(new DefaultMutableTreeNode( 
                     new PanelNode("Node 1", false, icon, "Node 1 Tip"))); 
        root.add(new DefaultMutableTreeNode( 
                     new PanelNode("Node 2", true, icon, "Node 2 Tip")));

        JTree tree = new JTree(new DefaultTreeModel(root));

        tree.setCellRenderer(new PanelNodeRenderer()); 
        tree.setCellEditor(new PanelNodeEditor()); 
        return new JScrollPane(tree); 

    public static void main(String[] args) { 
        JFrame f = new JFrame(); 
        f.getContentPane().add(new PanelNodeTip().getContent()); 

class PanelNode { 
    String name; 
    boolean value; 
    Icon icon; 
    String tip;

    public PanelNode(String s, boolean b, Icon icon, String t) { 
        name = s; 
        value = b; 
        icon = icon; 
    tip = t;

    public String toString() { 
        return "PanelNode[value: " + value + ", name:" + name + 
               "icon: closedIcon, " + tip + "]"; 

    public String getTip() {
    return tip;

class PanelNodeRenderer implements TreeCellRenderer { 
    JCheckBox checkBox; 
    JLabel label; 
    JPanel panel; 

    public PanelNodeRenderer() { 
        checkBox = new JCheckBox(); 
        label = new JLabel(); 
        panel = new JPanel(); 

    public Component getTreeCellRendererComponent(JTree tree, 
                                                  Object value, 
                                                  boolean selected, 
                                                  boolean expanded, 
                                                  boolean leaf, 
                                                  int row, 
                                                  boolean hasFocus) { 
        DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; 
        PanelNode panelNode = (PanelNode)node.getUserObject(); 

    label.setToolTipText("ToolTip Test for " + panelNode.getTip());
    tree.setToolTipText("ToolTip Test for " + panelNode.getTip());
        return panel; 

class PanelNodeEditor extends AbstractCellEditor 
                      implements TreeCellEditor, ActionListener { 
    JCheckBox checkBox; 
    JLabel label;     
    PanelNode panelNode; 
    JComponent editedComponent; 
    JPanel panel; 

    public PanelNodeEditor() { 
        checkBox = new JCheckBox(); 
        label = new JLabel(); 
        panel = new JPanel(); 

    public Component getTreeCellEditorComponent(JTree tree, 
                                                Object value, 
                                                boolean isSelected, 
                                                boolean expanded, 
                                                boolean leaf, 
                                                int row) { 
        DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; 
        panelNode = (PanelNode)node.getUserObject(); 
        return panel; 

    public Object getCellEditorValue() { 
        if(editedComponent == label) 
   = label.getText(); 
            panelNode.value = checkBox.isSelected(); 
        return panelNode; 

    public boolean isCellEditable(EventObject anEvent) { 
        if (anEvent instanceof MouseEvent) { 
            return ((MouseEvent)anEvent).getClickCount() >= 1; 
        return true; 

    public void actionPerformed(ActionEvent e) { 
        editedComponent = (JComponent)e.getSource(); 


Compile and execute the program of with JDK 1.5, you will found that there is no tooltip even if you put the mouse on any of the JTree nodes. What's wrong with it? I did add the lines of

label.setToolTipText("ToolTip Test for " + panelNode.getTip());
    tree.setToolTipText("ToolTip Test for " + panelNode.getTip());

If you execute the program with JDK/JRE 1.6, the tooltips display normally. However they doggedly disappear with JDK/JRE 1.5.

Is it a problem of JDK/JRE 1.5? Maybe. But I need the tooltips. What should I do?

The solution is simple. You just need to replace the line of

JTree tree = new JTree(new DefaultTreeModel(root));

with the following paragraph:

JTree tree = new JTree(new DefaultTreeModel(root)) {
        public String getToolTipText(MouseEvent ev) {
                if (ev == null) {
                      return null;
                TreePath path = this.getPathForLocation(ev.getX(), ev.getY());
                if (path != null) {
                    DefaultMutableTreeNode node =
                        (DefaultMutableTreeNode) path.getLastPathComponent();
                    return ((PanelNode) node.getUserObject()).getTip();
                return null;


Now you can see the tooltips no matter with JVM 1.5 or 1.6.

Do you find what the text in the tooltip is displayed exactly? It is the panelNode.getTip() instead of "ToolTip Test for " + panelNode.getTip(). The lines of label.setToolTipText(...) and tree.setToolTipText(...) could be removed simply.

Wednesday Dec 16, 2009

Rescue openSUSE grub

NOTE: The solution has tested on openSUSE 1.2 and openSUSE 11.1.

I installed openSUSE 11.1 and Windows 7 on a single hard disk. When the Windows 7 was out of gear, I rescued the Windows 7. The Windows OS works. However, the openSUSE grub was overwritten. I rescued the grub by following steps:

[1] Reboot the system with OpenSUSE 11.1 installation disc.

[2] Select 'Rescue System' and login with root.

[3] Enter grub interface by executing 'grub' in command line.

[4] INPUT: grub> find /boot/grub/stage1

    OUTPUT: (hd0, 4)

    INPUT: grub> root (hd0, 4)

           grub> setup (hd0)

    OUTPUT displays that it succeed.

[5] Reboot system. The grub is rescued. 

Thursday Nov 12, 2009

Is the cacerts file in diablo-JRE 1.6 incorrect?

The size of cacerts file of diablo-JRE 1.6 (JRE on FreeBSD OS) is only 32Bytes. Compare with
the 63KB cacerts file of SUN Solaris JRE 1.6, the diablo cacerts is
almost an empty file. 

When I run an java application which transports date with HttpClient
with the diablo-JRE 1.6, the transportation fails. After I replaced the
diablo cacerts with a cacerts file from Sun Solaris JRE 1.6, the
application transports data successfully. 

Is it the diablo cacerts incorrect?

BTW: I sent emails to freebsd java alias to ask this question. But I receive no reply.

Tuesday Jun 24, 2008

How should I do to block garbage comments ?

I am really boring to delete hundreds garbage comments (advertisements, baleful jokes etc.) manually. Who would teach me how to block them?

Wednesday May 14, 2008

Sun Device Detection Tool 2.1 source code is available on

Source code of Sun Device Detection Tool 2.1 is open sourced at (

The tool is a Java application, and builds a JNI binding for each of the following interfaces to collect device information on mutiple platforms:

Solaris OS
Looking up PCI nodes in prom tree and reading device data from the nodes
Windows OS
Win32 API  
Reading device data from registry through Win32 API
Linux OS
/proc/bus/pci Scanning the file of /proc/bus/pci and reading device data from it 
Mac OS X IOPCIDevice Collecting PCI Device Information

Thanks a lot for Tony Hu's hard work of developing native programs for Solaris, Windows, Linux and Mac platforms to scan PCI devices.


Device Detection Tool product web page:
Device Detection Tool open project web page:
Device Detection Tool developer blog:
Device Detection Tool support alias:

Help you to determine if OpenSolaris 2008.05 can be installed on your x86/x64 system

Sun Device Detection Tool 2.1 provides driver information for OpenSolaris 2008.05 now. You can check the OpenSolaris 2008.05 driver availability status for the PCI devices on your x86/x64 systems.


Monday May 05, 2008

Run a jar file with ant

Ant is a very useful tool. We can use it to compile, package jar files, and run the packaged jar.  Following is example to describe how to write the build.xml file to run a packaged jar file with ant.

The jar file's name is ddtool.jar, and the main class of it is com.sun.ddtool.manager.DDToolManager. If the jar file is executed manually in CLI mode, the running command will be as follows:

$ java -classpath ./lib/commons-codec-1.3.jar:./lib/commons-httpclient-3.0.1.jar:./lib/commons-logging-1.1.jar:./ddtool.jar -Djava.library.path=lib com.sun.ddtool.manager.DDToolManager DriverDB/

NOTE: "", "DriverDB/", and "" are 3 arguments passed to the main class.

In the build.xml, we can describe it like this:

<!--class path-->
 <path id="execute-classpath">
  <fileset dir="${dist.bin.dir}/">
   <include name="ddtool.jar"/>
  <fileset dir="${lib.dir}/">
   <include name="\*.jar"/>

  <target name="execute" depends="packjar" description="run the project">    
     <java classname="com.sun.ddtool.manager.DDToolManager" failonerror="true" fork="true">
       <classpath refid="execute-classpath"/>
       <sysproperty key="java.library.path" value="lib/"/> 
       <arg value=""/>
       <arg value="DriverDB/"/>   
       <arg value=""/>

Or, if you don't want to define the execute-classpath, the above targets could be replaced by the following one:
  <target name="execute" depends="packjar" description="run the project">
     <exec executable="java">
       <arg line="-classpath ${dist.lib.dir}/commons-httpclient-3.0.1.jar:${dist.lib.dir}/commons-codec-1.3.jar:${dist.lib.dir}/commons-logging-1.1.jar:${dist.bin.dir}/ddtool_21.jar  -Djava.library.path=${dist.lib.dir} com.sun.ddtool.manager.DDToolManager DriverDB/"/>

NOTE: "${dist.lib.dir}/commons-httpclient-3.0.1.jar:${dist.lib.dir}/commons-codec-1.3.jar:${dist.lib.dir}/commons-logging-1.1.jar" in the second example cannot be replaced with "${dist.lib.dir}/\*.jar".

Friday Feb 22, 2008

Change properties of Java ToolTip (background, border, etc.)

Sun UIRB commitee requires that ToolTips should have a yellow background color (#fff7c8), with a black or dark grey (#4c4f53) border, and the ToolTip should be showed for 15 seconds. 

To change such properties, there is a very simple way by adding a few lines of code (see below) where the ToolTip is referenced:


 UIManager.put("ToolTip.background", new ColorUIResource(255, 247, 200)); // The color is #fff7c8.
 Border border = BorderFactory.createLineBorder(new Color(76,79,83)); // The color is #4c4f53.
 UIManager.put("ToolTip.border", border);
 ToolTipManager.sharedInstance().setDismissDelay(15000);// 15 seconds    

 setToolTipText(message); // The message is a String variable containing text to display.


If more properties of the ToolTip are required to be changed, or the changed ToolTip needs to be referenced multiple times in the program, it is better to create a Look&Feel class as follows:


\* This test frame is to demonstrate changing of tooltip background

import javax.swing.\*;
import javax.swing.text.\*;
import java.awt.\*;
import java.awt.event.\*;

import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.ColorUIResource;
import javax.swing.border.Border;

class ToolTipLookAndFeel extends MetalLookAndFeel
    protected void initSystemColorDefaults(UIDefaults table)
        table.put("info", new ColorUIResource(255, 247, 200));   

    protected void initComponentDefaults(UIDefaults table) {

    Border border = BorderFactory.createLineBorder(new Color(76,79,83));
    table.put("ToolTip.border", border);

public class DemoFrame extends JFrame
    public DemoFrame()

        try {                             
            UIManager.setLookAndFeel(new ToolTipLookAndFeel());
        } catch(Exception ex) {       
            System.err.println("ToolTipLookAndFeel exception!");

        getContentPane().setLayout(new FlowLayout());

        JButton btn = new JButton("<html>Mouse Over me <br> for ToolTip </html>");               
        btn.setToolTipText("<html>I have changed <br> the color of <br> background and border. <br> It works! </html>");


        JLabel label = new JLabel("What about me");
        label.setToolTipText("Me Too...");


    public static void main(String[] arg)
        DemoFrame m = new DemoFrame();

        m.setSize(new Dimension(300, 150));


Save the above code into a single java file, compile it, and run the DemoFrame class.

Note: If the following line of

    UIManager.setLookAndFeel(new ToolTipLookAndFeel());

is changed to


the code of ToolTipLookAndFeel class must be moved to a separated file instead of being a internal class in the file. And the must be compiled before file.
Otherwise, the ToolTipLookAndFeel class cannot be really reference, the exception will be catched without any error or warning message while compiling. It is a little tricky.


Tuesday Dec 04, 2007

Is it a bug of Java Web Start?

I hava a Java application which is deployed by JavaWS. And a shared objected is deployed as a native library of the application. (Following is a piece of the JNLP file.)
<j2se version="1.4+" java-vm-args="-client"/>
<jar href="/foo.jar" main="true" download="eager"/>
<nativelib href="/libfoo.jar"/>

Theoretically, when the application launches, all jar files have been loaded on users' system. And the application should work, even if the network cable is plugged out.

When I tested the application with JRE 1.4.2 and 1.5, it actually worked after launching without network access.

However, when I tried it with JRE 1.6, the application fails. It seems that the application cannot load the shared object.

I am sure that I run the application with JRE1.4.2, 1.5 and 1.6 on the same machine. Each time before I tested it, the Java cache (/.java directory) is cleaned. So I can say that the shared library did not exist on the box for testing 1.4.2 and 1.5.

Is it a bug of JavaWS?

Wednesday Nov 28, 2007

A simple example of JNI

Java Native Interface (JNI) is a standard programming interface for writing Java native methods and embedding the JVM into native applications. Simply, it is a Java technology with which a Java application can call a method written with such as C, C++ and assembly.

Adopting JNI is very simple. You need two components -- a Java program, and a native library. The native library is written in other languages and compiled on corresonding platforms.

A function defined in the native library should be declared in Java code as a 'native' function. And the native library needs to be load in Java code with the System.loadLibrary method. The natvie function could be referced by other regular functions in the Java code.

Following is an example of the Java Code.

public class JNIFoo {    
    public native String nativeFoo();    

    static {

    public void print () {
    String str = nativeFoo();
    public static void main(String[] args) {
    (new JNIFoo()).print();

# javac
# javah -jni JNIFoo

A file named as JNIFoo.h is created by running the above two commands. A function of 'JNIEXPORT jstring JNICALL Java_JNIFoo_nativeFoo  (JNIEnv \*, jobject)' is in the JNIFoo.h file. The function must be implemented in a source code file (e.g. a C file), and it is the actually entry to do what the funtion of natvieFoo() in Java code do.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include "JNIFoo.h"

JNIEXPORT jstring JNICALL Java_JNIFoo_nativeFoo (JNIEnv \*env, jobject obj)
  int i;
  int ds_ret;

  char\* newstring;

  jstring ret = 0;

  newstring = (char\*)malloc(30);

  if(newstring == NULL)
      return ret;

  memset(newstring, 0, 30); 
  newstring = "foo: Test program of JNI.\\n";

  ret = (\*env)->NewStringUTF(env, newstring);


  return ret;

JNI libraries are named with the library name used in the System.loadLibrary method of your Java code with a prefix and a suffix. On different OS, the prefix and suffix might be different.

On Solaris OS, it is prefixed by 'lib' and suffixed with '.so'

# cc -Kpic -G -o -I/usr/java/include -I/usr/java/include/solaris foo.c -z text

On Linux OS, it is prefixed by 'lib' and suffixed with '.so'.

# gcc -shared -fpic -o -I/usr/java/include -I/usr/java/include/linux foo.c

On Windows OS, it is prefixed by nothing and suffixed with '.dll'.
It could be compiled with Visual Studio automatically and create a file named as foo.dll.

On Mac OS, it is prefixed by 'lib' and suffixed with '.jnilib'.

# gcc -dynamiclib -o libfoo.jnilib -I/System/Library/Frameworks/JavaVM.framework/Headers foo.c -framework JavaVM


To run the JNI program locally, the following command is fine:

# java -Djava.library.path=<path_of_native_lib> JNIFoo 

Tuesday Nov 27, 2007

Bundle an executive binary program with a Java Web Start application

When an Java application needs to call native program(s) to handle situations, JNI is frequently the preferred technology.

If the Java application is deployed with Java Web Start. The embedded native program could be packaged in a jar file, and the jar is deployed as a native library of the Java application.

Following is a piece of JNLP file. It is obvious that the foo.jar is the Java application, and the libfoo.jar including a native program which normally should be an shared object (e.g. on Unix-like OS or foo.dll on Windows OS).



    <j2se version="1.4+" java-vm-args="-client"/>

    <jar href="/foo.jar" main="true" download="eager"/>

    <nativelib href="/libfoo.jar"/>



However, sometimes a shared object doesn't meet our requirement. For example, an executive program is always much smaller rather than a dynamic-link library. Considering to reduce launch time, we prefer to bundle an exe file with our JavaWS application instead of a DLL file.

Technically we can bundle any files that we want in our application JAR. The executive binary program could also be packaged in a JAR file. But the methods in the native program cannot be referenced by the Java application directly. In such scenario, the JNI is inapplicable.

The executive program should be executed on users' system, and interface between it and the Java application is its out put message.

Although, it is possible to reference to the contents of the Java Web Start cache directly. We don't recommend it, since the cache is a private implementation of Java Web Start, and is subjected to change anytime.

The URL returned for calls to ClassLoader.getResource() is now the proper JAR URL of the item on the net. (, see bullet item related to "Jar Indexing")

We could to access the EXE in the JAR by using a JarURLConnection to get to the resource. e.g ,

| jar:!/foo.exe |

We can then get the InputStream of the binary program, and then copy it to a temp location on disk, and then execute it from there.

If the JAR resource is already cached, the contents of the resource will be returned from the cache directly, so there won't be extra network connections to the JAR itself.

NOTE: This solution is only applicable with JVM 1.6.

Following is an example of such an implementation.

File of


... ...

   private String execute() throw IOException {   

    ... ...

    ClassLoader loader = this.getClass().getClassLoader();

    /\* jnitest is an binary program running on Solaris OS.

        It is packaged in a Jar file of libjnitest.jar \*/

    URL url = loader.getResource("jnitest");                           

    /\* Note: the size of the buffer must not less than the size of jnitest \*/

    byte[] buf = new byte[20000];

    int len = 0;

    JarURLConnection uc = (JarURLConnection)url.openConnection();             


    InputStream in = new BufferedInputStream(uc.getInputStream());

    len =;


    File tmpFile = new File(fileName);           

    FileOutputStream output = new FileOutputStream(tmpFile);

    output.write(buf, 0, len);  


    runCommand("chmod +x " + fileName);

    /\* Execute the executive native program, and get its output message \*/

    String outputMessage = runCommand(fileName);            

    return outputMessage;


    private String runCommand(String command) {

        String[] cmd0 = {"sh", "-c", command};

        try {

                Process ps = Runtime.getRuntime().exec(cmd0);

                String str1 = loadStream(ps.getInputStream());

                return str1;

        } catch (IOException ioe) {

                System.err.println("Error: running command on Solaris OS failed.");

        return null;


    private String loadStream(InputStream input) throws IOException {

        int count;

        int BUFFER = 20;

        byte[] data = new byte[BUFFER];

        input = new BufferedInputStream(input);

        StringBuffer buff = new StringBuffer();

        while ((count =, 0, BUFFER)) != -1)

            buff = buff.append(new String(data, 0, count));

        return buff.toString();


... ...

File of testfoo.jnlp


... ...


    <j2se version="1.4+" java-vm-args="-client"/>

    <jar href="/testfoo.jar" main="true" download="eager"/>   

    <nativelib href="/libjnitest.jar"/>


... ...


Ye Julia Li


« April 2014