Monthly Archives: May 2010

Using the Java Naming and Directory Interface (JNDI) (Part 3)

Introduction

In the last two posts about using JNDI, I have shown examples of how one can get and add information in a number of ways. The first post discussed LDAP servers and showed a quick program one could use to retrieve entries stored in the server.  The entry was represented as a collection of attributes.  The second post demonstrated a number of ways to store and retrieve Java objects on the server.  The two examples demonstrated how objects could be saved in base-64 or as a reference to another object.  This post will demonstrate how a set of attributes can be turned into a Java object and how to store that object as a set of attributes.

Setup

To make a Java object get saved as a set of entry attributes, I have to refactor the User class.  By implementing the DirContext, the class will organize itself into a set of attributes. Here is the new version of User, AttrUser.

public class AttrUser extends DirContextAdapter {

public static final String INIT_VALUE = “none”;

public static final String NAME_ATTR = “cn”;

public static final String UID_ATTR = “uid”;

public static final String EMAIL_ATTR = “mail”;

public static final String SN_ATTR = “sn”;

private Attributes attrs;


public AttrUser() {

attrs = new BasicAttributes(true);

Attribute oc = new BasicAttribute(“objectclass”);

oc.add(“inetOrgPerson”);

oc.add(“top”);


attrs.put(oc);

attrs.put(NAME_ATTR, INIT_VALUE);

attrs.put(UID_ATTR, INIT_VALUE);

attrs.put(EMAIL_ATTR, INIT_VALUE);

attrs.put(SN_ATTR, INIT_VALUE);

}

public String getName() {

String name = null;

try {

Attribute a = attrs.get(NAME_ATTR);

name = a.get().toString();

} catch (NamingException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return name;

}


public void setName(String name) {

Attribute a = attrs.get(NAME_ATTR);

a.clear();

a.add(name);

}


public String getUserid() {

String userid = null;

try {

Attribute a = attrs.get(UID_ATTR);

userid = a.get().toString();

} catch(NamingException ne) {

ne.printStackTrace();

}

return userid;

}


public void setUserid(String userid) {

Attribute a = attrs.get(UID_ATTR);

a.clear();

a.add(userid);

}


public String getEmail() {

String email = null;

try {

Attribute a = attrs.get(EMAIL_ATTR);

email = a.get().toString();

} catch(NamingException ne) {

ne.printStackTrace();

}

return email;

}

public void setEmail(String email) {

Attribute a = attrs.get(EMAIL_ATTR);

a.clear();

a.add(email);

}


public void setLastname(String lastname) {

Attribute a = attrs.get(SN_ATTR);

a.clear();

a.add(lastname);

}


public String getLastname() {

String lastname = null;

try {

Attribute a = attrs.get(SN_ATTR);

lastname = a.get().toString();

} catch(NamingException ne) {

ne.printStackTrace();

}

return lastname;

}

public Attributes getAttributes(Name arg0) throws NamingException {

return getAttributes(arg0.toString());

}


public Attributes getAttributes(String arg0) throws NamingException {

if(arg0 != null && arg0.length() != 0) {

throw new NameNotFoundException();

}

return (Attributes)attrs.clone();

}


@Override

public Attributes getAttributes(Name arg0, String[] arg1) throws NamingException {

return getAttributes(arg0.toString(), arg1);

}


@Override

public Attributes getAttributes(String arg0, String[] arg1) throws NamingException {

if (arg0 != null && arg0.length() > 0) {

throw new NameNotFoundException();

}

Attributes ret = new BasicAttributes(true);


for (String id: arg1) {

ret.put(attrs.get(id));

}

return ret;

}

}

The class looks a lot different now. Notice that backed the whole class with an instance of Attributes.  I thought the code looked cleaner that way.  Also notice that I extended a DirContextAdapter instead of implementing DirContext.  To keep clean looking code, created an adapter.   Another reason is because the context provider looks to store java objects in a certain order. The provider will look to store a java object as a reference first, serialized second and a context third.  So If I had extended User.java, the example would have not worked.  Another small difference is I now added lastname as one of the attributes to store.  Lastname is a required attribute of InetOrgPerson, and since I want AttrUser to be accepted like an InetOrgPerson, it needs to keep track like one. The other class needed is the factory that will turn the context attributes back into an AttrUser.

public class AttrUserFactory implements DirObjectFactory {

@Override

public Object getObjectInstance(Object arg0, Name arg1, Context arg2,

Hashtable arg3) throws Exception {

// because this is for an attributes based object, this factory method simply returns null.

return null;

}

@Override

public Object getObjectInstance(Object arg0, Name arg1, Context arg2, Hashtable arg3, Attributes arg4) throws Exception {

AttrUser u = null;

if (checkObject(arg0, arg4)) {

u = new AttrUser();

Attribute a = arg4.get(AttrUser.NAME_ATTR);

u.setName(a.get().toString());


a = arg4.get(AttrUser.UID_ATTR);

u.setUserid(a.get().toString());


a = arg4.get(AttrUser.EMAIL_ATTR);

u.setEmail(a.get().toString());


a = arg4.get(AttrUser.SN_ATTR);

u.setLastname(a.get().toString());

}

return u;

}


private boolean checkObject(Object o, Attributes att) {

boolean isAttrUser = false;


if (o instanceof DirContext) {

if(att != null && att.size() >= 4) {

boolean areAttrThere = true;

areAttrThere &= (att.get(AttrUser.NAME_ATTR) != null);

areAttrThere &= (att.get(AttrUser.SN_ATTR) != null);

areAttrThere &= (att.get(AttrUser.EMAIL_ATTR) != null);

areAttrThere &= (att.get(AttrUser.UID_ATTR) != null);

isAttrUser = areAttrThere;

}

}

return isAttrUser;

}

}

This class implements the same DirObjectFactory as the last time but fills out the attribute based function. According to the DirObjectFactory contract, the function needs to return null if it is not a match for that data. Context providers can handle a range of factories.  It goes to each one and runs the factory call.  If the returned value is non-null or an exception, the traversal ends and it returns the value or allows the exception to be caught.  The setup is done, lets put it all together.

The Lookup

public class AttributeLookup {

static Hashtable<String, String> getEnv() {

Hashtable<String, String> env = new Hashtable<String, String>();

env.put(Context.INITIAL_CONTEXT_FACTORY, “com.sun.jndi.ldap.LdapCtxFactory”);

env.put(Context.PROVIDER_URL, “ldap://localhost:10389/ou=Users,dc=example,dc=com”);

env.put(Context.OBJECT_FACTORIES, AttrUserFactory.class.getName());


return env;

}

public static void main(String[] args) {

DirContext ctx = null;

try {

ctx = new InitialDirContext(getEnv());


AttrUser user = new AttrUser();

user.setEmail(“joey@example.com”);

user.setLastname(“Bourne”);

user.setName(“Joey”);

user.setUserid(“joey”);


ctx.rebind(“uid=” + user.getUserid(), user);

// read

AttrUser newUser = (AttrUser)ctx.lookup(“uid=” + user.getUserid());

System.out.println(“email is “ + newUser.getEmail());


} catch (NamingException e) {

e.printStackTrace();

}

finally{

try {

ctx.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

This looks a lot like the other examples that I have covered. The one exception is where I defined my object factory. If I did not include the object factory, the lookup would have returned a set of Attributes like on the first example. The data stored in the server shows this to be true.

dn: uid=joey,ou=Users,dc=example,dc=com

objectClass: organizationalPerson

objectClass: person

objectClass: inetOrgPerson

objectClass: top

cn: Joey

sn: Bourne

mail: joey@example.com

uid: joey

In fact, I could have just used the object factory and read any user entry from the server and have the entries turned into AttrUser objects and not have to worry about implementing DirContext.

That was Fun

That is the last demonstration I have right now for JNDI.  I have shown a simple entry lookup, how to store a Java Object directly and indirectly on the server to objects that create entries and those entries can be read back into a Java object.  If you have anymore JNDI knowledge to share, leave a comment.  The source code can be found at https://github.com/darylmathison/jndi-example.

Advertisement

One for the Little Guy

Motivation

Those who may know me know that I was recently laid off. During that time one of my uncles gave me a challenge I could not refuse, “Could you put all of my customer contacts in a computer?” I was intrigued because I thought everyone put information that valuable in a database for lightning quick look-ups. I was also in need of money so I jumped at the chance to help family.  After further discussion, I found that he had a worse problem, no website.

Persistent Problem Needs a Solution

While I took up the cause to place the virtual placard for his business on the web, the first problem was still knocking at the door of my mind persistent and unyielding.  I starting thinking about how many times this problem has been solved and wouldn’t solving it be like reinventing the wheel.  So I looked at other solutions but none of them really wanted me to jump up and say, “Eureka!”  I even thought of just using MS Outlook to solve the problem.  On further internal discussions, MS Outlook is actually almost perfect for their needs.  Even storing directions to the customer’s homes could be done in the notes section.

Solution Found

Well now that I figured it out, it is only a matter of implementing the plan putting in notebooks upon notebooks of customer data into MS Outlook.  That thought exercise was a  lot of fun and now I can get to the business of Java development.  My uncle is a small business owner who doesn’t need to have access to enterprise level software.  In fact, some would saw he doesn’t deserve it.  Wait a minute!  The only thing that is small in a small business is the number of workers in the company.  Everything else is big, why can’t software grow with the company too?

New Solution for a Old Problem

The answer to the question is money.  An enterprise class solution gets enterprise class money.  Not everyone can afford enterprise solutions because they all don’t have enterprise money coming in.  Even those that have the money are told by busy, honest consultants that they don’t need it.  They say things like, “Use Outlook” and go on to the next project.  However, to show future employers what I can do, designing a enterprise PIM (Personal Information Management) software would be a good way to do it.  It is familiar enough to identify, but not as simple as “Hello World.”  It also can be solved in a number of ways.  I could make a solution that is strictly for an enterprise but that would not be for the “little guy.”  If I could form a solution that can run on a single PC just as well as a network that would be for the “little guy.”  Then people like my uncle can dream big and have a solution that can grow with them.

Spring into Action

Pardon the pun.  I was doing some research on the Spring Framework when I found these links.  Yes they are 5 years old but it still got me thinking in Spring.

The Spring series, Part 1: Introduction to the Spring framework

The Spring series, Part 2: When Hibernate meets Spring

The Spring series, Part 3: Swing into Spring MVC

The Spring series, Part 4: 1-2-3 messaging with Spring JMS

Tips for the Home Coder

I don’t know about anyone else but I spend a good amount of time coding at home.  It is nice because I get to drink beer and code at the same time.  It is tough to do because I have all my home duties plus getting an hour or two to make something fun or learn somethings.  I started to look at what I do at work to save time since “Time is Money” at work but “Time is Time” at home.  Here is a quick list.

Source Control

It sounds simple enough but I looked over the times where I have blasted my code at home and spent a week restoring while a simple “svn revert” would have done the trick.  For those who are worried about standing up a new server at home, don’t.  There are many sites out there that will host your code for free.  I keep all mine at darylmathisonblog.googlecode.com.  A lot of these sites do this for open source only so if you are making the next best mousetrap, you should take the time and stand one up at your abode.  Now all your changes are in one place that can be easily backed up by someone else or yourself.

Automated Testing

Automated testing is an investment in the future.  In the beginning, it is a real pain to set up, then it becomes awesome time saver. With the click of a button tests that could have taken minutes to finish by hand are done in seconds.  Unit tests can be combined and create test suites, now any change can be tested against the whole system or just that module.  Automated test can run while you make dinner or put the kids to bed.  The other neat thing, if you walk away from a project for a while the tests can give you an idea of how that module is supposed to run so you don’t spend a week of spare time figuring out your own code.  Don’t forget to source control your automated tests too.

Use an IDE

An IDE (Integrated Development Environment) will pay back in the long run.  When I am compiling, the error log created contains links to the offending code.  When I create a new Java class, I use templates to initially set up the class.  When I am starting up a new project, the IDE organizes it to make it easy to deploy.  Good ones are expandable so I can create a tool to help me create a solution if one hasn’t been created already.  I have been using Eclipse lately and the amount of add-ons it has blows me away.  I can install servers on my computer and Eclipse will start up and shutdown the server as needed.  It automatically builds source code so I know exactly where the code doesn’t compile before I do a test.

Well, there is my quick list.  If you can think of anything else, post a comment.

Links

www.eclipse.org

www.googlecode.com

Using the Java Naming and Directory Interface (JNDI) (Part 2)

In my last post, I discussed directory servers and a quick example of how to query them. To read part one click here. JNDI can be used to store whole objects into a LDAP server. In this entry, a java object will be stored on the server directly.

First example uses a mechanism that Java has had since the beginning, serialization. I need to define a user class. The following class is from User.java:

public class User implements Serializable {

private static final long serialVersionUID = 3999866113934116781L;

private String name;

private String userid;

private String email;


public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getUserid() {

return userid;

}

public void setUserid(String userid) {

this.userid = userid;

}

public String getEmail() {

return email;

}

public void setEmail(String email) {

this.email = email;

}

}

It is just a number of setters and getters for a name, user id and email. It does implement java.io.Serializable to make it work for storage. Here is the class that uses User.java.

public class JavaObjectLookup {

static Hashtable<String, String> getEnv() {

Hashtable<String, String> env = new Hashtable<String, String>();

env.put(Context.INITIAL_CONTEXT_FACTORY, “com.sun.jndi.ldap.LdapCtxFactory”);

env.put(Context.PROVIDER_URL, “ldap://localhost:10389/ou=java,dc=example,dc=com”);

return env;

}

public static void main(String[] args) {

DirContext ctx = null;

try {

ctx = new InitialDirContext(getEnv());

// first bind an object to the Directory

User user = new User();

user.setName(“Joey”);

user.setUserid(“joey”);

user.setEmail(“joey@example.com”);


ctx.bind(“cn=joey”, user);


User u = (User)ctx.lookup(“cn=joey”);


System.out.println( “User’s email is “ + u.getEmail());

} catch (NamingException e) {

e.printStackTrace();

}

finally {

try {

ctx.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

}


Notice that the connection URL has been changed to “ou=java,dc=example,dc=com” so the object will be put into the java organizational unit. On the LDAP server, the entry looks like the following:

dn: cn=joey,ou=java,dc=example,dc=com

objectClass: javaSerializedObject

objectClass: javaObject

objectClass: javaContainer

objectClass: top

cn: joey

javaClassName: org.mathison.example.jndi.User

javaSerializedData:: rO0ABXNyAB5vcmcubWF0aGlzb24uZXhhbXBsZS5qbmRpLlVzZXI3gmE

J1ipHrQIAA0wABWVtYWlsdAASTGphdmEvbGFuZy9TdHJpbmc7TAAEbmFtZXEAfgABTAAGdXNlcm

lkcQB+AAF4cHQAEGpvZXlAZXhhbXBsZS5jb210AARKb2V5dAAEam9leQ==

javaClassNames: org.mathison.example.jndi.User

javaClassNames: java.lang.Object

javaClassNames: java.io.Serializable

The instance is stored as a base-64 encoded string. This is good for storing an instance that a lot of different java processes will access such as a printer driver. But what if a service wants to register its services on a LDAP server? It is no good to store a copy of a service on a directory server because a service is only useful if it can serve. It would be better if the entry just “referred” at the service rather being a copy of it. This is the rational for using javax.naming.Reference. To store a reference, a developer can have the class implement java.naming.Referenceable and call bind on the object or create a reference and bind the reference. On retrieving the reference, a developer can create an object from the information in the reference instance. If an object factory is used, the lookup returns an instance of your class. The following example has ReferUser that implements Referencable and uses an object factory.

Example code:

public class JavaObjectRefLookup {


static Hashtable<String, String> getEnv() {

Hashtable<String, String> env = new Hashtable<String, String>();


env.put(Context.INITIAL_CONTEXT_FACTORY, “com.sun.jndi.ldap.LdapCtxFactory”);

env.put(Context.PROVIDER_URL, “ldap://localhost:10389/ou=java,dc=example,dc=com”);


return env;

}

/**

* @param args

*/

public static void main(String[] args) {

DirContext ctx = null;

try {

ctx = new InitialDirContext(getEnv());


// first bind an object to the Directory

ReferUser user = new ReferUser();

user.setName(“Joey”);

user.setUserid(“joey”);

user.setEmail(“joey@example.com”);


ctx.rebind(“cn=joey”, user);


ReferUser u = (ReferUser)ctx.lookup(“cn=joey”);


System.out.println( “User’s email is “ + u.getEmail());

} catch (NamingException e) {

e.printStackTrace();

}

finally{

try {

ctx.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

import org.mathison.jndi.example2.User;

public class ReferUser extends User implements Referenceable {

public Reference getReference() throws NamingException {

Reference ref = new Reference(this.getClass().getName(),

ReferUserFactory.class.getName(), null);

ref.add(new StringRefAddr(“name”, getName()));

ref.add(new StringRefAddr(“mail”, getEmail()));

ref.add(new StringRefAddr(“uid”, getUserid()));

return ref;

}

}

import javax.naming.directory.Attributes;

import javax.naming.spi.DirObjectFactory;

public class ReferUserFactory implements DirObjectFactory {

@Override

public Object getObjectInstance(Object arg0, Name arg1, Context arg2, Hashtable arg3, Attributes arg4) throws Exception {

return getObjectInstance(arg0, arg1, arg2, arg3);

}

@Override

public Object getObjectInstance(Object arg0, Name arg1, Context arg2, Hashtable arg3) throws Exception {

ReferUser user = null;

if (arg0 instanceof Reference){

Reference ref = (Reference)arg0;

if(ref.getClassName().equals(ReferUser.class.getName())) {

RefAddr name = ref.get(“name”);

RefAddr uid = ref.get(“uid”);

RefAddr email = ref.get(“mail”);


user = new ReferUser();

user.setName(name.getContent().toString());

user.setEmail(email.getContent().toString());

user.setUserid(uid.getContent().toString());

}

}

return user;

}

}

Here is what you will find if you take a look at the Directory Server:

dn: cn=joey,ou=java,dc=example,dc=com

objectClass: javaContainer

objectClass: javaNamingReference

objectClass: javaObject

objectClass: top

cn: joey

javaClassName: org.mathison.example.jndi.ReferUser

javaFactory: org.mathison.example.jndi.ReferUserFactory

javaReferenceAddress: #0#name#Joey

javaReferenceAddress: #1#mail#joey@example.com

javaReferenceAddress: #2#uid#joey

Notice that the object’s attributes are now stored in javaReferenceAddress attributes. Also notice how each of these techniques used special java storage attributes to the job done. Well, the next part of this blog series will tackle creating a object without special java structures. All of this code can be downloaded from https://github.com/darylmathison/jndi-example via subversion or your browser.