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.