Wednesday, January 21, 2009

Spring LDAP : My experiments

" Spring LDAP is a Java library for simplifying LDAP operations, based on the pattern of Spring's JdbcTemplate. The framework relieves the user of common chores, such as looking up and closing contexts, looping through results, encoding/decoding values and filters, and more. "

Spring LDAP

I took some time to explore the Spring LDAP library & I am impressed with it. The library aims to make a developer productive by eliminating a lot of plumbing code that one would encounter with plain-vanilla JNDI. I have worked on building an User Management application using JNDI & when I compare it with the facilities provided in Spring LDAP, I defenitely would think twice before coding in regular JNDI.

I created a sample application, by using the principles listed out in the Spring LDAP Reference Documentation & some of the samples I found using my favourite Google.

The steps to use Spring LDAP are quite the same for any Spring application :-

1. Get the Spring LDAP libraries.
2. Configure the applicationContext.xml
3. Write any utility classes you may need.
4. Write the Interface and Implementation class.
5. Write a test harness to see if all these gule together & work ( of course, it will ! :) ).

Let's take it one step at a time...

1. Get the Spring LDAP libraries.

You can get it from the Spring LDAP home page.

After downloading the library, you can unizp it & have a look at the README file. The file usually outlines the dependencies very clearly. In my case, I had to include all these libraries in my project :-
  • spring-ldap-core-tiger-x.x.jar
  • Commons Logging
  • Commons Lang
  • Commons Pool
  • spring-beans
  • spring-core
  • spring-context
  • spring-jdbc
  • spring-tx
  • ldapbp
2. Configure the applicationContext.xml

Here's my applicationContext.xml :-

< ?xml version="1.0" encoding="UTF-8"? >
< !DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
<>

< !-- The Spring LDAP Context Source Configuration. The information provided here is used to create an instance of InitialLdapContext -- >
< id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
< name="url" value="ldap://ecmser.idc.oracle.com:389">
< name="base" value="cn=Users,dc=idc,dc=oracle,dc=com">
< name="userDn" value="cn=orcladmin">
< name="password" value="allstate1">
< /bean >

< !-- The Spring LDAP Template executes tcore LDAP functionalities. It requires the Context Source for its operations. -- >
< id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
< ref="contextSource">
< /bean >

< !-- Our User Bean that makes uses of the pre-configured Spring LDAP Template-- >
< id="user" class="com.org.sandeep.dao.impl.UserDAOImpl">
< name="ldapTemplate" ref="ldapTemplate">
< /bean >

< /beans >

3. Write any utility classes you may need.

First, I an interface as a placeholder for some of the constants :-

package com.org.sandeep.util;

public interface UserConstants
{
String FIRST_NAME = "cn";
String LAST_NAME = "sn";
String BLANK = "";
String OBJECT_CLASS = "objectclass";
String PERSON = "person";
}


Next, I wrote my bean class :-

package com.org.sandeep.bean;

public class UserBean
{
private String firstName;
private String lastName;

public UserBean()
{
}

public UserBean(String firstName,String lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}

public void setFirstName(String firstName)
{
this.firstName = firstName;
}

public String getFirstName()
{
return firstName;
}

public void setLastName(String lastName)
{
this.lastName = lastName;
}

public String getLastName()
{
return lastName;
}


public String toString()
{
return firstName + " : "+lastName;
}
}

Finally, I wrote an AttributesMapper class the implements Spring's AttributeMapper interface. To quote from the Spring LDAP Reference Manual :-

An interface used by LdapTemplate for mapping LDAP Attributes to beans. Implementions of this interface perform the actual work of extracting results, but need not worry about exception handling. NamingExceptions will be caught and handled correctly by the LdapTemplate class.

Here's my AttributesMapper class :-

package com.org.sandeep.mapper;

import com.org.sandeep.bean.UserBean;
import com.org.sandeep.util.UserConstants;

import javax.naming.NamingException;
import javax.naming.directory.Attributes;

import org.springframework.ldap.core.AttributesMapper;

public class UserMapper implements AttributesMapper
{
public Object mapFromAttributes(Attributes attributes)
{
UserBean userBean = null;

String firstName = null;
String lastName = null;

try
{
firstName = (String)attributes.get(UserConstants.FIRST_NAME).get();
lastName = (String)attributes.get(UserConstants.LAST_NAME ).get();
}
catch (NamingException objNamingException)
{
objNamingException.printStackTrace();
}

if ( firstName != null || lastName != null )
{
userBean = new UserBean(firstName,lastName);
}

return userBean;
}
}


4. Write the Interface and Implementation class.

First, we need an interface :-

package com.org.sandeep.dao;

import com.org.sandeep.bean.UserBean;

import java.util.List;

public interface UserDAO
{
public List get(UserBean user);
public void add(UserBean user);
public void modify(UserBean user);
public void remove(UserBean user);
}
Next, we need an implementation class that uses the interface :-

package com.org.sandeep.dao.impl;

import com.org.sandeep.bean.UserBean;
import com.org.sandeep.dao.UserDAO;
import com.org.sandeep.mapper.UserMapper;

import com.org.sandeep.util.UserConstants;

import java.util.List;

import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;

import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;


public class UserDAOImpl implements UserDAO
{
private LdapTemplate ldapTemplate;

public UserDAOImpl()
{
}

public void setLdapTemplate(LdapTemplate ldapTemplate)
{
this.ldapTemplate = ldapTemplate;
}

public List get(UserBean user)
{

String firstName = null;
String lastName = null;

AndFilter andFilter = null;

firstName = user.getFirstName();
lastName = user.getLastName();

andFilter = new AndFilter();
andFilter.and(new EqualsFilter(UserConstants.OBJECT_CLASS,UserConstants.PERSON));
andFilter.and(new EqualsFilter(UserConstants.FIRST_NAME,firstName));
andFilter.and(new EqualsFilter(UserConstants.LAST_NAME,lastName));

return ldapTemplate.search(UserConstants.BLANK, andFilter.encode(),new UserMapper());
}

public void add(UserBean user)
{
Attributes userAttributes = null;
BasicAttribute userBasicAttribute = null;
DistinguishedName userDN = null;


userAttributes = new BasicAttributes();
userBasicAttribute = new BasicAttribute(UserConstants.OBJECT_CLASS);

userBasicAttribute.add(UserConstants.PERSON);
userAttributes.put(userBasicAttribute);
userAttributes.put(UserConstants.FIRST_NAME, user.getFirstName());
userAttributes.put(UserConstants.LAST_NAME, user.getLastName());

userDN = new DistinguishedName(UserConstants.BLANK);
userDN.add(UserConstants.FIRST_NAME, user.getFirstName());

ldapTemplate.bind(userDN, null, userAttributes);
}

public void modify(UserBean user)
{
Attributes userAttributes = null;
BasicAttribute userBasicAttribute = null;
DistinguishedName userDN = null;


userAttributes = new BasicAttributes();
userBasicAttribute = new BasicAttribute(UserConstants.OBJECT_CLASS);

userBasicAttribute.add(UserConstants.PERSON);
userAttributes.put(userBasicAttribute);
userAttributes.put(UserConstants.FIRST_NAME, user.getFirstName());
userAttributes.put(UserConstants.LAST_NAME, user.getLastName());

userDN = new DistinguishedName(UserConstants.BLANK);
userDN.add(UserConstants.FIRST_NAME, user.getLastName());

ldapTemplate.rebind(userDN, null, userAttributes);
}

public void remove(UserBean user)
{
DistinguishedName userDN = new DistinguishedName(UserConstants.BLANK);
userDN.add(UserConstants.FIRST_NAME, user.getFirstName());
ldapTemplate.unbind(userDN);
}
}


5. Write a test harness to see if all these gule together & work

Well, we have reached the finish line ! The only remaining step is to see if all these gel together !

Here's my test client :-

package com.org.sandeep.client;

import com.org.sandeep.bean.UserBean;
import com.org.sandeep.dao.UserDAO;

import java.util.List;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.dao.DataAccessException;


public class UserClient
{

public static void main(String[] args)
{
try
{
Resource resource = new ClassPathResource("applicationContext.xml");
BeanFactory factory = new XmlBeanFactory(resource);
UserDAO userDAO = (UserDAO)factory.getBean("user");

UserBean userBean = new UserBean("Sandeep","Seshan");

userDAO.add(userBean);

System.out.println("Done");

List userList = userDAO.get(userBean);

System.out.println("User "+userList);

userDAO.remove(userBean);

System.out.println("Done & Deleted");

}
catch (DataAccessException objDataAccessException )
{
objDataAccessException .printStackTrace();
}
}
}


That's it ! It's that simple !


No comments: