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 !


Monday, January 19, 2009

FlashBack to JDeveloper 10.1.2.2.0

" I want to migrate my application code base from JDeveloper 10.1.3 to 10.1.2 "

One of my colleagues recently faced this problem of moving the source code of a J2EE Application from Jdeveloper 10.1.3 to JDeveloper 10.1.2.

We faced a few issues during the process & here's a partial list of the issues we faced :-

1. Platform Migration Issues

JDeveloper 10.1.2.x makes use of JDK 1.4, whereas JDeveloper 10.1.3.x makes use of JDK 1.5. Hence, the move involves changing Java code to ensure that the code is stripped of all the useful features we are used to in JDK 1.5. E.g: contains() method of java.lang.String, etc.

2. “Error (n, m): identifier OracleJspRuntime not found”

We noticed that we had to manually add the JSP Runtime Library to the new Project in jDeveloper 10.1.2.

Please see one of my previous posts for more information about this problem.

3. Error: JSP files must reside in the server root directory or a subdirectory

We noticed that we had to manually change the HTML Root Directory to the new Project in jDeveloper 10.1.2.

Please see one of my previous posts for more information about this problem.

4. Change XSD references to DTDs

The JDeveloper 10.1.2.x does not recognize references to XSDs in web.xml & other configuration files. Hence, we had to manually change these references to DTDs.

Well, we managed to backport the application after resolving these problems. I would like to hear if there are any other similar experiences.