You are on page 1of 11

Core Java Technologies Tech Tips

Tips, Techniques, and Sample Code


Welcome to the Core Java Technologies Tech Tips for October 19,
2004. Here you'll get tips on using core Java technologies and
APIs, such as those in Java 2 Platform, Standard Edition (J2SE).
This issue covers:
* Queues and Delayed Processing
* Getting to Know Synth
These tips were developed using Java 2 Platform Standard
Edition Development Kit 5.0 (JDK 5.0). You can download JDK 5.0
at http://java.sun.com/j2se/1.5.0/download.jsp.
This issue of the Core Java Technologies Tech Tips is written by
John Zukowski, president of JZ Ventures, Inc.
(http://www.jzventures.com).
You can view this issue of the Tech Tips on the Web at
http://java.sun.com/developer/JDCTechTips/2004/tt1019.html
See the Subscribe/Unsubscribe note at the end of this newsletter
to subscribe to Tech Tips that focus on technologies and products
in other Java platforms.
For more Java technology content, visit these sites:
java.sun.com - The latest Java platform releases, tutorials, and
newsletters.
java.net - A web forum for collaborating and building solutions
together.
java.com - The marketplace for Java technology, applications and
services.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - QUEUES AND DELAYED PROCESSING
The J2SE 5.0 release adds a new top-level Queue interface to the
Collections Framework, to go along with the existing Map, List,
and Set interfaces. Typically, queues are a first in, first out
data structure, though for some implementations such as priority
queues, elements might not be added at the end of a queue. You
can think of a queue and its first in, first out structure
much like the line at a bank. A bank teller checks to see if
there are customers in the line. The teller then handles the
first customer in the line, processing any transactions for that
person. In doing so, the teller removes the customer from the
waiting line.
In addition to the new Queue interface in J2SE 5.0,there are
some new queue implementations. One such implementation is a
DelayQueue. In a DelayQueue, the items in the queue can't be
processed until a delay has happened. In this tip, you'll learn

about the new Queue interface and the DelayQueue implementation.


First, let's examine the Queue interface. This interface extends
Collection and adds its own series of five methods:
o
o
o
o
o

E element()
boolean offer(E o)
E peek()
E poll()
E remove()

As a result of the new support in J2SE 5.0 for generics


(http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html),
E here can be any data type, as specified by the type of
element defined when the Queue got created.
Although you can certainly use the Collection interface methods
for adding and removing elements from a Queue, it is recommended
that you limit yourself to those in the Queue interface. That's
because the methods enforce additional behavioral requirements.
For instance, instead of adding an element to the queue with the
Collection.add method, you offer it to the queue with the offer
method. Why the difference? An add operation can fail -- one
example is if a queue is bounded in size (using the bank line
analogy, there can't be more than 10 people waiting.) With the
add method of Collection, add fails by throwing an exception.
By comparison, the offer method "fails" by returning false. So
by using the offer method, you can save exception handling for
truly exceptional cases (especially since the exception thrown
is an unchecked runtime exception).
The other four methods of Queue can be described in pairs:
remove/poll and element/peek. Both the remove and poll methods
of Queue are used to remove the first element, or "head", of the
queue. The remove method throws an exception when it's called
with an empty collection. The poll method simply returns null in
this case. Instead of removing the head element, you can just
look at the element. That's where the element and peek methods
are used. Here, the element method throws an exception on an
empty queue, and peek returns null. Because queues are typically
used for processing tasks, having an empty queue isn't
necessarily an exceptional condition. As a result, the poll/peek
model tends to be the more appropriate model to follow.
Queue is typically used in a way similar to the following:
class Producer implements Runnable {
private final Queue queue;
Producer(Queue q) { queue = q; }
public void run() {
try {
while(true) { queue.offer(produce()); }
} catch (InterruptedException ex) { ... handle ...}
}
Object produce() { ... }
}
class Consumer implements Runnable {
private final Queue queue;
Consumer(Queue q) { queue = q; }

public void run() {


try {
Object o;
while((o = queue.poll()) != null) { consume(o); }
} catch (InterruptedException ex) { ... handle ...}
}
void consume(Object x) { ... }
}
One question you might have is what happens when the queue is
full (for the producer) or empty (for the consumer)? That
question is a good reason to start the discussion of another
new queue interface: BlockingQueue. Instead of using Queue to
blindly add elements (with offer) and remove them (with poll),
you can add elements with the put method of BlockingQueue and
remove them with the take method. Both put and take cause the
calling thread to block if a certain condition is true. For
put, the blocking condition is a full queue. For take, the
blocking condition is an empty queue.
The typical usage pattern for BlockingQueue is:
class Producer implements Runnable {
private final BlockingQueue queue;
Producer(BlockingQueue q) { queue = q; }
public void run() {
try {
while(true) { queue.put(produce()); }
} catch (InterruptedException ex) { ... handle ...}
}
Object produce() { ... }
}
class Consumer implements Runnable {
private final BlockingQueue queue;
Consumer(BlockingQueue q) { queue = q; }
public void run() {
try {
while(true) { consume(queue.take()); }
} catch (InterruptedException ex) { ... handle ...}
}
void consume(Object x) { ... }
}
Each created producer waits if put tries to add a newly produced
item to a full queue, and take waits until there is something
to take. The while(true) condition will never change if the
queue is empty.
DelayQueue is a concrete implementation of the BlockingQueue
interface. Items added to a DelayQueue must implement the new
Delayed interface, which has a single method:
long getDelay(TimeUnit unit). DelayQueue works as a time-based
scheduling queue that is backed by a priority heap data
structure. In other words, when you add an element to the queue,
you specify how long the queue must wait before the element can
be processed.
To demonstrate, the following program, DelayTest, provides an
implementation of the Delayed interface that works in seconds.

The key things to know are (1) a nanosecond is a billionth of


a second, and (2) there is a new method of System called
nanoTime that allows you to work in nanosecond units. Working
in nanoseconds is important because the getDelay method wants
the number of nanoseconds returned.
import
import
import
import

java.util.Random;
java.util.concurrent.Delayed;
java.util.concurrent.DelayQueue;
java.util.concurrent.TimeUnit;

public class DelayTest {


public static long BILLION = 1000000000;
static class SecondsDelayed implements Delayed {
long trigger;
String name;
SecondsDelayed(String name, long i) {
this.name = name;
trigger = System.nanoTime() + (i * BILLION);
}
public int compareTo(Delayed d) {
long i = trigger;
long j = ((SecondsDelayed)d).trigger;
int returnValue;
if (i < j) {
returnValue = -1;
} else if (i > j) {
returnValue = 1;
} else {
returnValue = 0;
}
return returnValue;
}
public boolean equals(Object other) {
return ((SecondsDelayed)other).trigger == trigger;
}
public long getDelay(TimeUnit unit) {
long n = trigger - System.nanoTime();
return unit.convert(n, TimeUnit.NANOSECONDS);
}
public long getTriggerTime() {
return trigger;
}
public String getName() {
return name;
}
public String toString() {
return name + " / " + String.valueOf(trigger);
}
}
public static void main(String args[])
throws InterruptedException {
Random random = new Random();
DelayQueue<SecondsDelayed> queue =
new DelayQueue<SecondsDelayed>();
for (int i=0; i < 10; i++) {
int delay = random.nextInt(10);
System.out.println("Delaying: " +
delay + " for loop " + i);
queue.add(new SecondsDelayed("loop " + i, delay));

}
long last = 0;
for (int i=0; i < 10; i++) {
SecondsDelayed delay = (SecondsDelayed)(queue.take());
String name = delay.getName();
long tt = delay.getTriggerTime();
if (i != 0) {
System.out.println("Delta: " +
(tt - last) / (double)BILLION);
}
System.out.println(name + " / Trigger time: " + tt);
last = tt;
}
}
}
The DelayTest program places ten elements in the DelayQueue
before processing them.
Here is the output for one run of DelayQueue:
Delaying: 8 for loop 0
Delaying: 7 for loop 1
Delaying: 2 for loop 2
Delaying: 4 for loop 3
Delaying: 0 for loop 4
Delaying: 9 for loop 5
Delaying: 3 for loop 6
Delaying: 4 for loop 7
Delaying: 6 for loop 8
Delaying: 2 for loop 9
loop 4 / Trigger time:
Delta: 1.9995545
loop 2 / Trigger time:
Delta: 0.0012475
loop 9 / Trigger time:
Delta: 0.9995177
loop 6 / Trigger time:
Delta: 0.9995187
loop 3 / Trigger time:
Delta: 6.408E-4
loop 7 / Trigger time:
Delta: 2.0001667
loop 8 / Trigger time:
Delta: 0.9986953
loop 1 / Trigger time:
Delta: 0.9995595
loop 0 / Trigger time:
Delta: 1.001262
loop 5 / Trigger time:

1883173869520000
1883175869074500
1883175870322000
1883176869839700
1883177869358400
1883177869999200
1883179870165900
1883180868861200
1883181868420700
1883182869682700

The output indicates that the item for loop 4 is set to start at
time 0, that is, without delay:
Delaying: 0 for loop 4
So it runs first:
loop 4 / Trigger time: 1883173869520000

Loop 2 has a 2 second delay:


Delaying: 2 for loop 2
So it comes in next:
loop 2 / Trigger time: 1883175869074500
The delta here is 1.9995545, which is roughly 2 seconds:
Delta: 1.9995545
Similar deltas are there for the other loops.
For a more real-world example, instead of just printing out
something when pulled from the DelayQueue, items in the queue
might implement Runnable and you would call their run method.
For additional information about Queue, DelayQueue, and other
changes to the Collections Framework with J2SE 5.0, see the
document "Collection Framework Enhancements"
(http://java.sun.com/j2se/1.5.0/docs/guide/collections/changes5.html).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - GETTING TO KNOW SYNTH
Synth is a new look and feel added to project Swing in J2SE 5.0.
Synth is a skinnable (that is, a customizable) look and feel,
where the "skin" (that is, the user interface) is controlled by
an XML file. Instead of customizing the look and feel by
providing default properties to the UIManager in a Properties
table, you load in an XML file with component definitions.
That means you can create a custom look without writing code.
To demonstrate, here's an example of using the Synth look and
feel:
import
import
import
import
import
import
import
import
import
import

java.awt.BorderLayout;
java.awt.EventQueue;
java.io.InputStream;
java.text.ParseException;
javax.swing.JButton;
javax.swing.JLabel;
javax.swing.JFrame;
static javax.swing.JFrame.*;
javax.swing.UIManager;
javax.swing.plaf.synth.SynthLookAndFeel;

public class HelloSynth {


public static void main(String args[]) {
Runnable runner = new Runnable() {
public void run() {
SynthLookAndFeel synth = new SynthLookAndFeel();
try {
Class aClass = HelloSynth.class;
InputStream is =
aClass.getResourceAsStream("synth.xml");
if (is == null) {
System.err.println("Missing configuration file");
System.exit(-1);

}
synth.load(is, aClass);
} catch (ParseException e) {
System.err.println("Bad configuration file");
System.exit(-2);
}
try {
UIManager.setLookAndFeel(synth);
} catch
(javax.swing.UnsupportedLookAndFeelException e) {
System.err.println("Cannot change to Synth");
System.exit(-3);
}
JFrame frame = new JFrame("First");
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
JLabel label = new JLabel("Hello, Synth");
label.setHorizontalAlignment(JLabel.CENTER);
frame.add(label);
frame.setSize(300, 100);
frame.setVisible(true);
}
};
EventQueue.invokeLater(runner);
}
}
The HelloSynth program creates a new SynthLookAndFeel object
and loads an XML file, synth.xml, with
synth.load(InputStream, Class). This means that you need the XML
file to complete this first example:
<synth>
<style id="label">
<font name="Monospaced" size="18" style="BOLD ITALIC"/>
<opaque value="true"/>
<state>
<color value="PINK" type="FOREGROUND"/>
<color value="YELLOW" type="BACKGROUND"/>
</state>
</style>
<bind style="label" type="region" key="Label"/>
</synth>
Run the HelloSynth program with the synth.xml file. The program
reads in the XML file, creates a JLabel, and adds it to the
screen. Notice the absence of any color or font configuring of
the label in the program. Instead, that's done through the XML
file. The synth.xml file configures the label as follows:
18-point bold-italic font, opaque, foreground color of pink
(text color), and background color of yellow. The HelloSynth
program then draws the label according to those specifications.
java HelloSynth synth.xml
If you don't like that color-font combination, you can change
the information in the XML file to produce something more
appealing. For example, you might change the font to bold, the
text color to red, and the background to black:
<synth>

<style id="label">
<opaque value="true"/>
<font name="Monospaced" size="18" style="BOLD"/>
<state>
<color value="RED" type="FOREGROUND"/>
<color value="BLACK" type="BACKGROUND"/>
</state>
</style>
<bind style="label" type="region" key="Label"/>
</synth>
By simply changing the XML file, and without
recompiling your program, you change the look produced by the
program.
java HelloSynth synth.xml
And that is the power of the Synth look and feel. It allows you
to change the look produced by the program without reprogramming.
The format of the XML file used with Synth is described in the
Synth File Format document
(http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/plaf/synth/doc-files/synthF
ileFormat.html).
Inside the outermost synth tag, you can have tags such as
<style>, <bind>, <font>, <color>, <imagePainter>, and <imageIcon>.
There are also special properties that you can set, these are
listed in the Component Specific Properties document
(http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/plaf/synth/doc-files/compon
entProperties.html).
Essentially, you define styles and bind them to components, as
shown in the previous examples.
The <style> element contains the most information. You can
configure numerous elements for each named style:
o
o
o
o
o
o
o
o
o
o

<property>
<defaultsProperty>
<state>
<font>
<painter>
<imagePainter>
<backgroundImage>
<opaque>
<imageIcon>
<inputMap>

The <font> tag allows you to specify the font's name, size, and
style. Supported styles are PLAIN, BOLD, and ITALIC. To get
both BOLD and ITALIC, provide both in a quoted string, with
a space between the two:
<font style="BOLD ITALIC"/>
The <state> tag can be more complex than shown in the examples.
For instance, if you describe a button, and you want a different
look when the mouse is over the button, you could configure a
MOUSE_OVER state, as follows:
<state value="MOUSE_OVER">

<font name="Serif" size="24" style="ITALIC"/>


</state>
This sets the font to a 24-point, italic, Serif font, at the
appropriate state change.
In addition to MOUSE_OVER, other available state settings are as
follows:
o
o
o
o
o
o

ENABLED
PRESSED
DISABLED
FOCUSED
SELECTED
DEFAULT

Depending on the component, you can configure any or all of


these seven states to have different properties. Be careful if
the state change causes a size change to the component. It will
not revalidate itself when the state changes.
You can also work with icons, using the imageIcon element. For
example, the following defines a style that changes the default
tree icons:
<style id="tree">
<imageIcon id="collapsedIcon" path="collapsed.png"/>
<property key="Tree.collapsedIcon" value="collapsedIcon"/>
<imageIcon id="expandedIcon" path="expanded.png"/>
<property key="Tree.expandedIcon" value="expandedIcon"/>
</style>
The <style> element in the previous example needs to be bound to
the tree components, so you need to specify a bind tag. The
collapsed.png and expanded.png image files are then located
relative to the Class object passed into the load method for the
SynthLookAndFeel.
Synth also allows you to embed object references in the
configuration file. Synth provides this through its support for
long-term persistence of JavaBeans components
(http://java.sun.com/products/jfc/tsc/articles/persistence3/).
This support allows you to embed any object reference, however
it's typically used to embed a reference to a Painter object.
For example, if you include the following element in a
configuration file:
<object id="gradient" class="GradientPainter"/>
it specifies a class named GradientPainter that is identified by
the gradient id. The class extends the SynthPainter class.
Then, if you include the following element:
<painter method="textFieldBackground" idref="gradient"/>
it specifies that the paintTextFieldBackground method of
GradientPainter (matching the idref of painter to the id of
object) should be called for the component to which this painter
is associated.

One thing Synth doesn't provide is a way to customize the feel


of a component. For example, if you don't like having to click
a button to select it, Synth doesn't provide a way to customize
the selection action -- perhaps to voice activation.
With little or no programming, you can now skin the look and
feel for your applications. By separating the look of an
application from its internal logic, graphic design and
programming tasks can be better separated, leading to better
designed programs that are more easily maintained. When
appropriate, consider switching to the Synth look and feel for
both new and existing applications.
You can learn more about Synth in the article "The Synth Look
and Feel" (http://www.javadesktop.org/articles/synth/).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OTHER RESOURCES
Got a question about Java technologies, tools, or resources? Get
answers at the following event:
Chat: Oct. 26th at 11:00 A.M. PDT/18:00 UTC
Project Looking Glass
Guests: Hideya Kawahara, Paul Byrne, and Deron Johnson
(http://java.sun.com/developer/community/chat/)
. . . . . . . . . . . . . . . . . . . . . . .
IMPORTANT: Please read our Terms of Use, Privacy, and Licensing
policies:
http://www.sun.com/share/text/termsofuse.html
http://www.sun.com/privacy/
http://developers.sun.com/dispatcher.jsp?uid=6910008
* FEEDBACK
Comments? Please enter your feedback on the Tech Tips at:
http://developers.sun.com/contact/feedback.jsp?category=sdn
* SUBSCRIBE/UNSUBSCRIBE
Subscribe to other Java developer Tech Tips:
- Enterprise Java Technologies Tech Tips. Get tips on using
enterprise Java technologies and APIs, such as those in the
Java 2 Platform, Enterprise Edition (J2EE).
- Wireless Developer Tech Tips. Get tips on using wireless
Java technologies and APIs, such as those in the Java 2
Platform, Micro Edition (J2ME).
To subscribe to these and other JDC publications:
- Go to the JDC Newsletters and Publications page,
(http://developer.java.sun.com/subscription/),
choose the newsletters you want to subscribe to and click
"Update".
- To unsubscribe, go to the subscriptions page,
(http://developer.java.sun.com/subscription/),

uncheck the appropriate checkbox, and click "Update".


- To use our one-click unsubscribe facility, see the link at
the end of this email:
- ARCHIVES
You'll find the Core Java Technologies Tech Tips archives at:
http://java.sun.com/developer/TechTips/index.html
- COPYRIGHT
Copyright 2004 Sun Microsystems, Inc. All rights reserved.
4150 Network Circle, Santa Clara, California 95054 USA.
This document is protected by copyright. For more information, see:
http://java.sun.com/jdc/copyright.html
Core Java Technologies Tech Tips
October 19, 2004
Trademark Information: http://www.sun.com/suntrademarks/
Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks
or registered trademarks of Sun Microsystems, Inc. in the
United States and other countries.

You might also like