Java Naming and Directory Interface: Lesson 3

1. NAMING OPERATIONS

The following sections describe how to perform different name-related operations. We shall use ApacheDS as the name server unless it is specified explicitly.

1.1. Adding, Replacing and Removing Binding

A binding is added to a context using its bind() method, which takes an object and either a String or a Name. The following example binds a Java String object with the name cn=AJavaObject and associates with the specified initial context.

ctx.bind(“cn=aJavaObject”, new String(“JNDI”));

The types of objects that are allowed [Table 24.2:] to bind depends on the naming server. For example, LDAP can only bind Referenceable, Serializable and DirContext objects, whereas file system can only bind Referenciable or Reference objects. Java’s rmiregistry can only bind Remote, Reference and Referenciable objects and Java IDL’s orbd and tnameserv can only bind CORBA objects. For more information, read the respective server’s documentation. So, Java’s String object (which is only Serializable) can be bound to only LDAP naming service.

The bind() method throws a NameAlreadyBoundException if the name has already been used to bind some other object. The rebind() method works like bind(), except that it replaces existing binding (if any) with the specified one.

ctx.rebind(“cn=aJavaObject”, new String(“JNDI”));

To remove an existing binding, use unbind() method specifying the name of the binding:

ctx.unbind(“cn=aJavaObject”);

This removes an existing binding having name aJavaObject from the current context.

1.2. Looking Up

To find an object from the naming tree, we use lookup() method specifying name of the object as follows:

Object o = ctx.unbind(“cn=aJavaObject”);

Since, the type of the object we bound earlier is a String, we can cast the result of lookup() to its target class as follows:

String s = (String)ctx.lookup(args[0]);

1.3. Renaming

The name of an object in a context may be renamed using rename() method.

ctx.rename(“old_name”, “new_name”);

This renames the object that was bound to “old_name” to “new_name”. The bound object remains unchanged.

1.4. Listing

It is sometimes necessary to find all bindings under a certain context. JNDI provides two methods for this: listBindings() and list() .

1.4.1. Using list()

This method returns an enumeration of NameCiassPair, which consists of the object’s name and its class name. The following example illustrates this:

//List.java

import javax.naming.*;

public class List {

public static void main(String args[]) {

try {

Context ctx = new InitialContext();

java.util.Enumeration e = ctx.list(args[0]);

while(e.hasMoreElements()) {

NameClassPair ncp = (NameClassPair)e.nextElement();

System.out.println(ncp.getName()+” <-> ”+ncp.getClassName());

}

}catch (Exception e) { e.printStackTrace(); }

}

}

This program lists all object’s names and their class names. So far, we have used name servers such as rmiregistry and ApacheDS. Let us now use file system naming service. For file system, sun also provides a factory class com.sun.jndi.fscontext.RefFSContextFactory. However, this class is not a part of core Java. So, we downloaded the JAR fscontext.jar containing this class from http://www.java2s.com/Code/Jar/f/Downloadfscontextjar.htm and placed in the class path during runtime. To see the list of file bindings under D:\apacheds-2.0.0-M15 drive, use the following command:

java -cp ..\fscontext.jar;. –

Djava.naming.factory.initial=com.sun.jndi.fscontext.RefFSContextFactory –

Djava.naming.provider.url=file:///D:/ List apacheds-2.0.0-M15

A sample result is shown below:

bin <-> javax.naming.Context

instances <-> javax.naming.Context

lib <-> javax.naming.Context

LICENSE <-> java.io.File

NOTICE <-> java.io.File

1.4.2. Using listBindings()

This returns an enumeration of Binding, which is a subclass of NameClassPair, and additionally contains the object. The following code demonstrates this:

//ListBindings.java import javax.naming.*; public class ListBindings {

public static void main(String args[]) {

try {

Context ctx = new InitialContext();

java.util.Enumeration e = ctx.listBindings(””);

while(e.hasMoreElements()) {

Binding b = (Binding)e.nextElement();

System.out.println(b.getName()+” <-> ”+b.getObject());

}

}catch (Exception e) { e.printStackTrace(); }

}

}

To see the list of file bindings under D: drive, use the following command:

java -cp ..\fscontext.jar;. –

Djava.naming.factory.initial=com.sun.jndi.fscontext.RefFSContextFactory –

Djava.naming.provider.url=file:///D:/ ListBindings apacheds-2.0.0-M15

A sample result is shown below:

bin <-> com.sun.jndi.fscontext.RefFSContext@2a15cd

instances <-> com.sun.jndi.fscontext.RefFSContext@fd68b1

lib <-> com.sun.jndi.fscontext.RefFSContext@e45076

LICENSE <-> D:\apacheds-2.0.0-M15\LICENSE

NOTICE <-> D:\apacheds-2.0.0-M15\NOTICE

2. WORKING WITH SUBCONTEXT

In addition to operations on object bindings, JNDI also allows creating and destroying subcontexts. To create a subcontext, we use createSubcontext() providing the name of the subcontext.

ctx.createSubcontext(”cn=CU”);

It creates a subcontext cn=cu under the current context. To destroy a context, we use destroySubcontext() providing the name of the context to be destroyed.

ctx.destroySubcontext(”cn=CU”);

This destroys the context cn=CU from the context ctx.

3. WORKING WITH DIRECTORY

To work with directory specific operations, we need the package javax.naming.directory. We import all classes under this package, using the following import statement:

import javax.naming.directory.*;

In a directory, the counterparts of Context and InitialContext are DirContext and InitialDirContext respectively. So, obtain a reference to an initial directory context as follows:

DirContext dc = new InitialDirContext();

The InitialDirContext also reads the values of two properties java.naming.factory.initial and java.naming.provider.url. So, specify them using any one of the procedures described earlier. Let us now discuss how to perform different directory-related operations. We shall use ApacheDS as the name server unless it is specified explicitly. To avoid specifying long JNDI properties repeatedly, we created property file jndi.properties and placed in the directory containing our test programs. The content of this file is shown below:

#jndi.properties

java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory

java.naming.provider.url=ldap://172.16.5.81:10389/dc=example,dc=com

3.1. Reading Attributes

Let us first see how to read all attributes of a directory object. The getAttributes() method on Context object is used for it. It takes the name of an object and returns an Attributes object that holds the unordered collection of attributes the object possesses. The Attributes interface has useful methods to obtain individual attributes or listing all attributes. The following program prints all attributes of the specified directory context:

//ReadAttributes.java import javax.naming.*;

import javax.naming.directory.*;

public class ReadAttributes {

public static void main(String args[]) {

try {

DirContext ctx = new InitialDirContext();

Attributes attrs = ctx.getAttributes(args[0]);

for (NamingEnumeration ne = attrs.getAll();

ne.hasMore();) {

Attribute attr = (Attribute)ne.next();

System.out.println(”attribute: ” + attr.getID());

for (NamingEnumeration e = attr.getAll();

e.hasMore();)

System.out.println(”value: ” + e.next());

}

}catch (Exception e) { e.printStackTrace(); }

}

}

Use the following command to see all attributes of the ApacheDS partition dc=exampie,dc=com:

java ReadAttributes ””

A sample result is shown below:

attribute: dc

value: example

attribute: objectclass

value: domain value: top

3.2. Binding with Attributes

The DirContext class is a subclass of Context and provides overloaded methods many of which accept an additional Attributes parameter. So, we can use these methods to associate attributes with the object at the time of the binding. Consider the following example:

//BindAttr.java

import javax.naming.directory.*; class BindAttr {

public static void main(String[] args) {

try {

DirContext ctx = new InitialDirContext();

Attributes attrs = new BasicAttributes(true);

attrs.put(new BasicAttribute(”objectclass”, “organization”));

ctx.bind(args[0], new String(”An organization”), attrs);

} catch (Exception e) { e.printStackTrace(); }

}

}

This associates an attribute objectclass having value organization with a String object and binds it with the name specified as command line argument. The objectclass attribute tells that the String object is a kind of organization object. Run this program using the following command:

java BindAttr o=JU

Since ApcaheDS specifies that an organization object must also have an o (o for organization) attribute, the String object must have o attributes. We have specified it in its name o=ju. We can check this using our previous program as follows:

java ReadAttributes o=JU

A sample result is shown below:

attribute: javaSerializedData

value: [B@70be88

attribute: objectclass

value: organization

value: javaSerializedObject

value: javaObject

value: top

attribute: javaClassNames

value: java.lang.String

value: java.lang.Object

value: java.io.Serializable

value: java.lang.Comparable

value: java.lang.CharSequence

attribute: javaClassName

value: java.lang.String

attribute: o

value: JU

Like replacing a binding in a name service, we can use unbind() method to replace a binding in a directory service.

3.3. Creating subcontext with Attributes

To create a subcontext having attributes, we use createSubcontext() method specifying name of the subcontext and attributes. Here is an example:

//AddEntry.java

import javax.naming.directory.*; public class AddEntry {

public static void main(String args[]) {

try {

DirContext ctx = new InitialDirContext();

Attributes attrs=new BasicAttributes(true);

attrs.put(new BasicAttribute(”objectclass”, “country”));

ctx.createSubcontext(args[0], attrs);

}catch (Exception e) { e.printStackTrace(); }

}

}

This creates a subcontext with an attribute objectclass having value country. The created subcontext essentially is a DirContext object that can contain a set of country bindings. Use the following command to run this:

java AddEntry c=India

We can check this using our List.java program as follows:

java List ””

A sample result is shown below:

o=JU <-> java.lang.String

c=India <-> javax.naming.directory.DirContext

3.4. Adding Attributes

Attributes may be added, deleted, replaced using modifyAttributes() method that takes the following form:

void modifyAttributes(Name name, int mod_op, Attributes attrs)

The second argument indicates the type of modification and can have any one of the static constants ADD_ATTRIBUTE, REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE of DirContext class. The following example adds an attribute description to the object having specified name.

//AddAttribute.java

import javax.naming.directory.*;

public class AddAttribute {

public static void main(String args[]) {

try {

DirContext ctx = new InitialDirContext();

Attributes attrs = new BasicAttributes(true);

attrs.put(new BasicAttribute(args[1],args[2]));

ctx.modifyAttributes(args[0], DirContext.ADD_ATTRIBUTE, attrs);

}catch (Exception e) { e.printStackTrace(); }

}

}

Run this program as follows:

java AddAttribute o=JU description A_University

This adds an attribute description to the object having specified name. We can check this using our ReadAttributes.java program as follows:

java ReadAttributes o=JU

A sample result is shown below:

attribute: objectclass

value: organization

attribute: description

value: A_University

attribute: o

value: JU

3.5. Modifying Attributes

We use the constant replace_attribute for modifying an existing attribute. The following example changes the value of description attribute:

//ModifyAttribute.java

import javax.naming.directory.*;

public class ModifyAttribute {

public static void main(String args[]) {

try {

DirContext ctx = new InitialDirContext();

Attributes attrs = new BasicAttributes(true);

attrs.put(new BasicAttribute(args[1],args[2]));

ctx.modifyAttributes(args[0], DirContext.REPLACE_ATTRIBUTE, attrs);

}catch (Exception e) { e.printStackTrace(); }

}

}

Run this program as follows:

E:\ajp\jndi\final>java ModifyAttribute o=JU description ”One of the top 5 universities in India”

We can check this using our ReadAttributes.java program as follows:

java ReadAttributes o=JU

A sample result is shown below:

attribute: objectclass value: organization attribute: description

value: One of the top 5 universities in India

attribute: o

value: JU

3.6. Removing Attributes

We use the constant remove_attribute for deleting an existing attribute. The following example deletes the postalCode attribute:

//RemoveAttribute.java

import javax.naming.directory.*;

public class RemoveAttribute {

public static void main(String args[]) {

try {

DirContext ctx = new InitialDirContext();

Attributes attrs = new BasicAttributes(true);

attrs.put(new BasicAttribute(args[1]));

ctx.modifyAttributes(args[0], DirContext.REMOVE_ATTRIBUTE, attrs);

}catch (Exception e) { e.printStackTrace(); }

}

}

Before running this example, make sure that the attribute postaiCode already exists. You can add it using our AddAttribute.java program as follows:

java AddAttribute o=JU postaiCode 700032

We can now delete this attribute using the following command:

java RemoveAttribute o=JU postalCode

If you read all attributes of o=ju, you will no longer see this attribute.

3.7. Batch Operation on Attributes

The modifyAttributes() method also has an overloaded version that takes a list of modification requests, each of which is represented by ModificationItem. Each ModificationItem consists of an Attribute to be used for modification and one of the numeric constants (add_attribute, replace_ attribute, remove_attribute) indicating the type of modification to make. The following example illustrates this:

//ModifyAttributes.java

import javax.naming.directory.*;

public class ModifyAttributes {

public static void main(String args[]) {

try {

DirContext ctx = new InitialDirContext();

ModificationItem[] mi = new ModificationItem[3];

mi[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, new

BasicAttribute(“description”, “Located in Kolkata, India”));

mi[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,

new BasicAttribute(“description”, “Located in Kolkata, India”));

mi[2] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE,

new BasicAttribute(“postalCode”));

ctx.modifyAttributes(args[0], mi);

}catch (Exception e) { e.printStackTrace(); }

}

}

This adds a description attribute, then it changes its value and finally it removes the postalCode attribute.

3.8. Search

In addition to searching objects by their names, a directory server also allows us to search by attributes. The directory then returns a list of entries satisfying the query. For example, we may want to have all entries having an attribute o with value ju. The JNDI DirContext provides useful methods for various types of searching described in the following sections:

3.8.1. Basic Search

In the simplest case, we can search within a given context for entries having specified set of attributes. We first create an Attributes object and populate with desired attributes. The following specifies an attribute o with value ju and objectclass with value organization.

Attributes attrs = new BasicAttributes(true);

attrs.put(new BasicAttribute(”o”, ”JU”));

attrs.put(new BasicAttribute(”objectclass”, “organization”));

We then pass this attribute and context to be searched to the search() method as follows:

NamingEnumeration result = ctx.search(args[0], attrs);

The search() method returns NamingEnumeration consisting of a list of SearchResult object each of which represents an entry satisfying the search. The following program prints details of entries having attributes o with value ju.

import javax.naming.*;

import javax.naming.directory.*;

class BasicSearch {

public static void main(String[] args) {

try {

DirContext ctx = new InitialDirContext();

Attributes attrs = new BasicAttributes(true);

attrs.put(new BasicAttribute(“o”, “JU”));

attrs.put(new BasicAttribute(“objectclass”, “organization”));

NamingEnumeration result = ctx.search(args[0], attrs);

while (result.hasMore()) {

SearchResult sr = (SearchResult)result.next();

System.out.println(”name: ” + sr.getName());

for(NamingEnumeration ne = sr.getAttributes().getAll();

ne.hasMore();){ Attribute attr = (Attribute)ne.next();

System.out.println(”attribute: ” + attr.getID());

for (NamingEnumeration e = attr.getAll();

e.hasMore();) System.out.println(”value: ” + e.next());

}

}

} catch (Exception e) { e.printStackTrace(); }

}

}

Before running this application, add some bindings using the following commands:

java BindAttr o=JU

java BindAttr o=CU

java BindAttr o=BU

Now, run the program using the following command:

java BasicSearch ””

A sample output is shown below:

name: o=JU

attribute: javaSerializedData

value: [B@28305d

attribute: objectclass

value: organization

value: javaSerializedObject

value: javaObject

value: top

attribute: javaClassNames

value: java.lang.String

value: java.lang.Object

value: java.io.Serializable

value: java.lang.Comparable

value: java.lang.CharSequence

attribute: javaClassName

value: java.lang.String

attribute: o

value: JU

The search() m.ethod returns, by default, all attributes associated with the entries that satisfy the query. However, we can specify a set of attributes the search() method should include in the return value. For example, the following specifies that only the objectclass attribute should be returned:

String[] desiredAttrs = {“objectciass”};

NamingEnumeration result = ctx.search(args[0], attrs, desiredAttrs);

This gives the following result:

name: o=JU

attribute: objectciass

value: organization

value: javaSerializedObject

value: javaObject value: top

3.8.2. Filters

A search may be performed specifying search query, called search filter, consisting of logical expressions. For example, the following search filter selects entries that have an attribute o with value

JU and objectciass with organization.

String filter = ”(&(o=JU)(objectclass=organization))”;

The following example essentially does the same thing as our previous example:

import javax.naming.*;

import javax.naming.directory.*;

class SearchFilter {

public static void main(String[] args) {

try {

DirContext ctx = new InitialDirContext();

SearchControls ctls = new SearchControls();

String filter = “(&(o=JU)(objectclass=organization))”;

NamingEnumeration result = ctx.search(args[0], filter, ctls);

while (result.hasMore()) {

SearchResult sr = (SearchResult)result.next();

System.out.println(”name: ” + sr.getName());

for(NamingEnumeration ne = sr.getAttributes().getAll();

ne.hasMore();){ Attribute attr = (Attribute)ne.next();

System.out.println(”attribute: ” + attr.getID());

for (NamingEnumeration e = attr.getAll();

e.hasMore();) System.out.println(”value: ” + e.next());

}

}

} catch (Exception e) { e.printStackTrace(); }

}

}

Like basic search, we can customize the set of attributes to be returned by the search() method as follows:

String[] desiredAttrs = {“objectciass”};

ctls.setReturningAttributes(desiredAttrs);

Source: Uttam Kumar Roy (2015), Advanced Java programming, Oxford University Press.

Leave a Reply

Your email address will not be published. Required fields are marked *