Thursday, April 10, 2008

Enhancements in Java SE 6

Java SE 6 contains a number of features that make programming easier with Java technology. In this article, we discuss four new features that allow us 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 Java Foundation Classes/Swing (JFC/Swing) applications

Setting File and Directory Permissions:
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)

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
Java SE 6 gives us 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, "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.


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

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.

//code of TabbedPane
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. JTabbedPane With Several JComponents as Tabs

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 non responsive 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 declaration of SwingWorker from the Javadocs.

public abstract class SwingWorker extends Object
implements RunnableFuture

Note that the declaration contains two generic type variables: T and V. [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 Java development.

1 comment:

Shanthi said...

The article is very useful for faculties. They can discuss about the features of java6 while teaching.

I would like to know whether the articles will be available on request. If yes, I would like to know about the applet communication.

Thank you.
Shanthi