No unmet needs exist, and current unmet needs that are being met will continue to be met. | ||
--Transportation Commission on Unmet Needs, California |
The Java™ 2 Platform Standard Edition comes with many standard packages. These packages, all subpackages of the root java
package, define the main classes and interfaces for the platform. Throughout this book we have taught you about many of these classes, particularly those relating to the core language and those most commonly used in general programs. The main packages that we have covered are
java.lang
—. The main language classes, such as Object
, String
, Thread
, Class
, and so on. The subpackage java.lang.annotation
defines some annotation types and was covered in Chapter 15. The subpackage java.lang.reflect
provides a way to examine types in detail and was covered in Chapter 16. The subpackage java.lang.ref
defines the weak reference types that allow you to influence garbage collection and was covered in Chapter 17.
java.io
—. Input and output and some file system manipulation. This package was covered extensively in Chapter 20.
java.util
—. Classes of general utility. Defines the collection classes both new and legacy that were covered in Chapter 21 and the localization classes covered in Chapter 24. Miscellaneous utilities were covered in Chapter 22.
java.util.regex
—. Classes for regular expressions. These were covered in Chapter 13.
java.security
—. The platform security classes, which were briefly described in Chapter 23. It contains a number of other classes for encryption, authentication, digital signatures, and other useful security-related code.
java.text
—. Internationalization and localization for formatting and parsing numbers and dates, sorting strings, and message lookup by key. These were touched on briefly in Chapter 24.
We mentioned these main packages briefly, without covering them in detail:
java.nio
—. The “New I/O” classes that we briefly introduced at the end of Chapter 20. The CharBuffer
class from this package also pops up in a couple of other places.
java.nio.charset
—. Classes for defining character sets and their encodings. This package was briefly covered in Chapter 13.
java.util.concurrent
—. Contains (amongst other things) the concurrent collections that were briefly discussed in Chapter 21.
In addition, there are main packages we haven't covered at all:
java.awt
—. The Abstract Window Toolkit abstraction layer for writing platform-independent graphical user interfaces.
java.applet
—. The Applet
class and related types for writing subprograms that can be hosted inside other applications, such as HTML
browsers.
java.beans
—. The JavaBeans components for user-composable code.
java.lang.instrument
—. Services for defining agents that can instrument applications running on a virtual machine.
java.lang.management
—. Services for monitoring and managing both the virtual machine and the operating system on which it runs.
java.math
—. Mathematical manipulations. Currently, this package has only three classes, which handle some kinds of arbitrary-precision arithmetic.
java.net
—. Networking classes for sockets, URL
s, and so on.
java.rmi
—. Remote Method Invocation, with which you can invoke methods on objects running in other virtual machines, typically across a network.
java.sql
—. The JDBC
package for using relational databases.
java.util.jar
—. Classes for reading and writing JAR
(Java ARchive) files. These archive files can be used to distribute packages of related classes and resources, as covered in “Package Objects and Specifications” on page 477.
java.util.logging
—. A framework for logging from within your code.
java.util.prefs
—. Provides a mechanism for managing user and system preferences, as well as application configuration.
java.util.zip
—. Classes for reading and writing ZIP
files.
Further packages exist outside the java
package of the platform, in what are known as the standard extensions, for example:
javax.accessibility
—. A framework for developing GUI
s that are more accessible to people with disabilities.
javax.naming
—. Classes and subpackages for working with directory and naming services.
javax.sound
—. Has subpackages for creating and manipulating sounds.
javax.swing
—. The Swing package of GUI
components.
And there are packages that provide interaction between the platform and other environments:
org.omg.CORBA
—. Classes for the Common Object Request Broker Architecture (CORBA
), object request brokers (ORB
's).
org.omg.CosNaming
—. The Common Object Service's naming classes.
Other versions of the platform, such as the Java™ 2 Platform Enterprise Edition, contain even more packages.
This book cannot be large enough to contain full coverage of every one of these packages—some of them require complete books of their own. This chapter discusses some of the java
subpackages not otherwise covered in this book, giving an overview of the purpose and contents, as well as some of the other packages we have listed. All these packages are covered in detail in your local documentation and the java
packages are covered in the volumes of The Java™ Class Libraries, Second Edition. Most of the java
packages, including the AWT
and applet packages and some extensions such as Swing, are taught in the different versions of The Java™ Tutorial. These books and all others cited in this chapter are part of this official series of documentation from the source—the people who invented the Java programming language, its virtual machine, and many of its packages.
The Abstract Window Toolkit allows you to write graphical user interfaces (GUI
s) that will run on every system in a reasonable way. The AWT
displays GUI
components (such as buttons, labels, and text fields) using (by default) the local platform's look and feel, showing Macintosh buttons on a Mac, Motif buttons on X platforms, Windows buttons on Windows systems, and so on.
For this to work, you may need to change how you think about laying out your GUI
. You may be accustomed to interactive tools that let you place the various GUI
components on the screen exactly where you want them to be. Such absolute placement will not work for a portable interface because, for example, the size of a button is different on different systems. When your interface is used on a system other than the one on which you designed it, some buttons will overlap and others will have ugly gaps between them.
Although you can use absolute placement in AWT
, it is not recommended. When you place a component into an AWT
display frame, the frame's layout manager decides where to put it. Almost all the layout managers provided with the AWT
use relative placement: Components are placed and sized relative to other components. All the provided layout managers implement either the interface LayoutManager
or its extended interface LayoutManager2
. Layout managers range from the simple (FlowLayout
adds components to a line until they don't fit and then starts a new line) to the sophisticated (GridBagLayout
has a great deal of flexibility). You can also write your own layout manager.
Instead of thinking about where a check box should go on the screen, you should think about how it should be placed relative to other components. Then choose a layout manager for the frame in which the check box will be placed and add the components so that they have the expected relationships. If you do this right, your interface will look clean on all platforms.
AWT
has a set of standard GUI
components: labels, buttons, check boxes, choice lists, scroll bars, text fields, text regions, and so on. Several top-level containers, such as dialog boxes and windows, let you place other components inside them (preferably using a relative-placement layout manager).
When you need to draw your own graphics on the screen, the simplest technique is to subclass Component
or Container
, overriding the paint
method to draw what you need using the Graphics
object passed to paint
. Component
is the basis for many customized user-interface components when no standard component does what is needed. You would subclass Container
if your drawn component contained other components.
The AWT
is an event-based system. When the user performs an action, such as moving a mouse, clicking a mouse button, or typing a key, an event is generated that identifies the underlying component as the source of that event and that encapsulates the nature of that event. The event is then placed in a central event queue, and will be serviced by a single event thread. The event thread takes events from the queue one at a time and dispatches them to their source components. You can also generate events programmatically—for example, invoking repaint
on a component puts an UPDATE
event into the event queue—or can create event objects and insert them directly into the event queue.
Components are notified of events (mouse up, mouse down, mouse drag, keyboard events, and the like) by having methods invoked on them by the event thread. You can also register listener objects that are notified when an event occurs within a component. For example, a button that lets the user exit the program might be set up like this:
Button b = new Button("Exit"); b.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); } } ); gui.add(b);
Action events come from the basic action on the component, such as pressing a button or selecting a check box. The ActionEvent
class has details of an action event, such as the keyboard modifiers (such as the Alt or Control key) that were being pressed when the action occurred. To receive this event you must register interest in action events, either by adding an ActionListener
to the component as shown here or by invoking enableEvents
with an appropriate mask.
The ActionListener
interface in the previous example is a listener interface. Listener interfaces extend java.util.EventListener
and exist for many kinds of events: mouse, keyboard, window, item, text, container, and general component events. You use objects that implement listener interfaces to trigger method execution when events occur—all of which get executed by the event thread. When an event occurs, the component's processEvent
method is called, which in turn calls processXXXEvent
for the specific event, and that in turn will call the XXXOccurred
method of all registered listeners for that event.
Other classes in java.awt
allow you to set colors, fonts, and so on. All these properties are inherited by default from outer components—the default colors of a button are those of the frame in which it has been placed. You can override this setting at any level by explicitly specifying a color for a component. That component and all its contained components will change to the specified color unless a subcomponent has its color specified.
Various subpackages of java.awt
allow you to manipulate images, sounds, and other media:
java.awt.color
—. color manipulation tools
java.awt.datatransfer
—. moving data between applications, such as with cut, copy, and paste
java.awt.dnd
—. drag-and-drop functionality
java.awt.event
—. various event listener and adapter classes for connecting events to your code
java.awt.font
—. font manipulation API
s
java.awt.geom
—. 2D geometry
java.awt.im
—. input method interfaces for localized input devices
java.awt.image
—. several interfaces and classes for reading and manipulating images
java.awt.print
—. printing API
s
Applets are a way to run code inside another application—commonly a web browser. Applets are the first exposure many people have to the Java virtual machine and its uses. An applet is defined primarily by the protocol that governs its lifetime and the methods by which it can query and manipulate its runtime environment. The types in java.applet
—primarily the Applet
superclass itself—define this environment.
When an <applet>
or <object>
tag is found in a web page's HTML
, the browser downloads the code for the named class from a URL
, creates an object of that class, creates a region on the web page for that object to control, and then invokes the object's init
method—often creating a ThreadGrou
p and Thread
just for this applet. As the applet runs, it can download other classes from the server as needed. Applets are usually run in a tightly secured “sandbox” in which potentially dangerous operations (such as file access, network access, and running local programs) are restricted by the security policy.
The method init
is one of four lifecycle methods defined for the applet in the Applet
class. After init
is called, start
is called, which tells the applet that it is active and should perform its function. If the browser decides that the applet is no longer of interest to the user, for example, the user leaves the page the applet was on, the applet's stop
method is invoked to tell it to stop what it was doing. If the user returns to the applet's page the browser may invoke start
again to tell the applet to recommence—so the applet can cycle through a stop–start sequence as the user presses the “Back” and “Forward” buttons (or their equivalents) to visit and leave the page. When the browser decides that the applet is no longer of interest (perhaps because the user has moved a number of pages away) the applet's destroy
method is invoked to free up any resources used by the applet. If the applet's page is now revisited the lifecycle will recommence with init
being called again. The browser ultimately defines the lifecycle of an applet and may choose to invoke destroy
immediately after stop
when the user leaves the page. Whether or not an applet class is unloaded and reloaded between visits to the applet is again a feature of the browser.
These lifecycle methods are typically overridden by applet classes. For example, if an applet uses a thread to do its work, init
would typically create the thread; start
would invoke the thread's start
method the first time and ask it to continue execution on subsequent invocations; stop
could ask the thread to pause to prevent it from consuming resources while the page is not visible; and destroy
could interrupt the thread because the thread would no longer be needed.
An applet can get parameters from a <param>
tag to customize its behavior. It might get colors, fonts, or the URL
of an image to display.
Applets usually run in a highly constrained security environment to protect your computer and network from unwelcome inspection or invasion by a hostile applet. This means that certain conveniences, such as a local scratch disk, may not be available by default. You can grant permission to perform particular actions to individual applets by specifying an appropriate security policy.
The applet model is a good example of how the Java platform provides power. The fact that the same code runs on all systems in the same way allows a single piece of code (an applet) to run in a variety of browsers on a variety of windowing systems running on a larger variety of operating systems. The portability of Java bytecodes allows you to execute part of your application on the server and another part on the client system via downloaded code, whichever is appropriate. It is the same platform on both sides: the Java virtual machine. The ability to move code from one place to another and execute it in a secure environment enables new ways of thinking about where to execute what part of your design.
The JavaBeans™ component architecture helps independent vendors write classes that can be treated as components of larger systems assembled by users. The java.beans
package provides necessary and useful classes for writing such beans. A bean exports properties, generates events, and implements methods. By following certain design patterns or by implementing methods that provide a description of these facets of behavior, you can compose beans by using interactive tools to build a system the user needs.
Much of a bean's behavior is simplified if you follow expected design patterns. For example, if your bean class is called Ernest
and you provide a class named ErnestBeanInfo
that implements the BeanInfo
interface, the JavaBeans tools will use ErnestBeanInfo
as a source of information about the behavior of the bean: the events it supports, the icons it uses, and so on.
Providing a BeanInfo
object is itself optional—the JavaBeans system will use reflection to infer events and properties. For example, if a class Ernest
has methods named getImportance
and setImportance
, the JavaBeans system will assume that you have an importance
property that can be set, either directly or via another bean. Builder tools are expected to present the properties and events to users, who can use them to connect beans as components to build custom applications.
AWT
components are beans, and the event model described earlier for AWT
components is also the JavaBeans event model.
The JavaBeans component architecture is designed to interoperate with existing component architectures, extending the “Write Once, Run Anywhere” capability to create a homogeneous component platform.
The subpackage java.beans.beancontext
defines interfaces and classes that are used to talk about the context in which a bean or set of beans is executing. Bean contexts can be nested.
The package java.math
is destined for classes that help with mathematical calculations. Currently, it has three classes—BigInteger
, BigDecimal
and MathContext
—and an enum, RoundingMode
, that defines different rounding modes.
The class BigInteger
provides arbitrary-precision integer arithmetic, providing analogous operations for all integer operations except >>>
, which is equivalent to >>
because there is no sign bit to copy in an arbitrary-precision integer. Neither will the provided single-bit operations (clearBit
and setBit
) change the sign of the number on which they operate. BigInteger
behaves as if it were defined in two's-complement notation—the same notation used by the primitive integer types. Binary bitwise operations start by extending the sign of the smaller number and then executing the operation, just as operations on primitive values do. BigInteger
objects are immutable, so all operations on them produce new BigInteger
objects. The following simple method returns an iterator over the prime factors of a number:
static final BigInteger ONE = BigInteger.valueOf(1); static final BigInteger TWO = BigInteger.valueOf(2); static final BigInteger THREE = BigInteger.valueOf(3); public static Iterator<BigInteger> factors(BigInteger num) { ArrayList<BigInteger> factors = new ArrayList<BigInteger>(); if (num.compareTo(ONE) <= 0) { // num<=ONE means skip it factors.add(num); return factors.iterator(); } BigInteger div = TWO; // divisor BigInteger divsq = BigInteger.valueOf(4); // div squared while (num.compareTo(divsq) >= 0) { BigInteger[] res = num.divideAndRemainder(div); if (res[1].signum() == 0) { // if remainder is zero factors.add(div); num = res[0]; } else { // try next divisor if (div == TWO) div = THREE; else div = div.add(TWO); divsq = div.multiply(div); } } if (!num.equals(ONE)) // leftover must be a factor factors.add(num); return factors.iterator(); }
The constants ONE
, TWO
, and THREE
are used often, so we create objects for them once. If the number we are factoring is less than or equal to one, we treat it as its own factor (BigInteger
and BigDecimal
implement the Comparable
interface described on page 574). After this validity test we perform the real work of the method: testing potential divisors. If a divisor divides into the number evenly, it is a prime factor, and we proceed with the result of the division to find more factors. We first try two, and then all odd numbers, until we reach a divisor whose square is larger than the current number. You could optimize this method in any number of ways, but it shows how to use some BigInteger
functionality.
BigDecimal
provides an arbitrary-precision signed decimal number consisting of an arbitrary-precision integer and an int
scale that says how many decimal places are to the right of the decimal point. The actual precision required can be set through a MathContext
object that is either associated with the BigDecimal
at construction time or passed as an argument to the actual operation being performed.
Decimal division and changing a number's scale require rounding, which you can specify on each operation or through an associated MathContext
object. You can require that rounding be up, down, toward zero, away from zero, or toward the nearest value. You can also assert that no rounding will be necessary, in which case ArithmeticException
will be thrown if you are found to be wrong. The eight different rounding modes are defined by the RoundingMode
enum.
The java.net
package provides classes for working with network infrastructure, such as sockets, network addresses, Uniform Resource Identifiers (URI
s), and Uniform Resource Locators (URL
s).
The java.net
package is centered around the Socket
class, which represents a connection to another socket—possibly on another machine—across which bytes can flow. You typically create a socket with a host name or InetAddress
and a port number. You can also specify a local InetAddress
and port to which the socket will be bound. A ServerSocket
class lets you listen on a port for incoming connection requests, creating a socket for each request. For example, the following program accepts input on a socket:
import java.net.*; import java.io.*; public class AcceptInput { public static final int PORT = 0xCAFE; public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(PORT); byte[] bytes = new byte[1024]; for (;;) { try { System.out.println("---------------------"); Socket sock = server.accept(); InputStream in = sock.getInputStream(); int len; while ((len = in.read(bytes)) > 0) System.out.write(bytes, 0, len); in.close(); } catch (IOException e) { e.printStackTrace(); } } } }
This program creates a ServerSocket
and repeatedly accepts connections to it. The Socket
obtained for each connection is queried for an input stream and the data from that socket is read from the stream and written to the standard output stream, printing whatever bytes it receives. A client that shipped its standard input to the server might look like this:
import java.net.*; import java.io.*; public class WriteOutput { public static void main(String[] args) throws IOException { String host = args[0]; Socket sock = new Socket(host, AcceptInput.PORT); OutputStream out = sock.getOutputStream(); int ch; while ((ch = System.in.read()) != -1) out.write(ch); out.close(); } }
Once we have the actual Socket
connection, I/O is performed, like all other I/O, using an appropriate input or output stream.
A URL
object stores a URL
, providing methods to examine and set its various parts (protocol, host, port number, and file). A URL
object simply names a resource—you invoke openConnection
to connect to the named resource. The openConnection
method returns a URLConnection
object that lets you get the header fields and content of the resource as input and output streams. The following program reads the contents of a URL
:
import java.net.*; import java.io.*; public class ReadURL { public static void main(String[] args) { for (String url : args) { try { readURL(url); } catch (Exception e) { System.err.println(url + ":"); e.printStackTrace(); } } } private static void readURL(String name) throws MalformedURLException, IOException { URL url = new URL(name); URLConnection connect = url.openConnection(); InputStream in = connect.getInputStream(); byte[] bytes = new byte[1024]; int len; // number of bytes actually read while ((len = in.read(bytes)) >= 0) System.out.write(bytes, 0, len); } }
The URLEncoder
class lets you turn an ISO
Latin-1 string (such as a user-typed query) into a form that can be included as part of a URL
. ASCII
letters and digits remain unchanged, the space character is converted to a +
, and all other characters are represented by their lower 8 bits in hex preceded by a %
.
The File.toURL
method creates a file URL
for the path specified by that File
object.
You can create DatagramSocket
objects for sockets that send and receive DatagramPacket
objects, which contain an array of bytes. Datagrams are a connectionless packet delivery service, in which packets are individually addressed and routed and could be received in any order. With the MulticastSocket
subclass of DatagramSocket
, you can set up a socket that can send and receive packets to and from multiple addresses.
When you can download and run code on other systems, the face of distributed computing changes. The Java platform's Remote Method Invocation (RMI
) gives you a way to create objects whose methods can be invoked from other virtual machines, including those running on completely different hosts. Because RMI
is designed for communicating between Java virtual machines it can take advantage of the architecture's features and can operate in a natural fashion for programmers. RMI
is contained in the package java.rmi
and its subpackages, most notably the package java.rmi.server
for RMI
server implementations.
To use RMI
you must first design one or more remote interfaces—interfaces whose methods can be invoked remotely. A remote interface extends the Remote
interface, and its methods throw RemoteException
in addition to any other exceptions. Here, for example, is a simple definition of a compute server interface that will accept Task
objects for execution, returning the resulting Object
:
import java.rmi.*; public interface ComputeServer extends Remote { Object compute(Task task) throws RemoteException; }
The Task
interface is generic, allowing a ComputeServer
to do any computation requested of it:
public interface Task extends java.io.Serializable { Object run(); }
Task
itself is not a remote interface. Each ComputeServer
object will run on a host and will be asked to execute tasks locally on its own host and return any results. (Traditionally, the application invoking a remote method is called the client of the invocation, and the application executing the method is called the server, although a client of one invocation may be the server of another.) Task
extends Serializable
because all local types passed to or returned from a remote method must be serializable.
Each method of a remote interface is required to throw RemoteException
because any invocation of a remote method can have failures that must be signaled. Recovery from these failures is unlike recovery in local computation, where methods always arrive at their intended objects and either results are always returned or you clearly know that they were not returned.
When you invoke a remote method, network failures create new uncertainties—if the network fails after the request is transmitted from the client, the client doesn't know whether the request was received. The failure may have happened before the invocation reached the server, or it may have happened after it reached the server but before results could be returned. There is no way for the client to know which of these two cases actually happened. If the request is to withdraw money from your bank account, you would not want to simply retransmit the request—it might get there twice. Remote interfaces must be designed to allow clients to recover from such partial failures. Methods may be idempotent—meaning the method can safely be reinvoked—or some other recovery method may be provided for the interface, such as transactions that can be aborted when non-idempotent methods fail. The users of a remote object must recover from such failures, and the declared RemoteException
helps them do so.
Here is a simple implementation of the ComputeServer
interface:
import java.rmi.*; import java.rmi.server.*; public class ComputeServerImpl extends UnicastRemoteObject implements ComputeServer { public ComputeServerImpl() throws RemoteException { } public Object compute(Task task) { return task.run(); } public static void main(String[] args) throws java.io.IOException { // use the default, restrictive security manager System.setSecurityManager(new RMISecurityManager()); ComputeServer server = new ComputeServerImpl(); Naming.rebind("ComputeServer", server); System.out.println("Ready to receive tasks"); } }
This code is also straightforward. When a compute
invocation arrives from a client, ComputeServerImpl
implements the ComputeServer
interface by taking the Task
object it is given and invoking its run
method, returning the resulting Object
. Each incoming request typically gets its own thread, so this compute server implementation could have many concurrently executing tasks for different clients. ComputeServerImpl
extends UnicastRemoteObject
, which provides references for single-server remote method invocation. UnicastRemoteObject
, like most types needed only by servers, is defined in java.rmi.server
. The ComputeServerImpl
constructor declares that it throws RemoteException
because it can be thrown by the (implicitly invoked) UnicastRemoteObject
constructor when it registers the object with the RMI
system.
Now comes the fun part. Clients can ask the server to perform any computation at all. Suppose, for example, that you want to compute π to some number of decimal places. You can have the compute server do this for you:
import java.math.BigDecimal; public class Pi implements Task { private int decimals; /** Calculate Pi to a given number of decimal places */ public Pi(int decimals) { this.decimals = decimals; } public Object run() { BigDecimal res = computePi(); return res; } BigDecimal computePi() { // ... } }
The Pi
class implements the Task
interface with a run
method that returns a java.math.BigDecimal
object containing the computed result. You then put the compiled Pi
class someplace where it can be downloaded from a URL
, just as an applet class would be. The URL
from which to download the code will be set as a property for your client.
When you invoke the compute server's compute
method, passing a Pi
object, the server will need the Pi
class. It will look at the URL
that has been implicitly passed with the request, download the class, and run it in a secure sandbox. It will then invoke the Pi
object's run
method and return the result.
This ComputeServer
example leverages the Java virtual machine's homogeneous computing model, in which all code means the same thing on all platforms, and its security model, in which downloaded code can be run securely.
Many environments can use the basic infrastructure just described. A compute farm that uses a large number of computers to render animation can use such a system to feed those computers images to be rendered, sound computations, and other tasks. This simple example needs some hardening to be used in other situations, especially when Task
objects could not be trusted to be friendly. All you need is a Java virtual machine at both ends of the network.
UnicastRemoteObject
references are for remote services that are started by the user or system. You can use java.rmi.activation.Activatable
for references to remote objects that you want to be started automatically when they are first needed.
Because of the dangers inherent in running downloaded code, RMI
requires that a security manager be installed. The RMISecurityManager
class is a very conservative security manager that you can install to prevent unauthorized access to your system, or you can provide your own security manager.
Your server class may not be able to extend an RMI
server class because your class must extend a different class. For example, an applet class must extend Applet
and so cannot extend UnicastRemoteObject
. You can create servers that do not extend UnicastRemoteObject
by having your class's constructor invoke a static method:
public class SnazzyApplet extends Applet implements SnazzyRemote { public SnazzyApplet() throws RemoteException { UnicastRemoteObject.exportObject(this); } // ... implement SnazzyRemote and Applet methods ... }
Classes are downloaded from client to server (or server to client) only if they are needed. Classes will often be known on both sides. For example, BigDecimal
is part of the core, so both the client and the server already know about it and it will not be downloaded. User-defined classes are treated in the same way—if both the client and server have a class installed, it will not be downloaded across the network.
Arguments and return values of remote methods are handled somewhat differently from those of local methods. RMI
passes arguments and return values by using object serialization (see page 549). When a remote reference is passed as an argument or return value, the receiver will get a reference to the same remote object that was passed. This is how local references act in local methods—an object reference refers to the same object in both the invoking code and the invoked method. Primitive types, too, are passed in the same way locally and remotely; the receiver gets a copy of the value.
References to local objects must be passed differently. Local objects are not designed to deal with partial failures, so it is not possible to pass across the network a remote reference to a local object. Local objects are instead passed by a deep copy made by serialization. Any remote references contained within the object or in any part of the graph of objects it denotes will be passed as described earlier. All other parts of the graph will be serialized on the sender's system and deserialized on the receiver's. Changes made on one side will not be visible to the other side, because each side has its own local copy of the object.
The RMI
registry provides a simple naming system to store remote references for bootstrapping your clients. This is not a full naming system, but it will let you store objects that register or find top-level services. The registry is accessed via the Naming
class.
Server objects are governed by a “best effort” distributed garbage collector. When no outstanding references to a remote object exist, that object can be collected. This is similar in principle to garbage collection for local objects, but the failures of distributed computing make surety impossible. If a client hasn't been in contact for a long time, it is presumed to have gone away without notifying the server (possibly the system crashed). If a long-lived network failure is actually at fault, the client may find that the server has been garbage-collected when the network reconnects. Every reasonable effort is made to preclude this possibility. A server can ensure that it will never be garbage-collected by simply holding on to a remote reference to itself, thus ensuring that at least one remote reference will keep it alive. This reference can be dropped when the server decides that it no longer must be forced to be alive.
RMI
is explicitly designed to take advantage of having both client and server in the Java programming language. This gives it a form that is simple and direct for people who know the language. You can, of course, implement any server methods you like as native
methods. Native methods can help you use RMI
as a bridge to existing native code, such as in two- and three-tier systems.
The subpackage java.rmi.activation
provides for activatable servers, which will be started when they receive messages if they are not already running. Multiple servers can be put in the same activation group, and will then be run in the same virtual machine.
The security architecture that was introduced in Chapter 23 is quite extensive and incorporates a range of mechanisms for encryption, authorization, authentication, and so forth. These mechanisms are spread across a number of packages.
The package java.security
contains several useful tools for security-related functions: digital signatures, message digests, key management, and cryptographic keys. Subpackages define abstractions for certificates (java.security.cert
), RSA
and DSA
keys (java.security.interfaces
), and key and algorithm parameter specifications (java.security.spec
).
The javax.security
subpackages complement these tools with a full authentication and authorization framework (javax.security.auth
and its subpackages), including support for the Simple Authentication and Security Layer (SASL
) as defined by RFC
2222 (the javax.security.sasl
package). The authorization component allows specification of access controls based on code location, code signers, and code executors (subjects), using common protocols such as Kerberos and X500.
The javax.crypto
package and subpackages (interfaces
and spec
) provide rich mechanisms for cryptography, including encryption with various kinds of ciphers, MAC
generation, and key creation and agreement.
The org.ietf.jgss
package provides a framework that helps you use security services such as authentication, data integrity, and data confidentiality from a variety of underlying security mechanisms. The security mechanisms an application can choose are identified with unique object identifiers. For example, the Kerberos v5 GSS
-API
mechanism has the object identifier 1.2.840.113554.1.2.2. This mechanism is available through the default instance of the GSSManager
class.
Because there are many ways to approach things like cryptography and authentication, and there will be many more in the future, the security architecture provides abstractions of security interactions. Implementations of the abstractions are supplied by providers. Each platform has one or more providers. The java.security.Security
utility class provides methods to find out which providers are available. Providers can interoperate through the provided abstractions.
Much more information on these packages, and on security in general in the virtual machine, is in Inside Java™ 2 Platform Security, Second Edition.
The package java.sql
provides the Java Database Connectivity™ (JDBC)
package for using relational databases. These classes and methods can be implemented directly by your database vendor or through the industry-standard Open Database Connectivity (ODBC
) interface. JDBC
is, in effect, a mapping of ODBC
. The JDBC
package and other issues about relational databases are covered in JDBC™ API
Tutorial and Reference, Third Edition.
Some of the utility classes in java.util
are not independent classes but groups of related classes that together provide a particular functionality. These classes are themselves grouped into subpackages within java.util
.[1]
The package java.util.concurrent
, together with its subpackages provides a range of utilities for writing efficient multithreaded applications. These utilities expand the in-built synchronization facilities of the Java programming language to allow building concurrent applications that would not otherwise be possible with synchronized code, and to simplify common synchronization tasks that would otherwise be tedious and error-prone to write using the language facilities. The utilities provided can be divided into five categories:
Concurrent collections—. These were described in Chapter 21.
Synchronizers—. A group of utility classes that simplify common synchronization tasks (discussed below).
Executor framework—. A framework for asynchronous task execution, including a flexible, high-performance thread pool implementation.
Locks and Conditions—. Classes that provide locking and waiting capabilities that go beyond those of the built-in monitor capabilities.
Atomic variables—. A set of classes that allow safe expression of lock-free and wait-free algorithms within an application.
There are four synchronizer classes:
Semaphore
—. A counting semaphore. Useful for many situations in which access to a resource must be controlled. To use the resource a thread must acquire a (conceptual) permit, or token, from the semaphore. If none is available then the thread blocks until one is. When finished with the resource the thread releases the permit back to the semaphore to allow other threads to proceed. A counting semaphore can hold an arbitrary number of permits; a binary semaphore is a semaphore with only one permit.
CountDownLatch
—. A utility for blocking until a given number of events have occurred or until certain conditions hold. The latch is created with a given number of events to expect. Each thread that triggers an event or sets a condition calls the countDown
method which decrements the initial count. A thread that calls the await
method will block until the latch has counted down to zero.
CyclicBarrier
—. A utility that forces all threads using the barrier to wait until all those threads are ready to pass the barrier. This is useful for multi-phase algorithms in which all threads must complete a given phase before any threads commence the next phase.
Exchanger
—. A simple utility that allows two threads to exchange data. The first thread to arrive at the exchanger waits until the other arrives, they then exchange data and proceed.
An Executor
knows how to execute a task—which is represented by a Runnable
object—and replaces the direct creation of new threads within an application. Depending on the implementation used, the Executor
may execute the task in the current thread, submit the task to a thread pool for later execution, or perhaps create a new thread to execute the task. The Executor
abstracts all the details of thread creation and management and allows the problem of executing tasks to be separated from the problem of generating tasks. An ExecutorService
expands on the basic executor to provide lifecycle management—such as shutting down an executor. Particular implementations of ExecutorService
provide thread pools (ThreadPoolExecutor
) and permit the scheduling of tasks in the future (ScheduledThreadPoolExecutor
).
The Callable
interface defines a task that can return a result (unlike a Runnable
). A Future
represents a computation—such as that in a Callable
—that may not yet have completed or even begun. You can submit a Future
to an Executor
(using the concrete FutureTask
class) knowing that at some time in the future the result of the computation will be available. You can use the Future
to ask whether the task has completed or to block until the task completes and the result is available.
The java.util.concurrent.locks
subpackage defines the Lock
and Condition
interfaces and the ReentrantLock
implementation. Lock
abstracts the functionality of locking, as implied in the use of synchronized
blocks and methods, to allow more flexible locking. A Lock
can be locked in one method and unlocked in a different one—something that is needed in some concurrent traversal algorithms, and which is impossible to do with synchronized
blocks or methods. In addition, the methods of Lock
allow you to acquire the lock only if it is available—that is, you don't block trying to acquire the lock—or to stop trying to acquire the lock if interrupted or if a certain time has elapsed.
A Condition
abstracts the functionality of the Object
monitor methods wait
, notify
, and notifyAll
into a distinct interface with methods await
, signal
, and signalAll
. Condition
objects are used with Lock
objects the same way that wait
/notify
are used with monitors. The advantage is that while there is only a single wait-set per object, a Lock
can have as many associated Condition
objects as you need. The ReentrantLock
class is one useful implementation of Lock
that gives you Condition
objects that are functionally equivalent to synchronized
methods and blocks, plus the use of the monitor methods.
The package also contains the ReadWriteLock
interface—and an implementation—for a lock that allows for both exclusive use and shared use—informally, only one writer may hold the lock (exclusive) but arbitrarily many readers can hold the lock (shared).
The java.util.concurrent.atomic
package provides classes that represents values (int
, long
, references, and arrays of these values) that can be manipulated atomically. These classes provide operations that without the use of locks or in-built synchronization can, for example, get, set, increment, or decrement an integer. Atomic variables are used in the implementation of high-performance concurrent algorithms. For each basic atomic variable class, such as AtomicInteger
, there is a corresponding atomic field updater class, such as AtomicIntegerFieldUpdater
, that allows you to access a given field of an object atomically. These classes also provide the compareAndSet
operation that is the fundamental operation in most lock-free and wait-free concurrent algorithms. It will atomically set a variable to a new value only if the variable currently holds an expected value.
The java.util.jar
package provides classes for reading and writing the JAR
(Java ARchive) file format, which is based on the standard ZIP
file format with an optional manifest file. JAR
files can be used to distribute the classes and resources of a Package
as a single unit. The manifest stores information about the JAR
file contents and provides the specification information for the Package
contained therein—as we discussed on page 477.
Archive files define a format for storing multiple files within a single file, and allow for the compression of those original file contents. A full description of archive files, particularly the JAR
format, is beyond the scope of this book. For illustration we briefly describe each of the classes within the java.util.jar
package:
Attributes
—. maps Manifest
attribute names to associated string values.
Attributes.
Name
—represents an attribute's name. The main function of this class is to define static Attributes.Nam
e objects for each attribute that a manifest may have. For example, the object corresponding to the “Manifest-Version” attribute of a manifest is stored in the static field Attributes.Name.MANIFEST_VERSION
.
JarEntry
—. represents a JAR
file entry.
JarFile
—. represents an actual JAR
file and allows that JAR
file to be read or written.
JarInputStream
—. reads a JAR
file. Methods exist that read each entry of the JAR
file into a new JarEntry
object.
JarOutputStream
—. writes a JAR
file. Methods exist that take a JarEntry
object that is written to the JAR
file.
Manifest
—. maintains manifest entry names and their associated attributes.
A JarFile
represents an actual JAR
file and allows its contents to be read or written with the appropriate streams. A JAR
file consists of a manifest and a number of JAR
entries that can be read or written individually. The manifest contains all the attributes of the JAR
file and all the attributes of the entries in the JAR
file. The manifest is decoded with the Manifest
and Attributes
classes. Each JarEntry
contains the per-entry attributes extracted from the manifest.
Most users never need to use the classes within java.util.jar
. JAR
files can be created via tools supplied as part of your development environment and are read automatically by the class loader within the virtual machine when needed.
The java.util.zip
package provides classes for reading and writing standard ZIP
and GZIP
file formats. We mention it only for completeness, as it predates the newer JAR
file format and forms its basis.
ZipFile
—. represents a ZIP
or GZIP
format file.
ZipEntry
—. represents an entry in a ZIP
or GZIP
format file.
ZipInputStream
—. reads ZIP
file format.
ZipOutputStream
—. writes ZIP
file format.
GZIPInputStream
—. reads GZIP
file format.
GZIPOutputStream
—. writes GZIP
file format.
Deflater
—. provides general support for using ZLIB
compression.
DeflaterOutputStream
—. writes using ZLIB
compression
Inflater
—. provides general support for using ZLIB
decompression.
InflaterInputStream
—. reads ZLIB
compressed data.
CheckedInputStream
—. reads a stream, creating a checksum of the data.
CheckedOutputStream
—. writes a stream, creating a checksum of the data.
CRC32
—. computes a checksum using the CRC
-32 algorithm.
Adler32
—. computes a checksum using the Adler-32 algorithm.
Standard extensions are packages or collections of packages that can be downloaded to extend the capabilities of a particular virtual machine. They will usually have names starting with javax
, although future exceptions may be made. Conversely, packages with javax
in the name may become part of the main set of packages over time. For example, the javax.swing
and javax.accessibility
packages are part of the main set. The name indicates that they were at first not part of the main set, but integrated later.
Standard extensions are a mechanism that allows growth in the capabilities of the platform without further extensions to the core packages required of all virtual machines. New standard extensions can be defined and made available, but only installed where they are needed. Several standard extensions are defined, and more are on the way. Listing them all is far outside the scope of this book—you can find more information on the Web, especially at http://java.sun.com.
Graphical user interfaces traditionally assume certain kinds of abilities on the part of the user, such as the ability to use a mouse or keyboard. These assumptions are a problem for some people. The javax.accessiblity
interfaces and classes provide a way to write interfaces that can use assistive technologies for input and output, allowing people with different abilities to use the same interfaces. If an application fully supports the accessibility API
, then it should be compatible with, and friendly toward, assistive technologies such as screen readers and magnifiers.
This package defines the naming operations of the Java Naming and Directory Interface™ (JNDI
), with the subpackage javax.naming.directory
, defining the directory operations. These are designed to be independent of any specific naming or directory service implementation. Thus a variety of services—new, emerging, and already deployed ones—can be accessed in a common way.
A context, represented by the Context
interface, consists of a set of name-to-object bindings. Context
is the core interface for looking up, binding, unbinding, and renaming objects, and for creating and destroying subcontexts. lookup
is the most commonly used operation. You supply lookup
the name of the object you want to look up, and it returns the object bound to that name. For example, the following code fragment looks up a printer and sends a document to the printer object to be printed:
Printer printer = (Printer) ctx.lookup("Duplex"); printer.print(report);
Every naming method in the Context
interface has two overloads: one that accepts a Name
argument and one that accepts a string name. Name
is an interface that represents a generic name—an ordered sequence of zero or more components. For these methods, you can use Name
to represent a composite name (CompositeName
) so that you can use a name that spans multiple namespaces.
The overloads that accept Name
arguments are useful for applications that need to manipulate names: composing them, comparing components, and so on. The overloads that accept string names are likely to be more useful for simple applications, such as those that simply read in a name and look up the corresponding object.
The Binding
class represents a name-to-object binding. It is a tuple containing the name of the bound object, the name of the object's class, and the object itself. The Binding
class is actually a subclass of NameClassPair
, which consists simply of the object's name and the object's class name. The NameClassPair
is useful when you only want information about the object's class and do not want to pay the extra cost of getting the object.
Objects are stored in naming and directory services in different ways. If an object store supports storing objects it might store that object in its serialized form. However, some naming and directory services do not support the storing of such objects. Furthermore, for some objects in the directory, Java programs are just one group of applications that access them. In this case, a serialized object might not be the most appropriate representation. JNDI
defines a Reference
that contains information on how to construct a copy of the object. JNDI
will attempt to turn references looked up from the directory into the objects they represent so that JNDI
clients have the illusion that what is stored in the directory are objects.
In JNDI
, all naming and directory operations are performed relative to a context—there are no absolute roots. Therefore, JNDI
defines an InitialContext
class that provides a starting point for naming and directory operations. Once you have an initial context, you can use it to look up other contexts and objects.
The DirContext
interface represents a directory context. It defines methods for examining and updating attributes associated with a directory object, or directory entry as it is sometimes called. You use getAttributes
to retrieve the attributes associated with a directory object (for which you supply the name). You can add, replace, or remove attributes and/or attribute values with the modifyAttributes method
.
DirContext
also behaves as a naming context by extending the Context
interface. This means that any directory object can also provide a naming context. For example, the directory object for a person might contain the attributes of that person, and also provide a context for naming objects relative to that person such as printers and home directory.
DirContext
contains methods for performing content-based searching of the directory. In the simplest and most common form of usage, the application specifies a set of attributes—possibly with specific values—to match, and submits this attribute set to the search
method. Other overloads of search
support more sophisticated search filters.
The subpackage javax.naming.event
supports event notification for directory and naming services. Events include adding or removing objects, as well as events signifying changes in the stored objects themselves.
The subpackage javax.naming.spi
defines the Service Provider Interface (SPI
) which allows for dynamically plugging in support for using JNDI
.
The javax.sound
package consists only of subpackages. javax.sound.midi
provides interfaces for reading, writing, sequencing, and synthesizing data that conforms to the Musical Instrument Digital Interface (MIDI
) format. javax.sound.sampled
provides classes and interfaces for capturing, processing, and playing back digital (sampled) audio data. Each of these has a SPI
subpackage to support the dynamic addition of sound services.
The Swing components are a rich set of graphical user-interface controls. They are written to look and behave for the user identically on all systems as far as possible. This is in contrast to the AWT
components which rely on the native GUI
components. An AWT
button will look like a Windows button on a Windows machine, a Macintosh button on a Macintosh computer, and so forth—its interaction with the program is the same on all platforms, but the user will see the native look and feel. A Swing component can have the same look on all platforms—its code is written in the Java programming language. The look and feel is “pluggable”—you or the user can use one of the standard look-and-feels or invent one. Swing components are also give you more control over the look and behavior of the system. You only occasionally need such control, but it is nice to have it when you do need it.
The Swing components use the same event model as AWT
and JavaBeans components, although the components do define some new events.
Several subpackages define interface objects, tools for defining a custom pluggable look-and-feel, support HTML
and text editing, and support some of the more complex components, such as tree displays and tables.
The package org.omg.CORBA
, its various subpackages and other org.omg
subpackages (such as CosNaming
) provide the mapping of the OMG CORBA API
s and services to the Java programming language. (The package names are different from the standard core package names because they are defined by the Object Management Group and incorporated into the core package set.) CORBA
is a way to send messages between processes that are not necessarily written in the same programming language. This is distinct from RMI
, which communicates between programs written, at least in part, in the Java programming language (we use the phrase “at least in part” because RMI
programs can include native code).
Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far the universe is winning. | ||
--Rich Cook |
[1] For pragmatic reasons the collections classes had to be defined at the top level of java.util
, rather than in a subpackage, because of the existing legacy collections.