More Enhancements in Java SE 6

   
By Robert Eckstein, January 2006  

Version 6 of the Java Platform, Standard Edition contains a number of features that make programming with Java technology easier. In this article, we discuss four new features that allow you to do the following:

  • Set file and directory permissions
  • Obtain the free space and usable space on partitions
  • Add Component objects to the tabs of a JTabbedPane
  • Use the popular SwingWorker class in conjunction with your Java Foundation Classes/Swing (JFC/Swing) applications

If the JSR 270 Expert Group approves these features through the Java Community Process program, you can expect to find them in the final release of Java SE 6.

Note: To run the code in this article, you must download and install the latest build.

Setting File and Directory Permissions

Starting with build 31, you can now set the readable, writable, and executable flag of a file in the local file system. This functionality has been added to the java.io.File class with the following methods:

    public boolean setReadable(boolean readable, boolean ownerOnly) 
    public boolean setReadable(boolean readable)

    public boolean setWritable(boolean writable, boolean ownerOnly) 
    public boolean setWritable(boolean writable) 

    public boolean setExecutable(boolean executable, boolean ownerOnly) 
    public boolean setExecutable(boolean executable) 

If you're working with a UNIX system, these methods should be very familiar -- they mirror much of the functionality of the chmod command. These methods attempt to set the appropriate permission flag for the file or directory represented by the current File object. If the optional second parameter is set to true, the permission applies only to the owner flag. Otherwise, these methods are applied to all users. Note that if the underlying file system is unable to distinguish the owner's permission from that of others, as is the case with some versions of Microsoft Windows, then the permission will apply to everybody, regardless of the value passed in.

If you're a Windows user with an NT file system (NTFS), you should read this document, which explains how to reach options that control various user permissions for files.

As you might expect, each of these methods will fail (that is, return false) if the user does not have permission to change the access permissions of this abstract pathname. These methods can also throw a java.lang.SecurityException if a Java security manager exists and its checkRead()/checkWrite()/checkExecute() method denies access to the file.

Table 1 shows the typical result of running these commands on various file systems, along with the applicability of the command on the target operating system.

Table 1. The java.io.File Permission Operations on Common OS File Systems
 
 
 
Windows XP
Linux
Solaris
setReadable(true) returns
true
true Equivalent to chmod +r
true Equivalent to chmod +r
setReadable(false) returns
false File readability cannot be set to false in Windows
true Equivalent to chmod -r
true Equivalent to chmod -r
setWritable(true) returns
true Toggles the Windows read-only file property
true Equivalent to chmod +w
true Equivalent to chmod +w
setWritable(false) returns
true Toggles the Windows read-only file property
true Equivalent to chmod -w
true Equivalent to chmod -w
setExecutable(true) returns
true
true Equivalent to chmod +x
true Equivalent to chmod +x
setExecutable(false) returns
false File executability cannot be set to false in Windows
true Equivalent to chmod -x
true Equivalent to chmod -x

The methods of determining whether a file is readable, writable, or executable remain from the previous version of this platform, Java 2 Platform, Standard Edition (J2SE) 5.0.

     public boolean canRead();
     public boolean canWrite();
     public boolean canExecute();

Obtaining Space Allocation on Disks

In addition to allowing you to set file and directory permissions, Java SE 6 gives you three new methods to determine the amount of space available on the partition represented by a java.io.File object:

     public long getTotalSpace();
     public long getFreeSpace();
     public long getUsableSpace();

Each of these methods returns the requested size, in bytes, of the partition represented by the java.io.File or 0L if a partition cannot be obtained from the File object.

With getFreeSpace() and getUsableSpace(), the returned number of unallocated bytes is, according to the documentation, "a hint, but not a guarantee, that it is possible to use most or all of these bytes. The number of unallocated bytes is most likely to be accurate immediately after this call. It is likely to be made inaccurate by any external I/O operations including those made on the system outside of this virtual machine."

What's the difference between these two methods? The getFreeSpace() method returns an instantaneous count of the amount of free space on the partition. The getUsableSpace() method, on the other hand, contains extra functionality to check for write permissions and other operating system restrictions, which returns a better estimate of how much space will be available. If you want to determine whether you have enough disk space before writing to a file, getUsableSpace() will typically give you a more accurate estimate. Note that both of these methods can throw a SecurityException if a security manager has been installed and it denies a call to RuntimePermission("getFileSystemAttributes")

Representing Tabs in JTabbedPane With Components

This is a subtle but valuable change in Swing's JTabbedPane. Previously with JTabbedPane, you were limited to representing a tab with a String, an Icon, or a combination of both. In addition, you could add a tooltip to the tab if you wanted. Starting with build 39, it is now possible to use a Component to represent the tab in a JTabbedPane. Although this may bring to mind a number of possibilities, the most common use of this feature will be to add a Close button that will remove the tab from the JTabbedPane.

A recent blog entry by Sun programmer and Swing engineer Alexander Potochkin on this subject specifies the three new methods that have been added to JTabbedPane.

You can set a Component as a tab using the following method:

public void setTabComponentAt(int index, Component component)

You can get this component with the following method:

public Component getTabComponentAt(int index)

You can test whether a component is used in this JTabbedPane with this method:

public int indexOfTabComponent(Component tabComponent)

Here is sample source code for a tabbed pane that allows you to add and remove tabs dynamically from a JTabbedPane. Note that in this example we have created a JPanel that consists of two components: a JLabel on the left side of the panel ( BorderLayout.WEST) and a button with an ImageIcon on the right side of the panel ( BorderLayout.EAST). The graphic used is a 10x10 pixel gif that consists of a small X. In order to keep the size of the button small, we reset its preferred size to the icon's width and height plus two.

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;


public class TabbedPaneExample implements ActionListener {

    private JFrame frame;
    private JTabbedPane tabbedPane;
    private JButton addTabButton;
    
    private ImageIcon closeXIcon;
    private Dimension closeButtonSize;
    
    private int tabCounter = 0;
    
    public TabbedPaneExample() {

        //  Create the tabbed pane.
        
        tabbedPane = new JTabbedPane();
        
        //  Create a button that users can press to add a tab 
        //  to the tabbed pane.
        
        addTabButton = new JButton("Add Tab");
        addTabButton.addActionListener(this);
        
        //  Create a frame to hold the tabbed pane.
        
        frame = new JFrame();
        
        //  Create an image icon of the small 'X' for use with a close
        //  button on teach tab. The gif loaded is a 10x10 graphic 
        //  with transparency on areas that are not black.
        
        closeXIcon = new ImageIcon("C:/CloseX.gif");
        
        //  Create a Dimension that can be used to size the close
        //  buttons.
        
        closeButtonSize = new Dimension(
                closeXIcon.getIconWidth()+2,
                closeXIcon.getIconHeight()+2);
        
        //  Add the tabbed pane to the center of the graphic and the 
        //  "Add Tab" button to the south. Then pack it, size it,
        //  and show it.
        
        frame.add(tabbedPane, BorderLayout.CENTER);
        frame.add(addTabButton, BorderLayout.SOUTH);
        
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        frame.pack();
        frame.setMinimumSize(new Dimension(300, 300));
        frame.setVisible(true);
        
    }
    
    public void actionPerformed(ActionEvent e) {
        
        final JPanel content = new JPanel();
        
        //  Create a panel that represents the tab and ensure that it
        //  is transparent.
        
        JPanel tab = new JPanel();
        tab.setOpaque(false);
        
        //  Create a label and a Close button for the tab. Be sure to
        //  set its preferred size to nearly the size of the icon, and
        //  create an action listener that will locate the tab and 
        //  remote it from the tabbed pane.
        
        JLabel tabLabel = new JLabel("Tab " + (++tabCounter));
        
        JButton tabCloseButton = new JButton(closeXIcon);
        tabCloseButton.setPreferredSize(closeButtonSize);
        tabCloseButton.addActionListener(new ActionListener() {
            
            public void actionPerformed(ActionEvent e) {
                int closeTabNumber = 
                    tabbedPane.indexOfComponent(content);
                tabbedPane.removeTabAt(closeTabNumber);
            }
            
        });
        
        tab.add(tabLabel, BorderLayout.WEST);
        tab.add(tabCloseButton, BorderLayout.EAST);
        
        //  Add the tab to the tabbed pane. Note that the first
        //  parameter, which would ordinarily be a String that 
        //  represents the tab title, is null.
        
        tabbedPane.addTab(null, content);
        
        //  Instead of using a String/Icon combination for the tab,
        //  use our panel instead.
        
        tabbedPane.setTabComponentAt(tabbedPane.getTabCount()-1, tab);
        
    }
   
    public static void main(String[] args) {
        
        TabbedPaneExample main = new TabbedPaneExample();
    }
    
}

The result is shown in Figure 1.

 
Figure 1. A <code>JTabbedPane</code> With Several <code>JComponents</code> as Tabs
Figure 1. A JTabbedPane With Several JComponents as Tabs

Note that Alexander Potochkin's blog entry takes a different approach and subclasses JButton with one that overrides paintComponent() and draws its own ("X"). This more complex approach is very useful if you don't want to distribute a gif with your code.

SwingWorker Is Now Included

Most Swing programmers know that whenever they write event-handling code, such as an ActionListener that gets called when a button is pressed, events need to be processed quickly. You never want to spend any more time than you have to processing on the event-handling thread, or your Swing graphical user interface (GUI) will become nonresponsive and be unable to repaint itself efficiently. Taking on a larger task from the event dispatch thread often means kicking off a separate "worker" thread from the event dispatch thread and letting that run in the background. Thus, when writing a multithreaded application using Swing, a programmer needs to keep these two rules in mind:

  • Time-consuming tasks should not be run on the event dispatch thread, which causes the application to become unresponsive. They should be run on a worker thread instead.
  • Updates to Swing components should be performed on the event dispatch thread only.

Because this means that there are at least two threads at play, it helps to have a class that can handle interthread communication. The good news is that the SwingWorker class, which has been a popular solution for some time with Swing programmers, is included in the latest release.

Here is the official declaration of SwingWorker from the Javadocs.

public abstract class  
                   SwingWorker<T,V> extends  
                   Object
implements  
                   RunnableFuture<T>
                

Note that the declaration contains two generic type variables: T and V. If you're not familiar with type variables, be sure to read about Generics, a feature that was introduced in J2SE 5.0. Here are the definitions:

  • T: the result type returned by this SwingWorker's doInBackground() and get() methods
  • V: the type used for carrying out intermediate results by this SwingWorker's publish() and process() methods

We'll talk about these methods shortly. First, however, let's introduce the thread architecture that is used with a SwingWorker. To quote from the Javadocs: "There are three threads involved in the life cycle of a SwingWorker:

  • Current thread: The execute() method is called on this thread. It schedules SwingWorker for the execution on a worker thread and returns immediately. One can wait for the SwingWorker to complete using one of two get() methods.
  • Worker thread: The doInBackground() method is called on this thread. This is where all background activities should happen. To notify PropertyChangeListeners about bound properties changes, use the firePropertyChange and getPropertyChangeSupport() methods. By default, two bound properties are available: state and progress.
  • Event dispatch thread: All Swing-related activities occur on this thread. SwingWorker invokes the process() and done() methods and notifies any PropertyChangeListeners on this thread."

Typically, the current thread on which you instantiate the SwingWorker subclass is the event dispatch thread. The SwingWorker then usually follows this series of events:

  1. The execute() method is called or run on the event dispatch thread.
  2. SwingWorker notifies any PropertyChangeListeners that its state has shifted to SwingWorker.StateValue.STARTED.
  3. The doInBackground() method is executed on the worker thread.
  4. Once the doInBackground() method completes, the done() method is executed on the current thread.
  5. SwingWorker notifies any PropertyChangeListeners that its state has shifted to SwingWorker.StateValue.DONE.

While in the doInBackground() method, you can set an integer progress property that indicates the current progress of the worker thread using the following method:

     protected void setProgress(int progress);

Once this method is called, a property change event is fired by SwingWorker to all registered listeners to notify them of the updated progress value.

You can set or add to the final results of the worker thread using one of two methods:

     protected void process(V... chunks);
     protected void publish(V... chunks);

The latter of these methods, publish(), will take a variable number of objects of some intermediate type V and send them to the process() method for processing. You will typically call the process() method from the doInBackground() thread. The process() method should always be overridden to accept the incoming V parameters and concatenate these intermediate objects somehow into a type T. How the process() method accomplishes this task, of course, depends on the type parameters that are specified in your subclass of SwingWorker.

Meanwhile, on the current thread, you can call one of two get() methods to retrieve the results of the worker thread. The first method, get(), blocks indefinitely until the worker thread has completed. The second method will block for the specified amount of time before retrieving whatever results have been processed.

     public T get();
     public T get(long timeout, TimeUnit unit);

If you wish to cancel the worker thread before it has finished executing, you can call the cancel() method from the current thread.

     public final boolean cancel(boolean mayInterruptIfRunning)

Here, the mayInterruptIfRunning parameter specifies whether the thread executing this task should be interrupted in an attempt to stop the task. Note that calling the cancel() method will fail if the task has already completed, if it has already been cancelled, or if it could not be cancelled for some other reason. If, however, calling the method returns true and this task has not already started when the cancel() method is called, the SwingWorker should never execute.

Conclusion

Although these features are largely independent of each other, they represent the desire of the development team to address some of the smaller requests that the Java development community has made. In particular, learning to use the SwingWorker class is a must when creating Swing applications, and it relieves the programmer from troubleshooting complex GUI threading issues. As always, remember that these features are subject to the approval of the JSR 270 Expert Group before they can become part of the final version of Java SE 6.

For More Information

Download Java SE 6
Documentation for java.io.File
Documentation for javax.swing.JTabbedPane
Documentation for javax.swing.SwingWorker
Using the SwingWorker Class (in The Java Tutorial)
Download the TabbedPaneExample source as a NetBeans IDE project . If you are interested in only the source code, see the /src directory inside the distribution.
More information on desktop features in Java SE 6

Rate and Review
Tell us what you think of the content of this page.
Excellent   Good   Fair   Poor  
Comments:
Your email address (no reply is possible without an address):
Sun Privacy Policy

Note: We are not able to respond to all submitted comments.