|
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:
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.
 |
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 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.
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:
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:
- The
execute() method is called or run on the event
dispatch thread.
SwingWorker notifies any
PropertyChangeListeners that its state has shifted to
SwingWorker.StateValue.STARTED.
- The
doInBackground() method is executed on the
worker thread.
- Once the
doInBackground() method completes, the
done() method is executed on the current thread.
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
|