| By Teresa Lau | Article Rating: |
|
| November 1, 2001 12:00 AM EST | Reads: |
16,055 |
Lightweight Directory Access Protocol (LDAP) is fast becoming a de facto access method for common directory information. XML is now the standard for data exchange on the Web. The relational database, a technology for modeling data in table form, has always been the most popular way to store and retrieve application data.
How do these three technologies come together, other than the fact that they're all buzzwords in the technology world? They're all a way to represent data, which you can easily access using the powerful set of query capabilities they each provide. LDAP data can be queried using LDAP directory search, XML data can be queried using XPath, and relational database data can be queried using SQL.
In this article I describe an Employee Information Application that can use all three methods to store and query data. You'll learn how to construct each kind of query and write Java code to retrieve data, respectively. It's of great value to you as a developer because you're likely to come across systems implemented using one of these technologies and be asked to develop applications on top of it.
The Employee Information Application
Figure 1 shows the user interface of a simple Employee Information Application, organizing employees by last name, first name, phone, and e-mail address.
Each employee can be represented as a Java object (seen below). In the next few sections, I discuss how this data can be represented and queried in various ways.
class Employee
{
private String id;
private String lastName;
private String firstName;
private String phone;
private String e-mail;
}
Relational Database and SQL
The relational database is a common way to model application data. You can model all the employee information described above into one table called Employee. The id will be a unique key used to identify an employee.
create table Employee
{
id varchar(64) not
null;
lastName varchar(64) null;
firstName varchar(64) null;
phoneNumber varchar(12) null;
e-mail varchar(64) null
}
create unique index IndxEmp on Employee (id)
To retrieve data from relational database, use SQL. Most developers are familiar with SQL, so I won't explain it here. A standard SQL looks like this:
Select <field1>, <field2>
From <table>
Where <field1> = <value1>
And <field2> = <value2>
And ...
Querying a Relational Database in Java Code
To access the database using Java, you can use JDBC, the standard API for executing SQL statements in any relational database. With JDBC, different drivers can be installed dynamically to access different databases. As a result, your code remains the same irrespective of the kind of database you're using. The only difference is you need to include that particular database driver class in your classpath when you run the program; and inside the code you need to load your specific database driver class at the beginning.
In my implementation I use Sybase for the database and Sybase jConnect as the JDBC driver. To get the code to run I put jConnect.jar in my classpath when I run my program. To log in to the database, I specify the login properties below, which I'll put into a properties file, "db.properties."
user=username
password=password
server=servername
sqlinitstring=use databasename
Now I'll initialize by creating a connection to the database using the code below:
Class.forName("com.sybase.jdbc.SybDriver");
Properties prop = Util.loadProperties ("db.properties");
String URL="jdbc:sybase:Tds:whitehorn:6600";
Connection connection = DriverManager.getConnection(URL, prop);
This code does the following:
- Calls Class.forName() to load the database driver (in this case, the Sybase driver)
- Calls loadProperties (), a utility function I wrote (see Listing 1) to load the file db.properties to a Properties object
- Sets up the URL to connect to the database (the documentation of your JDBC driver should tell you what to put in your URL; it's of the form jdbc: protocol: data source information.)
- Passes URL and connection properties to getConnection() to create a database connection
String sql ="select id, lastName, firstName, " +
This code does the following: LDAP and LDAP Directory Search
In Figure 2, the top entry is an organization that has attri-
butes dc=whitehorn, dc=com. Note that for the same attribute type dc, you can have more than one value. Under the organization entry you can have children, which are also entries. For example, location newyork and location toronto are both child entries of organization. To identify an entry uniquely, a DN (distinguished name) is used. DN is similar to the concept of a primary key in a database. In this example, the DN of John Doe is represented as:
Uid=100012, ou=Employee,
Inside the John Doe entry you'll find other attributes such as lastName, firstName, phone, and e-mail.
An LDAP search operation is used to search a directory and retrieve individual directory entries. The LDAP search operation has eight parameters. I'll describe only three of them here, which should be sufficient for a simple query. (Refer to the links in the Resources section to learn more about LDAP searches.) Querying LDAP In Java Code JNDI is defined to be independent of any specific directory service implementation.
To use LDAP as the directory ser-vice implementation, add ldapbp.jar, ldap.jar,
and providerutil.jar to your classpath to access the LDAP service provider classes.
You can download the LDAP service provider release from http://java.sun.com/products/jndi/
#download.
In my example, the LDAP server I used is Netscape Directory Server, but it really doesn't matter what LDAP server you're using: the code should still be the same.
To access the LDAP server I need to specify information on how to get to it. I put this information in the properties file "ldap.properties" as shown below. This information includes the name of the service class provider (in this case, the Sun LDAP service provider class), the URL to the LDAP server, and the user login and password to the LDAP server.
java.naming.factory.initial=com.sun.jndi.ldap.LdapCtx Factory
In my initialization code below, I load the properties file into a Properties object and then create an InitialDirConext:
Properties p= Util.getProperties("ldap.properties"):
Now that I have an InitialDirContext, I can use it to look up all the employee entries using the code below:
String searchRoot = "ou=Employee" ;
This code does the following: XML and XPath
XPath is a language used to query XML, commonly used inside an XSLT stylesheet for XML transformation. It describes how to locate specific elements (and attributes, processing instructions, etc.) in a document. Similar to the LDAP search, which has a search base, XPath has the concept of the "context node", which is where you start from. Everything you search is relative to the starting location.
I'll now show you some examples of an XPath query using the XML in Listing 4. The basic syntax of XPath is similar to file system addressing. You can use an absolute path by starting with/or a relative path. For example, /Employees/
Employee/phone returns all the phone elements under Employees/Employee. Specifying * will get you all the elements located in the preceding path, so //Employee /* will return all elements (e.g., lastName, phone) under any Employee element in the document.
Expression in a square bracket can further specify an element. This is similar to a Where clause in a SQL. For example, Employee[starts-with(./lastName/text(),'D')] returns all employees with a lastName beginning in D. You can also specify the position of an element as in //Emp-
loyees/Employee[2], which returns the second Employee child of Employees.
Attributes are specified by an @prefix. For example, // @city returns all the city attributes in the document. Attributes can also be used inside the square bracket to specify an element. An example is Addr[@city='newyork'], which returns all the Address elements that have an attribute city=newyork.
You can use a Boolean operation inside the square bracket. For example:
1. Employee[boolean(./lastName) or boolean (./e-mail)]
The first query represents an employee that has lastName or e-mail attributes, while the second query represents an employee that has both lastName and e-mail attributes. There are lists of functions for Node-Set, Boolean, Number, and String that you can use within an expression (see the Resources section).
You can apply more conditions in sequence by entering square brackets one after another. This is similar to a subquery in SQL. In addition, if the query is sequence-dependent, the result will depend on which condition you specify first. This can be seen in the example below:
1. //Employees/Employee[1][starts-
Query 1 first selects all the first Employee elements under any Employees element, and then from that list, selects the ones that have a lastName beginning with "D." Query 2 first gets all the Employee elements under employees that have lastName D, and then from that list, selects the first one. The result and number of elements returned from these two queries may be different in this case.
There are many more powerful features of XPath that I haven't described here. Refer to the Resources section to learn more.
Querying XML in Java Code
In my example, I use the Xerces XML parser and Xalan XSLT processor, both from Apache (www.apache.org), but you can use any other parsers or XSLT processors that implement the JAXP API. In my classpath I need to include xerces.jar and xalan.jar so that the Apache classes can be found. The following code prepares for an XPath query by creating a DOM parser first. It uses the Xerces implementation of the DocumentBuilderFactory to create a Xerces DOM parser.
DocumentBuilderFactory factory =
Now that I have a DOM parser, I can use the following code to do an XPath query to get all the Employees elements.
URL cfgURL = Util.createURL("employees.xml"); String XPath = "//Employee emp.setLastName(lastname.getFirstChild().getNodeValue());
This code does the following: A Mode Complicated Query
The SQL query for this search is:
select id, lastName, firstName, phone, e-mail from Employee
Figure 3 shows the result of running this query in a SQL tool. Search Base: ou=Employee,dc=newyork,
dc=white
The result of running this query in the query tool of an LDAP browser is shown in Figure 4. //Employee[starts-with(./lastName/text(),'D') or Figure 5 shows the result of running this in XPath using a tool called Cooktop
XML editor (a free XML editor that can be downloaded at www.xmleverywhere.com/cooktop).
You can easily put the queries we constructed here back into the code we wrote in the last section, and have the results returned programatically in Java.
Common Interface Interface EmployeeQuery
The client can then call methods in this interface without knowing what the underlying implementation is like. In my test client program, I create a method call runTest that calls init() and then does a search with empty conditions. This returns a list of all employees, printing their first and last names.
public static void runTest(EmployeeQuery eq)
Then I create the database, XML and LDAP implementations of EmployeeQuery, and
call runTest on them, respectively, to test out the implementation.
public static void main(String[] args)
You can find the complete source code of DBImpl, LDAPImpl, and XMLImpl shown, respectively, in Listing 2, Listing 3, and Listing 5. Because this is only an example, currently all they do is return all the employees in their system. It doesn't use the condition parameter passed in to generate the query. You can do more work to specify conditions from a GUI, which you turn into the respective query to run and return the matching items.
Conclusion A final point to bear in mind when developing your client: it's possible that the underlying implementation may change or that your client may be used with more than one implementation. In that case, your system will benefit if you use an interface when designing your code.
Resources
"phone,
e-mail from Employee" ;
List employeeList = new ArrayList();
Statement stmt = connection.createStatement();
ResultSet s = stmt.executeQuery(sql);
while (s.next())
{
String id = s.getString ("id");
// ..Get other fields e.g.
lastName similarly
Employee employee = new Employee(id);
// ... Set other fields into
employee object similarly
employeeList.add(employee);
}
The full source of the database query code can be found in the DBImpl class in Listing 2.
LDAP is a hierarchical way of storing data. It's commonly used by system administrators to store system and user information. LDAP stores data in a directory structure similar to the Unix file system. The basic unit of information in a directory is an entry. An entry has attributes, each of which has a type and one or more values. Figure 2 shows how employee information may be represented in LDAP.
dc=newyork,dc=whitehorn,dc=com
Attr=value
equal
Attr~=value approximate
Attr=*value* use
* to indicate wild card any position
Attr>=value greater
or equal
Attr<=value less
or equal
Attr=* presence
(&(filter1)(filter2)) and
( | (filter1)(filter2)) or
( ! (filter)) negation
In Java you can use Java Naming and Directory Interface s(JNDI) to do a directory search on the LDAP server. JNDI is an API that provides naming and directory functionality to applications written using Java. JNDI is included in the Java 2 SDK, v1.3, and later releases. If you're using earlier Java versions, you'll need to include the JNDI extension package in your classpath.
java.naming.provider.url=ldap:/localhost:1100/dc=ny,dc =whs,dc=com
java.naming.security.principal=user
java.naming.security.credentials=password
DirContext ctx = new InitialDirContext(p);
String filter="(uid=*) ";
String[] attrIDs ={"uid", "givenname", "sn", "tele-
phonenumber",
"mail"};
SearchControls ctls = new SearchControls();
ctls.setReturningAttributes(attrIDs);
List employeeList = new ArrayList ();
NamingEnumeration list =
ctx.search(searchRoot,
filter, new SearchControls());
while (list.hasMore())
{
SearchResult
nc = (SearchResult)list.next();
Attributes
attrs = nc.getAttributes();
Attribute
attrId = (Attribute) attrs.get("uid");
String
id = (String) attrId.get();
Employee
e = new Employee(id);
//
...similarly get other fields to fill out
emplyeeList.add(employee);
}
The complete LDAP search code can be found in LDAPImpl class in Listing 3.
XML may not be the way to persist data, but with the B2B nature of applications, it's likely that we don't get our information directly from its persistence storage. For example, we could be getting the employee information from another Web service that will send us the information as an XML file, as shown in employees.xml in Listing 4.
2. Employee[boolean(./lastName) and boolean (./e- mail)]
with(./lastName/text(),'D') ]
2. //Employees/Employee[starts-
with(./lastName/text(),'D') ][1]
To query XML using XPath, we can use the JAXP ( Java API for XML Parsing) API.
JAXP provides a common interface for creating and using the standard SAX, DOM,
and XSLT APIs in Java, regardless of which vendor's implementation is actually
being used. JAXP API 1.1. is included in the J2SE 1.4, or you can get it from
http://java.sun.com/xml/download.html
to be used as an optional package for JDK 1.1.8 and above.
DocumentBuilderFactory.newInstance();
domParser = factory.newDocumentBuilder();
Document d = domParser.parse(cfgURL.toString());
List employeeList = new ArrayList();
NodeList nl = XPathAPI.selectNodeList(d, XPath );
for (int i = 0; i< nl.getLength(); i++)
{
Node n = nl.item(i);
// id is an attribute
Node nm = n.getAttributes().getNamedItem("id");
Employee emp = new
Employee (nm.getNodeValue());
// Other fields
are elements
NodeList childNodes
= n.getChildNodes();
Node lastname =
lookup (childNodes, "lastName");
// ...do similarly
for other fields
employeeList.add (emp);
}
The full listing of the XPath search code can be found in the XMLImpl class in Listing 5.
To further illustrate the various ways to query the data, I'll now explain how to assemble the respective queries based on the same search condition as shown below:
where (lastName like 'D%' or firstName like 'M%')
and e-mail = null
The LDAP query for this search is:
horn,dc=com
Search Filter: (& (|(givenname=M*)(sn=D*))
(!(mail=*)))
Attributes: uid,
sn, givenName, telephoneNumber, mail
The XPath query for this search is:
starts-with(./firstName/text(),'M')]
[not(boolean(./e-mail/text()))]
Depending on where the data is stored or comes from, the implementation for
the Employee Search Application will be different. We could apply the Bridge
pattern from the book, Design Patterns, to write the client for the Employee
Search Application. The Bridge pattern separates a class interface from its
implementation, so you can vary or replace the implementation without changing
the client code. To do so I define the following interface:
{
// Does initialization e.g. connect to server
init();
// Generate query based on Condition and
return
List of Employee
List search (Condition c);
}
{
System.out.println
("Running class " + eq.getClass().toString());
eq.init();
List
l = eq.search (new Conditions());
Iterator
i = l.iterator();
while
(i.hasNext())
{
Employee
e = (Employee) i.next();
System.out.println
(e.getId() + ":" +
e.getFirstName()
+ " " + e.getLastName() );
}
}
{
runTest(new DBImpl());
runTest(new XMLImpl());
runTest(new LDAPImpl());
}
I've shown you three ways of implementing the same client based on how the data is stored. It's unlikely that you'll have to implement an application in all three different ways. The purpose of this article is to show you the different syntaxes and code APIs you can use, so you can easily tackle each kind of query with minimal effort when you need to. I've only touched briefly on SQL, LDAP, and XPath. Each of them is in itself a big subject that you can study in detail when you have time.
Relational databases and SQL:
LDAP:
XML and XPath:
Published November 1, 2001 Reads 16,055
Copyright © 2001 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Teresa Lau
Teresa Lau has been an independent Java consultant for over four years, with
an emphasis on financial applications. She received her MS in computer
science from the University of Waterloo, and her BS in engineering from the
University of California, Berkeley.
- Cloud CEOs, CTOs & SVPs to Speak at 4th International Cloud Computing Expo
- Kindle 2 vs Nook
- Why IBM’s Server Chief Got Busted
- The Difference Between Web Hosting and Cloud Computing
- Cloud Computing Journal Opens "Readers' Choice Awards" Nominations
- Cloud Computing Expo: Exclusive Q&A with Yahoo! SVP Cloud Computing
- Industry Experts Discuss the State of Cloud Computing
- Ajax in RichFaces 3.3, JSF 2 and RichFaces 4
- It's the Java vs. C++ Shootout Revisited!
- The End of IT 1.0 As We Know It Has Begun
- An Introduction to Abbot
- Java Kicks Ruby on Rails in the Butt
- Interviewing Java Developers With Tears in My Eyes
- Cloud CEOs, CTOs & SVPs to Speak at 4th International Cloud Computing Expo
- 1st Annual Government IT Expo: Call for Papers Deadline July 15
- How to Diagnose Java Resource Starvation
- REA Is Where RIA Becomes the Norm
- Kindle 2 vs Nook
- Anatomy of a Java Finalizer
- Why IBM’s Server Chief Got Busted
- A Cup of AJAX? Nay, Just Regular Java Please
- Java Developer's Journal Exclusive: 2006 "JDJ Editors' Choice" Awards
- The i-Technology Right Stuff
- JavaServer Faces (JSF) vs Struts
- Rich Internet Applications with Adobe Flex 2 and Java
- Java vs C++ "Shootout" Revisited
- Bean-Managed Persistence Using a Proxy List
- Reporting Made Easy with JasperReports and Hibernate
- Creating a Pet Store Application with JavaServer Faces, Spring, and Hibernate
- What's New in Eclipse?































