CONTENTS | PREV | NEXT |
There are two core
interfaces in JNDI: Context
, and
DirContext
, with DirContext
extending
the base naming operations in Context
with directory
service operations. They have been separated into separate
interfaces both for modularity and also in keeping with the
"pay for what you use" goal of JNDI.
Naming is a basic
component found in many computing services such as file systems,
spreadsheets, calendar services, and directory services. By having
a base Context
interface for the naming operations, we
enable its use by all these other services, not just for directory
services.
DirContext
extends Context
to provide basic directory service
operations, which include manipulation of attributes associated
with named objects, attribute-based searches, and schema-related
operations of those attributes and named objects.
JNDI is separated into
four client packages ( javax.naming
,
javax.naming.directory
,
javax.naming.event
, javax.naming.ldap
)
and a service provider package ( javax.naming.spi
).
The idea is that each package contains the interfaces and classes
required for a particular category of applications, again in
keeping with the "pay for what you use" goal. For
example, an application that just wants to perform name-lookups
only needs to use the javax.naming
package. An
application that wants to examine/modify attributes associated with
an object uses the javax.naming
and
javax.naming.directory
packages. An application that
needs to use LDAP-specific controls or extended operations uses the
javax.naming.ldap
package. There is a step-by-step
progression of what classes and interfaces each category of
application writer needs to learn and use.
JNDI separates
interfaces and classes that a client application needs to use from
those that are only of interest to service providers into different
packages. For example, a client would use interfaces and classes
from javax.naming
, while a service provider that is
hooking up a naming service would use both
javax.naming
and javax.naming.spi
. The
package delineation minimizes confusion for the application
developer and makes clear which packages he needs to examine when
writing his program.
There are two common types of applications that list contexts: browser-style applications, and applications that need to perform operations on the objects in a context en-masse. Browser-style applications typically want to display the names of the contents of a context. In addition to the names, many browsers often require type information of the objects bound to the names, so that it can display appropriate pictorial representations of the objects. The browser is usually interactive. Once a user has used a browser to display the contents of a context, he would then select one or a few of the entries displayed and request more information on it.
Some applications need to perform operations on objects within a context en-masse. For example, a backup program might want to perform "file stats" operations on all the objects in a file directory. A printer administration program might want to restart all the printers in a building. To perform such operations, these programs need to obtain all the objects bound in a context.
With these two common
styles of usage in mind, the Context
interface has two
types of list methods: list()
and
listBindings()
. list()
returns a list of
name/class-name pairs while listBindings()
returns a
list of name/class-name/object tuples. list()
is
designed for browser-style applications that want mostly just the
names and types of objects bound in a context.
listBindings()
is for applications that want to
potentially get all the objects in the context, as well as their
names and types. listBindings()
returns an enumeration
of Binding
. Both the listBindings()
operation itself and invocation of methods in the
Binding
class (e.g. getObject()
) could
be implemented lazily or eagerly. Using listBindings()
simply indicates the potential that the caller might want all or
many of the objects in the context so that implementations that are
able can optimize for it. Using list()
indicates that
the caller is unlikely to want all, if any, objects in the context
so implementations can optimize for that if possible.
An alternative is to
have a single list operation and have the lazy or eager behavior as
part of the implementation of Binding
. The advantage
of this is that there is a single list operation to learn. The
disadvantage is that the caller has no way of indicating which
piece of information he wants back from list, and subsequently,
implementations cannot optimize for the eventual behavior of the
program.
Federation is a
first-class concept in JNDI. In the client interfaces, it is
supported by the use of the Name
interface for
specifying names that can span one or more namespaces. The caller
of the methods in the client interface need not know anything else
regarding federation. Resolution of names across multiple systems
is handled by the SPI and does not involve any intervention on the
part of the caller.
Although federation is a
first-class concept, that does not mean that all callers and
service providers must make use of it. If an application or service
does not want to take advantage of federation, there is no
requirement that Name
always span multiple namespaces.
Name
can just name objects within a single namespace,
and the SPI can handle name resolution within a single namespace as
well (as a degenerate case of multiple namespace support).
Instead of having
DirContext
extend Context
, an
alternative would be to not extend Context
at all but
to have a separate interface called DirObject
that
encapsulates all the directory-related methods. In that case, an
object can implement both Context
and
DirObject
if it supports both the naming and directory
operations; another object might implement just
DirObject
.
The problem with
eliminating DirContext
is that DirContext
contains some hybrid operations that involve both naming and
directories ( bind()
, createSubcontext()
methods that accept attributes as arguments). To keep these
operations and have DirObject
at the same
time would produce the need for a third interface (perhaps called
DirContext
) to contain just these hybrids.
Furthermore, having
DirContext
instead of DirObject
is
somewhat more convenient in that you can perform some operations in
one step instead of two. For example
DirContext.getAttributes()
could be used to get the
attributes associated with a named object, whereas with
DirObject
, you would need first to resolve to the
object ( Context.lookup()
) and then use
DirObject.getAttributes()
to get the attributes from
it.
The
DirContext
interface contains support for schemas. For
example, from a DirContext
object you can obtain its
schema object, which points to the directory space where the schema
for this particular DirContext
instance is defined.
From a DirContext
object, you can also obtain its
schema class definition (i.e. information about what type of object
this represents in the directory). There is further support for
schemas in the Attribute
class, which contains methods
for obtaining an attribute's syntax information (i.e. what is the
type of the attribute's value) and the attribute's definition (e.g.
is it multivalued, syntax, constraints on its syntax). There is no
requirement that any of this schema information be dynamically
accessible (i.e. points to live directory spaces). Support for such
schema information could be generated statically by the service
provider. For example, a particular directory service might only
support string attribute values, so it can hard-wire the syntax of
the attributes that it returns. Another directory might support
only static schemas (where information in the schema are not
modifiable). Yet another directory might support fully dynamic
schemas. The interfaces and classes in DirContext
are
flexible enough that these different levels of support for schemas
can be accommodated.
For each method in the
Context
and DirContext
interfaces that
accepts a Name
argument, there is a corresponding
overloaded form that accepts a String
argument for
specifying a name.
The motivation for
having the String
-based methods is that there are
many applications that simply accept a string name from the
end-user and perform context methods on the object named by that
string name. For those applications, it is useful to have the
context methods accept a string for the name directly, instead of
requiring the applications to first construct a Name
object using the string name.
The motivation for
having the Name
-based methods is that there are also
many applications that manipulate names and do not want to worry
about syntactic details of the names' string forms when composing
and modifying names. These applications deal with the parsed form
of names and hence would prefer to deal with Name
objects rather than string names. For these applications, we
provide the Name
-based methods in the context
interfaces. Not providing these methods would probably cause
proliferation of Name
-like interfaces/classes to
support manipulation of names in their structural form in
applications developed on top of JNDI.
There are different ways in which applications and services can use the directory to locate objects. JNDI is general enough that it accommodates several different models. For some applications, the object bound in the directory is the object itself. An application may build up a dynamic directory while the application is active, and delete the directory when the application exits. Another application might store URLs as attributes for locating objects in its namespace. Other systems might bind some reference information in the directory, which can subsequently be used to locate or access the actual object. This last case is quite common, especially for making Java applications take advantage of services in the installed base. The reference in the directory acts as a "pointer" to the real object.
JNDI defines a
Reference
class to provide a uniform way of
representing reference information. A Reference
contains information on how to access an object. It consists of a
list of addresses and class information about the object to which
this reference refers. When binding a name to an object that is to
be represented in the directory as a reference, the desired effect
is that the object's reference be extracted and bound. To allow for
this behavior, the object's class must implement the
Referenceable
interface, which contains the method
getReference()
.
There is some similarity
between the interfaces Serializable
and
Referenceable
and a natural question is "why not
just use Serializable
instead?" The answer is
that a serialized object is really a frozen version of the object,
whereas the reference contains just the information needed to
construct it. The serialized version may have a lot more state
which may not be appropriate for storage in the directory.
For an object that is
bound as a Reference
in the directory, JNDI SPI
framework automatically creates and instantiates the object
identified by the reference. In this way, the program can simply
narrow the result of lookup()
to the expected class,
instead of calling a separate operation to transform the result of
lookup()
into an object of the expected class.
For example, if you are looking up a printer object, a successful lookup would return to you a printer object that you can directly use.
Printer prt = (Printer) ctx.lookup(somePrinterName); prt.print(someFileName);
JNDI does this
automatically, instead of requiring an explicit conversion step,
because this is expected to be the common usage pattern. By having
the Reference
class, and a common mechanism for
converting a Reference
into the object identified by
the Reference
, JNDI encourages different applications
and system providers to utilize this mechanism, rather than
inventing separate mechanisms on their own.