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.