Archive

Posts Tagged ‘Java’

Ldap User Management with Spring LDAP

December 25th, 2009 2 comments

Java Naming and Directory Interface (JDNI) is the API used for LDAP programming on the Java platform. JNDI makes far too much work of simple procedures, such as ensuring that resources have been properly opened and closed. In addition, most JNDI methods throw checked exceptions, which are time-consuming to handle.

Spring LDAP provides a sophisticated wrapper API on top of JNDI to make LDAP Programming easier. Here i will show you on how to write, update and delete entries on ldap server using Spring LDAP.

Download Spring Ldap from http://www.springsource.com/download/community

Spring Configuration

Create an LdapContextSource and an LdapTemplate object. Configure the ldapServer url, baseDn and optionally userDn, password if anonymous access is not allowed. Also inject the LdapTemplate into DAO.

<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
    <property name="url" value="ldap://localhost:389" />
    <property name="base" value="dc=javachap,dc=com" />
    <property name="userDn" value="cn=Manager,dc=javachap,dc=com" />
    <property name="password" value="mypassword" />
</bean>

<bean id="ldapTemplate">
    <constructor-arg ref="contextSource" />
</bean>

<bean id="userService">
    <property name="ldapTemplate" ref="ldapTemplate"/>
</bean>

Domain

Create a simple user domain interface and implementation which holds the basic information about the user. The information with in this user  object will be persisted to the Ldap.

package com.javachap.domain;

public interface User {

    String getUserName();

    void setUserName(String userName);

    String getFirstName();

    void setFirstName(String firstName);

    String getLastName();

    void setLastName(String lastName);

    String getEmail();

    void setEmail(String email);

    String getPassword();

    void setPassword(String password);

    String getDepartment();

    void setDepartment(String departement);

    String[] getGroups();

    void setGroups(String[] groups);
}
package com.javachap.domain.impl;

import com.javachap.domain.User;

public class UserImpl implements User {

    private static final long serialVersionUID = 7487133273442955818L;

    private String userName;
    private String firstName;
    private String lastName;
    private String email;
    private String password;
    private String department;
    private String groups[];

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getFirstName() {
        return firstName;
    }

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

    public String getLastName() {
        return lastName;
    }

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

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public String[] getGroups() {
        return groups;
    }

    public void setGroups(String[] groups) {
        this.groups = groups;
    }

    public String toString() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("UserImpl[");
        buffer.append("id = ").append(id);
        buffer.append(" userName = ").append(userName);
        buffer.append(" email = ").append(email);
        buffer.append(" firstName = ").append(firstName);
        buffer.append(" lastName = ").append(lastName);
        buffer.append(" password = ").append(password);
        buffer.append("]");
        return buffer.toString();
    }
}

User DAO

LdapTemplate is built on the same principles as the JdbcTemplate in Spring JDBC. It completely eliminates the need to worry about creating and closing DirContext and looping through NamingEnumeration. It also provides a more comprehensive unchecked exception hierarchy, built on Spring’s DataAccessException.

package com.javachap.service;

import java.util.List;

/**
 * UserService
 *
 * @author JavaChap
 */

public interface UserService {

    User getUser(final String email);

    User save(final User user);

    boolean authenticate(final String userName, final String password);

    List<User> getUsers(final String pattern);

    void delete(final User user);
}
package com.javachap.service.impl;

import java.io.UnsupportedEncodingException;

/**
 * UserService Implementation
 *
 * @author JavaChap
 */

public class UserServiceImpl implements UserService {

    private static final long serialVersionUID = 4889152297004460837L;

    public static final String BASE_DN = "dc=javachap,dc=com";

    private static class UserAttributesMapper implements AttributesMapper {

        public Object mapFromAttributes(Attributes attrs)
                throws NamingException {
            User user = (User) AppUtils.getBean("user");
            if (attrs.get("uid") != null) {
                user.setUserName((String) attrs.get("uid").get());
            }
            if (attrs.get("cn") != null) {
                user.setFirstName((String) attrs.get("cn").get());
            }
            if (attrs.get("sn") != null) {
                user.setLastName((String) attrs.get("sn").get());
            }
            if (attrs.get("mail") != null) {
                user.setEmail((String) attrs.get("mail").get());
            }
            return user;
        }
    }

    private LdapTemplate ldapTemplate;

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

    public boolean authenticate(String userName, String password) {
        AndFilter filter = new AndFilter();
        filter.and(new EqualsFilter("objectclass", "person")).and(
                new EqualsFilter("uid", userName));
        return ldapTemplate.authenticate(DistinguishedName.EMPTY_PATH, filter
                .toString(), password);
    }

    public User getUser(final String userName) {
        AndFilter filter = new AndFilter();
        filter.and(new EqualsFilter("objectclass", "person")).and(
                new EqualsFilter("uid", userName));
        List<User> users = ldapTemplate.search(DistinguishedName.EMPTY_PATH,
                filter.encode(), new UserAttributesMapper());
        if (!users.isEmpty()) {
            return users.get(0);
        }
        return null;
    }

    public List<User> getUsers(final String pattern) {
        AndFilter filter = new AndFilter();
        filter.and(new EqualsFilter("objectclass", "person"));
        if (pattern != null) {
            filter.and(new LikeFilter("uid", pattern));
        }
        List<User> users = ldapTemplate.search(DistinguishedName.EMPTY_PATH,
                filter.encode(), new UserAttributesMapper());
        return users;
    }

    public User save(final User user) {
        Name dn = buildDn(user);
        ldapTemplate.bind(dn, null, buildAttributes(user));

        // Update Groups
        for (String group : user.getGroups()) {
            try {
                DistinguishedName groupDn = new DistinguishedName();
                groupDn.add("ou", "Groups");
                groupDn.add("cn", group);
                DirContextOperations context = ldapTemplate
                        .lookupContext(groupDn);
                context.addAttributeValue("memberUid", user.getUserName());
                ldapTemplate.modifyAttributes(context);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return user;
    }

    public User update(final User user) {
        Name dn = buildDn(user);
        ldapTemplate.rebind(dn, null, buildAttributes(user));
        return user;
    }

    public void delete(User user) {
        Name dn = buildDn(user);
        ldapTemplate.unbind(dn);
    }

    private Name buildDn(final User user) {
        DistinguishedName dn = new DistinguishedName();
        dn.add("ou", "People");
        if (user.getDepartment() != null) {
            dn.add("ou", user.getDepartment());
        }
        dn.add("uid", user.getUserName());
        return dn;
    }

    private Attributes buildAttributes(final User user) {
        Attributes attrs = new BasicAttributes();
        BasicAttribute ocattr = new BasicAttribute("objectclass");
        ocattr.add("person");
        ocattr.add("inetOrgPerson");
        attrs.put(ocattr);
        attrs.put("cn", user.getFirstName());
        attrs.put("sn", user.getLastName());
        attrs.put("userPassword", "{SHA}" + this.encrypt(user.getPassword()));
        attrs.put("mail", user.getEmail());

        return attrs;
    }

    private String encrypt(final String plaintext) {
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance("SHA");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e.getMessage());
        }
        try {
            md.update(plaintext.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e.getMessage());
        }
        byte raw[] = md.digest();
        String hash = (new BASE64Encoder()).encode(raw);
        return hash;
    }
}

Test class

package com.javachap.service.impl;

import org.apache.log4j.BasicConfigurator;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.ClassPathResource;

import com.javachap.domain.User;
import com.javachap.domain.impl.UserImpl;
import com.javachap.service.ServiceUtils;
import com.javachap.service.UserService;
import com.javachap.utils.AppUtils;

public class Test {

    public static void main(String args[]) {
        BasicConfigurator.configure();
        GenericApplicationContext appContext = new GenericApplicationContext();
        XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(
                appContext);
        xmlReader.loadBeanDefinitions(new ClassPathResource(
                "applicationContext.xml"));
        appContext.refresh();
        AppUtils.setApplicationContext(appContext);

        UserService userService = ServiceUtils.getUserService();
        // User Authenticate
        boolean authenticated = userService.authenticate("user@javachap.com",
                "javachap");
        System.out.println("Authenticated: " + authenticated);

        // User Save
        User user = new UserImpl();
        user.setUserName("javachap2");
        user.setFirstName("java");
        user.setLastName("chap");
        user.setEmail("user2@javachap.com");
        user.setPassword("chapjava");
        user.setDepartment("Engineering");
        user.setGroups(new String[] { "Admin", "HudsonAdmin", "WikiAdmin" });
        userService.save(user);

        // User Get
        user = userService.getUser("javachap2");
        System.out.println("User:" + user);
    }
}

References

http://today.java.net/article/2006/04/14/ldaptemplate-ldap-programming-java-made-simple

Categories: Java Tags: , ,

CRUD Application : Struts 1, Hibernate and MySQL

December 27th, 2008 24 comments

Sample Struts, Hibernate CRUD application with i18n and validation support.

Download the source code leadapp.zip (5.66 Mb)

Building and deploying the application

  • Create a schema/user using the schema.sql  file in src/database
  • Open build.properties and modify “tomcat.home” property to point your tomcat home directory
  • Database username/password can be configured in the hibernate config file \WebRoot\WEB-INF\hibernate.cfg.xml
  • Issue “ant clean deploy” command to build and deploy the application to tomcat.
  • Access the application http://localhost:8080/leadapp  (assuming tomcat is running on 8080 port)
  • username/password is user@javachap.com/javachap

I hosted this application on stax.net, access it from here http://hw4999.dvkvarma.staxapps.net

Application Screenshots

Login Page

Lead Listing Page

Lead Create Page

Categories: Java Tags: , , , , ,

Debugging in Java

December 22nd, 2008 No comments

The Java Debugger

The Java Debugger (jdb) helps you find and fix bugs in Java language programs both locally and on the server. A VM that is to be debugged with jdb must be started with the following options:

-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000

-Xdebug will instruct the VM to enable debugging support.

-Xrunjdwp <options> will load the JPDA reference implementation of JDWP.

Xrunjdwp Options

  • transport=dt_socket instructs JVM that the debugger connections will be made through a socket
  • address=8000 instructs JVM that the socket will be opened on port 8000
  • suspend=n If configured as ‘y’ the JVM starts in suspended mode and stays suspended until a debugger attaches to it. This is helpful if you want to start debugging as soon as the JVM starts.
  • server=y If “y”, listen for a debugger application to attach; otherwise, attach to the debugger application at the specified address. 

for more options visit http://java.sun.com/j2se/1.4.2/docs/guide/jpda/conninv.html#Xrunjdwp To enable debugging in tomcat set an environment variable called JAVA_OPTS.

set JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n

View Heap Dump

Hit Ctrl + Break to dump a list of threads and all their held locks to stdout. If your app is stuck, you can figure out what it is doing. If it is deadlocked, sometimes the JVM can even tell you exactly which threads are involved. If you are running the java application as a service then you will need a tool to send a SIGINT signal to the JVM. One such tool is SendSignal

Record heap dump on OutOfMemory

The most common problem that we run into is OutOfMemoryError, this happens when there is memory leakage. By specifying some options we can instruct JVM to take the heap dump when the JVM runs out of memory. The JVM must be configured with following options to record the heap dump

-Xrunhprof:heap=dump,format=b
-XX:+HeapDumpOnOutOfMemoryError

If you don’t want big dump files in the application working directory then the HeapDumpPath option can be used to specify an alternative location, for  example -XX:HeapDumpPath=/disk2/dumps will cause the heap dump to be generated in the /disk2/dumps directory.

Note: To list all available options, use java -Xrunhprof:help

Reference

Categories: Java Tags: ,

Java over the Cloud

December 19th, 2008 1 comment

I just found this site called Stax Network, http://www.stax.net.

Stax is offering a platform as a service product for Java applications, similar to Amazon EC2. Allows developers to create, test and deploy Java applications very easily. This will be extremely useful for the startups that build there apps on java, without having to build out their own physical infrastructure.

The best part is you can scale the apps on demand from a single shared server to a load-balanced cluster and  you will only pay for the computing resources that you use.

Applied for the beta, Waiting for the key. Its free until it moves out of beta.

Update: Got the invitation code today, tried a sample application, Worked as expected.  The admin UI has few glitches in firefox, but worked fine in Chrome.

Update2: Deployed my sample application i.e. built on top of hibernate/struts and mysql.  No issues while running on single server. But when it is load balanced on 2 servers, running into invalid session error. May be the requests are not sent to the same server. I need to figure this out, will update you on this. Contact me if you want the source code of the sample app. It uses hibernate and struts, db is mysql

You can access the sample app at http://hw4999.dvkvarma.staxapps.net

Categories: Java Tags: ,