Building a simple microservice using Spring Boot

In this short post, I will show how to build a simple JPA Microservice using Spring Boot. Spring Boot makes it easy to create stand-alone based applications that you can run and need very little Spring configuration as we will see in this short tutorial.

springboot

For an explanation about microservices, read this article of Martin Fowler.

As I was saying we are going to use Spring Boot. First start of with a simple java maven enabled project in the IDE of your choice. We will first start with the .pom file to get all the Spring dependencies right. I am going to build a simple ReferenceDataService microservice which can deliver some simple reference data such as a list of countries. Let take a look at the pom file:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ReferenceDataService</artifactId>
    <groupId>nl.redrock</groupId>
    <packaging>jar</packaging>
    <name>ReferenceData Microservice</name>
    <description>ReferenceData Service</description>
    <version>1.0</version>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
    </parent>
    
	
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Oracle JDBC driver -->
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc7</artifactId>
            <version>7.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

As you can see, we the 1.5.2.RELEASE as a basis. We then have 4 dependencies. We need spring-boot-starter-web for libraries to creating the rest service, we need spring-boot-starter-data-jpa for the jpa capability, we need spring-boot-starter-test for testing capabilities and last an ojdbc.jar because the data is located in a Oracle database. I am also adding the spring-boot-maven-plugin to be able to run it from maven using Tomcat.

Let start of with the entry point….the application. This is the starting point of our simple service. It will look like this:

package nl.redrock.referencedataservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ReferenceDataApplication {

    public static void main(String[] args) {
        SpringApplication.run(ReferenceDataApplication.class, args);
    }
}

As you can see, it is very simple. Use the @SpringBootApplication annotation to tag it as a Spring Boot application and that is it. Next we want a class which can retrieve data from database. Spring has a bunch helpful classes and templates for you to help with this. I already have a pre-filled oracle database running with 1 table called country. It has 3 columns….ID, CODE and NAME.

database

So as a first step we will create a Country class which maps to the table.

package nl.redrock.referencedataservice.data;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Country {
    
    @Id
    private int id;
    private String code;
    private String name;

    /**
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * @return the code
     */
    public String getCode() {
        return code;
    }

    /**
     * @param code the code to set
     */
    public void setCode(String code) {
        this.code = code;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return String.format("Country[id=%d, code='%s', name='%s']",
                id, code, name);
    }
}

As you can see this is also a very simple POJO with the 3 attributes mapping to the 3 columns with getter and setters. The annotation which does al the magic here is @Entity. This tells spring that it can be used for Object Relational Mapping. Next we we will create a class which will fetch the data.

package nl.redrock.referencedataservice.repository;

import nl.redrock.referencedataservice.data.Country;
import org.springframework.data.repository.CrudRepository;

public interface CountryRepository extends CrudRepository<Country, Long> {

    Country findById(int id);
}

And again….not much coding here. Just a simple interface which extends Spring’s CRUDRepository. We define just 1 extra interface for retrieving a country by its id. And basically that is it. We just have 1 thing left to do, and that is telling Spring which database to connect to. This is easily done by adding an application.properties to you classpath with all the settings in so it is automatically picked up by Spring.

#port to run apache on
server.port=8888

# Oracle settings
spring.datasource.url=jdbc:oracle:thin:@private.eu-west-1.compute.amazonaws.com:1521:xe
spring.datasource.username=SECRET
spring.datasource.password=SECRET
spring.datasource.driver-class-oracle.jdbc.driver.OracleDriver

#set sql level to debug to see all the sql statements
logging.level.org.hibernate.SQL=debug
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n

The server.port is a setting which you can use to adjust the port tomcat runs on. Next up are the oracle database connection settings. And last of all some logging tweaking. A last thing to do is to make a unit test to see if it al works.

package nl.redrock.referencedataservice.repository;

import junit.framework.TestCase;
import nl.redrock.referencedataservice.data.Country;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace=Replace.NONE)
public class CountryRepositoryTest extends TestCase {
    
    @Autowired
    CountryRepository countryRepository;
    
    @Test
    public void testCountryRepository(){
        Country c = this.countryRepository.findById(1);
        assertTrue(c != null);
        assertTrue(c.getCode().equals("ac"));
        assertTrue(c.getName().equals("Ascension Island"));
    }
}

The things to look for here are the @DataJpaTest annotation which tells Spring it is a JPA test. The @AutoConfigureTestDatabase(replace=Replace.NONE) annotation tells Spring to not replace the application default DataSource.

Run the test:

UT

As you can see, we can fetch data from the database with minimal coding.

Now for the service part. Spring also has easy ways to accommodate this using the @RestController annotation.

package nl.redrock.referencedataservice.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import nl.redrock.referencedataservice.data.Country;
import nl.redrock.referencedataservice.repository.CountryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/referencedataservice/countries")
public class ReferenceDataController {
    
    private final static Logger LOGGER = Logger.getLogger(ReferenceDataController.class.getName());
    
    @Autowired
    CountryRepository countryRepository;


    @RequestMapping("/{id}")
    public Country getCountry(@PathVariable int id) {
        Country result;
        LOGGER.log(Level.INFO, "Getting country with id " + id);
        result = countryRepository.findById(id);
        return result;
    }

    @RequestMapping(method = RequestMethod.GET)
    List<Country> getCountries() {
        List<Country> result;
        LOGGER.log(Level.INFO, "Getting all countries");
        result = new ArrayList();
        Iterable<Country> countryList = countryRepository.findAll();
        for (Country country : countryList) {
            result.add(country);
        }
        return result;
    }
}

As you can see we implement 2 operations. 1 to get all the countries and 1 to get a specific country by its id. We use @Autowired to inject the countryRepository. And now for the proof of the pudding. Run mvn clean spring-boot:run and watch maven spin up a Tomcat instance with the referencedataservice application deployed on it. Open up the browser and call:

http://localhost:8888/referencedataservice/countries

countriesResult

Now call http://localhost:8888/referencedataservice/countries/160 to get a specific country

countryResult

As you can see Spring makes it very easy to create rest services with minimal coding. If you want to look into some more advanced microservice features Spring has to offer, have a look here and here to see how you can use microservices in conjunction with Netflixs Eureka server.

SOA Suite 12C: Simple example of using the Spring component

A few days ago, we needed a some business functionality to validate an IBAN account number. You can use inline Java in your BPEL process but there are more elegant ways as you don’t want to pollute your orchestration layer with business functionality. An nice and elegant way, in my opinion, is the use of the Spring component. By using this component you can expose a Java bean as simple as you would by calling another webservice. This way the business functionality can be bundled in a nice .jar which you can also nicely test separate from the rest. Let’s see how this all works.

First of all, I will need to create a simple jar file which contains the function I want to use with an appriopriate interface. I my case I will create a BankAccountUtils class and IBankAccountUtils interface and a simple JUnit testcase to see if my function does what it has to do.

package nl.redrock.soa.utils;

/**
 * BankAccountUtils interface
 * @author Hugo
 */
public interface IBankAccountUtils {
    
    /**
     * Check an account number
     * @param account the account number to check
     * @return true of a valid IBAN, false if not
     */
    public boolean ibanTest(String account);
}
package nl.redrock.soa.utils;

import java.math.BigInteger;

/**
 * BankAccountUtils class
 * @author Hugo
 */
public class BankAccountUtils implements IBankAccountUtils {

    public final int IBANNUMBER_MIN_SIZE = 15;
    public final int IBANNUMBER_MAX_SIZE = 34;
    public final BigInteger IBANNUMBER_MAGIC_NUMBER = new BigInteger("97");

    @Override
    public boolean ibanTest(String accountNumber) {

        boolean result;

        String newAccountNumber = accountNumber.trim();

        // Check that the total IBAN length is correct as per the country. If not, the IBAN is invalid. We could also check
        // for specific length according to country, but for now we won't
        if (newAccountNumber.length() < IBANNUMBER_MIN_SIZE || newAccountNumber.length() > IBANNUMBER_MAX_SIZE) {
            result = false;
        } else {

            // Move the four initial characters to the end of the string.
            newAccountNumber = newAccountNumber.substring(4) + newAccountNumber.substring(0, 4);

            // Replace each letter in the string with two digits, thereby expanding the string, where A = 10, B = 11, ..., Z = 35.
            StringBuilder numericAccountNumber = new StringBuilder();
            for (int i = 0; i < newAccountNumber.length(); i++) {
                numericAccountNumber.append(Character.getNumericValue(newAccountNumber.charAt(i)));
            }

            // Interpret the string as a decimal integer and compute the remainder of that number on division by 97.
            BigInteger ibanNumber = new BigInteger(numericAccountNumber.toString());
            result = ibanNumber.mod(IBANNUMBER_MAGIC_NUMBER).intValue() == 1;
        }
        return result;
    }
}
import nl.redrock.soa.utils.BankAccountUtils;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 * Simple JUnit test class for the BankAccountUtils
 * @author Hugo
 */
public class BankAccountUtilsTest {
    
   
    @Test
    public void ibanTest(){
        assertTrue(new BankAccountUtils().ibanTest("NL28RBOS0569988888"));
        assertTrue(new BankAccountUtils().ibanTest("PL61109010140000071219812874"));
        assertTrue(new BankAccountUtils().ibanTest("CH3608387000001080173"));
        assertTrue(new BankAccountUtils().ibanTest("IT60X0542811101000000123456"));
        assertTrue(new BankAccountUtils().ibanTest("CY17002001280000001200527600"));
        assertTrue(new BankAccountUtils().ibanTest("FI2112345600000785"));
    }
}

Lets run the tests and see if the functionality works:
junit

Now package the class and interface in a nice .jar and we are done for this bit!

Now lets fire up JDeveloper and create a simple composite. Right click on the Project and choose Project Properties. Go to the Libraries and Classpath tab and add the .jar of the Java project we have created above. You can also just drop the .jar in the SCA-INF/lib folder of your project.

ProjectProperties

I created a BPEL process which should process a money transfer.

project

The in- and output look like this:

<?xml version="1.0" encoding="UTF-8"?> 
<schema attributeFormDefault="unqualified"
	elementFormDefault="qualified"
	targetNamespace="http://xmlns.oracle.com/SoaApplication/SpringComponent/ProcesMoneyTransfer"
	xmlns="http://www.w3.org/2001/XMLSchema">
	<element name="process">
		<complexType>
			<sequence>
				<element name="fromAccount" type="string"/>
                                <element name="toAccount" type="string"/>
                                <element name="amount" type="integer"/>
			</sequence>
		</complexType>
	</element>
	<element name="processResponse">
		<complexType>
			<sequence>
				<element name="result" type="boolean"/>
			</sequence>
		</complexType>
	</element>
</schema>

Now for the interesting part. We want to check if the fromAccount and the toAccount are valid IBAN accounts in the BPEL. First we drag the Spring component onto the composite and name the Spring Context.

SpringContext

A new Spring context file will be created. For all you Java-Spring people….this file will look very familiar in the next couple of steps. Open the Spring xml file and select the Weblogic SCA components.

SpringContext

Now drag the Service component onto the XML file. Give it a name and a target and select the interface from the .jar using the cog. For some documentation see here

Service

Save and close it. When you return to your composite you can see that the Spring component has an interface now.

Interface

We do need to make some extra adjustments. We also need to declare the bean which actually holds the implementation so go back and open the context and add the bean and we also need to add an extra interface.java element to the sca:service element to tell it is a Java interface.

<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee" xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">
  <!--Spring Bean definitions go here-->
  <sca:service name="BankAccountUtils" target="int" type="nl.redrock.soa.utils.IBankAccountUtils">
    <interface.java interface="nl.redrock.soa.utils.IBankAccountUtils"/>
  </sca:service>
  <bean name="int" class="nl.redrock.soa.utils.BankAccountUtils"/>
</beans>

Also one thing you will have to do is that you will need to replace the whole spring schema-location with a much simpler version as the one generated or else you will get an error during deployment. Found the solution for this here.

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">

Now we can wire the BPEL to the Spring component.

CreateInterface

You can see it has created a WSDL and a wrapper WSDL in the WSDLs folder. Now you can just call the bean like any other normal partner link.

BPEL

Don’t forget to drop the .jar file also on your server in the /lib directory. The server will need a restart after this to pick up the new .jar file.

Now build and deploy it and call it from the EM test console:

ResultTrue

and the result:

ResultTrue

And there we go. A nice and simple way to implement business functionality which is re-usable and doesn’t clutter your orchestration layer.

Using Java and Spring to connect to an Active Directory

You might find yourself in a position where you want to add, edit or delete accounts in your Active Directory. The OSB itself doesn’t have an adapter for it so how do you resolve this?

One of the options is to write a custom java library, add it to your domain and invoke it using a java-callout. Let’s start by making a simple Java project making use of Maven and Spring to connect to a Active Directory. Spring has a LDAP component which make it easy to connect to directories. Our pom file will look like this:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>nl.redrock</groupId>
    <artifactId>ActiveDirectoryAdapter</artifactId>
    <packaging>jar</packaging>
    <version>1.0</version>
    <name>Java Active Directory adapter</name>
 
    <properties>
        <spring.version>3.0.2.RELEASE</spring.version>
        <java.version>1.6</java.version>
        <ssl.trustStore>jssecacerts</ssl.trustStore>
    </properties>
 
    <build>
        <plugins>
            <!-- plugin for setting the compiler version -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <!-- Plugin for setting properties for the tests to run -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <systemProperties>
                        <property>
                            <name>javax.net.ssl.trustStore</name>
                            <value>${ssl.trustStore}</value>
                        </property>
                    </systemProperties>
                </configuration>
            </plugin>
            <!-- maven jar plugin to package all classes needed -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <!-- Plugin to make the ActiveDirectoryService the main class in the jar -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <!-- <classpathPrefix>lib/</classpathPrefix> -->
                            <mainClass>nl.roceindhoven.service.ActiveDirectoryService</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
 
    <reporting>
        <plugins>
            <!-- PMD -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-pmd-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <linkXref>true</linkXref>
                    <sourceEncoding>utf-8</sourceEncoding>
                    <minimumTokens>100</minimumTokens>
                    <targetJdk>${java.version}</targetJdk>
                </configuration>
            </plugin>
            <!-- Checkstyle -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-checkstyle-plugin</artifactId>
                <version>2.7</version>
            </plugin>
            <!-- Findbugs -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>findbugs-maven-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <findbugsXmlOutput>true</findbugsXmlOutput>
                    <findbugsXmlOutputDirectory>target/site</findbugsXmlOutputDirectory>
                </configuration>
            </plugin>
            <!-- Plugin for cross references -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jxr-plugin</artifactId>
                <version>2.3</version>
            </plugin>
        </plugins>
    </reporting>
 
    <dependencies>
        <!-- Spring Dependencies -->
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-core</artifactId>
            <version>1.3.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-core-tiger</artifactId>
            <version>1.3.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-odm</artifactId>
            <version>1.3.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-ldif-core</artifactId>
            <version>1.3.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-ldif-batch</artifactId>
            <version>1.3.1.RELEASE</version>
        </dependency>
        <!-- Test dependencies -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
 
    <!-- Add the Spring Maven Repositories to this project -->
    <repositories>
        <repository>
            <id>spring-release</id>
            <url>http://maven.springframework.org/release</url>
        </repository>
    </repositories>
</project>

With this setup, all the required libraries are included to connect to your AD. Next up is our applicationContext.xml. In here we define our beans which we can use to access the AD. This looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
    <bean id="contextSource"
          class="org.springframework.ldap.core.support.LdapContextSource">
        <!-- <property name="url" value="ldap://YOUR_SERVER:389"/> -->
        <property name="url" value="ldaps://YOUR_SERVER:636"/>
        <property name="base" value="OU=PRD,DC=redrock,DC=nl"/>
        <property name="userDn" value="CN=admin,OU=PRD,DC=redrock,DC=nl"/>
        <property name="password" value="MYPASSWORD"/>
        <property name="referral" value="follow"/>
        <property name="baseEnvironmentProperties">
            <map>
                <entry key="java.naming.security.authentication" value="simple"/>
            </map>
        </property>
    </bean>
     
    <bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
        <constructor-arg ref="contextSource"/>
    </bean>
     
    <bean id="ldapUserDAOBean" class="nl.redrock.dao.impl.UserDaoSpringImpl">
        <property name="ldapTemplate" ref="ldapTemplate"/>
    </bean>
     
</beans>

There are a few things to watch for. For example any spaces in the BASE or USERDN variable. Make sure you remove them so it looks like this: “OU=prd,DC=redrock,DC=nl” instead of this: “OU=prd, DC=redrock, DC=nl”. This might cause problems. Also remember that you can only set passwords for a user when you connect to the directory using HTTPS. This only changes the connection string from ldap://YOUR_SERVER:389 to ldaps://YOUR_SERVER:636. These are default ports btw.

Now we can create the UserDaoSpringImpl class and it’s UserDao interface. The UserDao can be quite simple. Let’s start with just looking up people in the Active Directory. The UserDao looks like this then:

package nl.redrock.dao;
 
import java.util.List;
 
import nl.redrock.domain.User;
import nl.redrock.domain.UserDetails;
 
/**
 * The User Data Access Object interface which can be used to manipulate users
 * in the active directory
 *
 * @author Hugo Hendriks
 *
 */
public interface UserDao {
 
    /**
     * retrieve all the users from the active directory
     *
     * @return a list of Users
     */
    public List getAllUsers();
 
}

An implementation of this would look like this:

package nl.redrock.dao.impl;
 
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
 
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
 
import nl.redrock.dao.UserDao;
import nl.redrock.domain.User;
import nl.redrock.domain.UserDetails;
import nl.redrock.exception.DuplicateUserException;
import nl.redrock.exception.LdapException;
import nl.redrock.exception.PasswordStrengthException;
import nl.redrock.utils.ActiveDirectoryUtils;
import nl.redrock.utils.UserContextMapper;
 
import org.springframework.ldap.NameAlreadyBoundException;
import org.springframework.ldap.OperationNotSupportedException;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.AbstractContextMapper;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.filter.Filter;
 
/**
 * The implementation of the User Data Access Object.
 *
 * @author Hugo
 *
 */
public class UserDaoSpringImpl implements UserDao {
 
    // Attribute names
    private static final String USER_ACCOUNT_CONTROL_ATTR_NAME = "userAccountControl";
    private static final String ACCOUNT_EXPIRES_ATTR_NAME = "accountExpires";
    private static final String PWD_LAST_SET_ATTR_NAME = "pwdLastSet";
    private static final String PASSWORD_ATTR_NAME = "unicodepwd";
    private static final String DISTINGUISHED_NAME_ATTR_NAME = "distinguishedname";
    private static final String MEMBER_ATTR_NAME = "member";
 
    // usercontrol params
    private static final int FLAG_TO_DISABLE_USER = 0x2;
    private static final int ADS_UF_DONT_EXPIRE_PASSWD = 0x10000;
    private static final int USER_CONTROL_NORMAL_USER = 512;
 
    private LdapTemplate ldapTemplate;
    ActiveDirectoryUtils activeDirectoryUtils = new ActiveDirectoryUtils();
 
    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List getAllUsers() {
        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        return ldapTemplate.search("", "(objectclass=person)", controls,
                new UserContextMapper());
    }
 
    public LdapTemplate getLdapTemplate() {
        return ldapTemplate;
    }
 
    public void setLdapTemplate(LdapTemplate ldapTemplate) {
        this.ldapTemplate = ldapTemplate;
    }
 
}

As you can see from the import I have more methods on the interface but I’m leaving them behind here due too the large nature of code then.

You can then write a class which creates the UserDao and exposes its methods….something like this:

package nl.redrock.service;
 
import java.util.List;
 
import nl.redrock.dao.UserDao;
import nl.redrock.dao.impl.UserDaoContextImpl;
import nl.redrock.dao.impl.UserDaoSpringImpl;
import nl.redrock.domain.User;
import nl.redrock.domain.UserDetails;
 
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;
 
public class ActiveDirectoryService {
 
    /**
     * Return all users
     *
     * @return
     */
    public static List getAllUsers() {
        return createDao().getAllUsers();
    }
 
    /**
     * Create the userDao
     *
     * @return UserDao
     */
    private static UserDao createDao() {
        Resource resource = new ClassPathResource("/applicationContext.xml");
        BeanFactory factory = new XmlBeanFactory(resource);
        return(UserDaoSpringImpl) factory.getBean("ldapUserDAOBean");
    }
 
}

The last thing to do now to see if it works is to write a test.

package nl.redrock.service;
 
import java.util.Iterator;
import java.util.List;
 
import junit.framework.TestCase;
import nl.redrock.domain.User;
import nl.redrock.domain.UserDetails;
 
public class ActiveDirectorySpringServiceTest extends TestCase {
 
    public void testGetAllUsers() {
        List result = ActiveDirectoryService.getAllUsers();
        assertTrue(!result.isEmpty());
        for (Iterator iterator = result.iterator(); iterator.hasNext();) {
            User user = (User) iterator.next();
            System.out.println(user.toString());
        }
    }
}

Run the test and you should be able to see all your user in the Active Directory from the base you have set. If you get an PKIX exception when running, it means you have a problem connecting over SSL to the AD. Your truststore is propably not set yet with the right certificate. You can use the InstallCert.java class to connect to the AD and generate a jssecacerts file. Put this file the root of you project and you should be fine running the test with maven as you have configured in the pom that it uses this JKS to run the tests.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <systemProperties>
            <property>
                <name>javax.net.ssl.trustStore</name>
                <value>${ssl.trustStore}</value>
            </property>
        </systemProperties>
    </configuration>
</plugin>

If you want to run in Eclipse for example you can set the VM arguments option on the Run Configuration: