{scrollbar}

Embedding ApacheDS as a Web Application

ApacheDS 1.5.5

This site was updated for ApacheDS 1.5.5.
Maven archetype depends on the current trunk.

My initial aim was to demonstrate embedding ApacheDS in a very simple, but nevertheless impressive way. I thought about embedding the server in Apache Tomcat first. But then I got a better plan: Creating a standard web application which wraps ApacheDS and can be deployed on any compliant application server. ApacheDS in a war-archive!

22list Version check

Although the concepts depicted below apply to all version of ApacheDS (even before 1.0), the configuration for starting and stopping the embedded server uses the style introduced with ApacheDS 1.5.5. Be sure that you use this version of the server, or a later one.

Solution Outline

Proof of concept character

Although it works well, please note that this is just an example on how to embed ApacheDS in an application! If you plan to run the server as LDAP production system, this is not the first option to consider. Some more steps have to be done, especially in the area of configuration.

The solution is quite simple. A web application carries all the necessary jar files for ApacheDS within the lib-directory of the WEB-INF folder. When the web application is started by the servlet container, appropriate code has to be executed to start ApacheDS. And the server has to be stopped, if the web application goes down (for instance if the server shuts down). There are (at least) two standard compliant ways to acomplish this:

  • A Servlet (automatically started with the web application, using the lifecycle methods init and destroy)
  • A ServletContextListener

In the following we have choosen the second option.

A Servlet Context Listener to start and stop ApacheDS

A servlet context listener receives notifications about changes to the servlet context of the web application it is part of. Documentation of the ServletContextListener interface can be found here. To receive notification events, the implementation class must be configured in the deployment descriptor for the web application. The two life cycle methods contextInitialized and contextDestroyed are suitable to start and stop ApacheDS.

A client within

After the server has been started from the Listener, it will be accessible from the outside via the network using LDAP. In order to demonstrate how to interact with the server from within the VM, a simple servlet is shown. It allows you to communicate with the embedded server via web browser. This is so simple, because the server already lives within a web application, only a servlet has to added to act as an entry point. Our sample servlet will display the Root DSE of the server.

The following class diagram visualizes the complete example. The gray elements will be developed in two steps and use Servlet and ApacheDS API.

Step 1: The web component which starts and stops the server

The ApacheDS core is comprised of JavaBeans components, and can easily be instantiated started and stopped with simple Java code. This is done by the following listener.

The class StartStopListener implements ServletContextListener and therefore contains the following two life cycle methods:

  • contextInitialized() is executed if the web application is started by the servlet container, it starts ApacheDS embedded
  • contextDestroyed() is executed if the web application is stopped by the servlet container, it stops the embedded server

The contextInitialized method creates a DefaultDirectoryService object. It configures the LDAP protocol and determines an appropriate working directory for the server. This directory is need to persist the partition data (entries). Our example uses a simple yet portable way for this task: the context attribute javax.servlet.context.tempdir.

Afterwards the method starts network protocol and directory service.

Finally the DirectoryService component is stored in the application context of the web application. This is done in order to provided it to embedded clients in the same web app (see the servlet below for an example).

The method contextDestroyed simply stops the protocol and shuts down the service.

javaStartStopListener.java package org.apache.directory.samples.embed.webapp; import java.io.File; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.apache.directory.server.core.DefaultDirectoryService; import org.apache.directory.server.core.DirectoryService; import org.apache.directory.server.ldap.LdapServer; import org.apache.directory.server.protocol.shared.transport.TcpTransport; /** * A Servlet context listener to start and stop ApacheDS. * * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> */ public class StartStopListener implements ServletContextListener { private DirectoryService directoryService; private LdapServer ldapServer; /** * Startup ApacheDS embedded. */ public void contextInitialized( ServletContextEvent evt ) { try { directoryService = new DefaultDirectoryService(); directoryService.setShutdownHookEnabled( true ); ldapServer = new LdapServer(); ldapServer.setDirectoryService( directoryService ); ldapServer.setAllowAnonymousAccess( true ); // Set LDAP port to 10389 TcpTransport ldapTransport = new TcpTransport( 10389 ); ldapServer.setTransports( ldapTransport ); // Determine an appropriate working directory ServletContext servletContext = evt.getServletContext(); File workingDir = ( File ) servletContext.getAttribute( "javax.servlet.context.tempdir" ); directoryService.setWorkingDirectory( workingDir ); directoryService.startup(); ldapServer.start(); // Store directoryService in context to provide it to servlets etc. servletContext.setAttribute( DirectoryService.JNDI_KEY, directoryService ); } catch ( Exception e ) { throw new RuntimeException( e ); } } /** * Shutdown ApacheDS embedded. */ public void contextDestroyed( ServletContextEvent evt ) { try { ldapServer.stop(); directoryService.shutdown(); } catch ( Exception e ) { throw new RuntimeException( e ); } } }

Deployment descriptor

In order to execute the listener code, the class has to be defined in the deployment descriptor of a web application, as depicted below:

xmlweb.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <display-name>ApacheDS embedded in a WebApp</display-name> <description> A simple yet portable way to run ApacheDS within a servlet container </description> <listener> <listener-class> org.apache.directory.samples.embed.webapp.StartStopListener </listener-class> </listener> </web-app>

Packaging and Deploying the WebApp

A standard web archive (war-File) is needed in order to deploy the application to a servlet container. The easiest way to create such a web archive including all dependencies is to use an Maven archetype we provide.

Creating the WebApp using the ApacheDS Maven Archetype

We assume you have Java Subversion and Maven 2.0.9 installed on your system.

To use the archetype you'll need to check it out and install it to your local repository:

svn co http://svn.apache.org/repos/asf/directory/documentation/samples/trunk/apacheds-archetype-webapp cd apacheds-archetype-webapp mvn install

Then change to your preferred location to create the new project and execute following command:

mvn archetype:generate -DarchetypeGroupId=org.apache.directory.samples \ -DarchetypeArtifactId=apacheds-archetype-webapp \ -DarchetypeVersion=1.5.5-SNAPSHOT \ -DgroupId=org.apache.directory.samples.embed.webapp \ -DartifactId=ApacheDS -Dversion=1.0-SNAPSHOT

Then change to the created directory and run the following command:

mvn package

This creates an ApacheDS.war file below the target folder.

Run on embedded Jetty

The fastest way to run the web application is to use the Maven Jetty plugin:

mvn jetty:run

The sample servlet 'RootDseServlet' is available at the following URL:

http://localhost:8080/ApacheDS/RootDse

true

A standard web archive (war-File) is needed in order to deploy the application to a servlet container. The Resources area at the end of this page provides a zip-File which contains the file structure. A build script for Apache Ant is included as well.

Directory layout for the sources, war file layout

The build script assumes that you have ApacheDS 1.5.5 and Tomcat 6.0.18 installed locally; it uses and (in the case of ApacheDS) copies the necessary files from their lib directories to the lib directory of the web application. You will likely want to adjust the installation directories defined in the build.xml file.

Note: Within the build script, Tomcat is only used for compilation. To be more concrete, only the servlet-api.jar is needed. Other options to provide this library at build time are imaginable, especially if you plan to deploy ApacheDS on a Web Application Server other than Tomcat.

After building the project, the classes folder will contain the compiled class files of the two Java classes above, and a properties file to configure the logging framework log4j. The lib folder will contain all jar-Files necessary, these are

  • antlr-2.7.7.jar
  • apacheds-bootstrap-extract-1.5.4.jar
  • apacheds-bootstrap-partition-1.5.4.jar
  • apacheds-core-1.5.4.jar
  • apacheds-core-avl-1.5.4.jar
  • apacheds-core-cursor-1.5.4.jar
  • apacheds-core-entry-1.5.4.jar
  • apacheds-core-jndi-1.5.4.jar
  • apacheds-core-shared-1.5.4.jar
  • apacheds-jdbm-1.5.4.jar
  • apacheds-jdbm-store-1.5.4.jar
  • apacheds-protocol-ldap-1.5.4.jar
  • apacheds-protocol-shared-1.5.4.jar
  • apacheds-schema-bootstrap-1.5.4.jar
  • apacheds-schema-registries-1.5.4.jar
  • apacheds-utils-1.5.4.jar
  • apacheds-xdbm-base-1.5.4.jar
  • apacheds-xdbm-search-1.5.4.jar
  • commons-collections-3.2.jar
  • commons-lang-2.3.jar
  • log4j-1.2.14.jar
  • mina-core-1.1.6.jar
  • mina-filter-ssl-1.1.6.jar
  • shared-asn1-0.9.12.jar
  • shared-asn1-codec-0.9.12.jar
  • shared-bouncycastle-reduced-0.9.12.jar
  • shared-ldap-0.9.12.jar
  • shared-ldap-constants-0.9.12.jar
  • slf4j-api-1.4.3.jar
  • slf4j-log4j12-1.4.3.jar

The webapp target in the build.xml file (which is the default target) packs the files for the web application together in a web archive called ApacheDS.war.

Deploying on Apache Tomcat

In order to run the application within Tomcat, simply put the ApacheDS.war file in the webapps directory of your Tomcat installation and start the server. If you have the manager application enabled (as described here), you can see and "manage" (start/stop) ApacheDS within its list view:

Connecting to ApacheDS from the outside

ApacheDS is up and running within the servlet container. Besides the administration tool listing, it seems to be invisible. But because we have configured network access via port 10389, you can easily access the server with an arbitrary LDAP client from outside.

One option is a command line tool like ldapsearch (see ApacheDS Basic User's Guide for details on how to connect to ApacheDS with such tools in general). Here is an example how to connect as administrator (simple bind) and fetch the Root DSE of our embedded ApacheDS instance:

none $ ldapsearch -h localhost -p 10389 -D "uid=admin,ou=system" -w secret \\ -b "" -s base "(objectClass=*)" * + version: 1 dn: supportedControl: 2.16.840.1.113730.3.4.3 supportedControl: 2.16.840.1.113730.3.4.7 supportedControl: 1.3.6.1.4.1.4203.1.10.1 supportedControl: 2.16.840.1.113730.3.4.2 supportedControl: 1.3.6.1.4.1.18060.0.0.1 namingContexts: ou=system namingContexts: ou=schema supportedLDAPVersion: 3 objectClass: extensibleObject objectClass: top supportedFeatures: 1.3.6.1.4.1.4203.1.5.1 supportedExtension: 1.3.6.1.4.1.1466.20036 subschemaSubentry: cn=schema vendorName: Apache Software Foundation vendorVersion: 1.5.4 $

Another choice are graphical LDAP clients (see ApacheDS Basic User's Guide for details on how to connect to ApacheDS with such tools in general).

With our popular Eclipse RCP application Directory studio for instance, connecting goes like this:
In the Connections view, select "New connection ...". Within a wizard dialog, you provide the connection data (host name, port, bind DN and password).

After successfully connecting to the embedded ApacheDS, you can browse the tree, add and manipulate entries and so on. If you check the connection properties, you can study the Root DSE as well.

Other Web Application Servers

The web application described here has been successfully deployed on

Here is a screen shot of the web based administration console of WebSphere Application Server 6.1 with the ApacheDS.war deployed and running, no changes in the deployment archive were needed.

Step 2: Adding functionality: A servlet which displays the Root DSE

To finish with, here is a simple example on how to access the server internally (Note: the servlet was already created by the maven archetype).

The following servlet, which will be deployed together with the other class in the web archive, connects to ApacheDS directly, i.e. via the internal JNDI provider. No network access is needed. In the doGet method it performs a search operation against the Root DSE of the server, as the examples above do.

javaRootDseServlet.java package org.apache.directory.samples.embed.webapp; import java.io.PrintWriter; import java.util.Hashtable; import javax.naming.Context; import javax.naming.NamingEnumeration; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.directory.server.core.DirectoryService; import org.apache.directory.server.core.jndi.CoreContextFactory; /** * A servlet which displays the Root DSE of the embedded server. * * @author <a href="mailto:dev@directory.apache.org">Apache Directory * Project</a> */ public class RootDseServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException { try { resp.setContentType("text/plain"); PrintWriter out = resp.getWriter(); out.println("*** ApacheDS RootDSE ***\n"); DirContext ctx = new InitialDirContext(this.createEnv()); SearchControls ctls = new SearchControls(); ctls.setReturningAttributes(new String[] { "*", "+" }); ctls.setSearchScope(SearchControls.OBJECT_SCOPE); NamingEnumeration<SearchResult> result = ctx.search("", "(objectClass=*)", ctls); if (result.hasMore()) { SearchResult entry = result.next(); Attributes as = entry.getAttributes(); NamingEnumeration<String> ids = as.getIDs(); while (ids.hasMore()) { String id = ids.next(); Attribute attr = as.get(id); for (int i = 0; i < attr.size(); ++i) { out.println(id + ": " + attr.get(i)); } } } ctx.close(); out.flush(); } catch (Exception e) { throw new ServletException(e); } } /** * Creates an environment configuration for JNDI access. */ protected Hashtable<Object, Object> createEnv() { // Fetch directory servive from servlet context ServletContext servletContext = this.getServletContext(); DirectoryService directoryService = (DirectoryService) servletContext .getAttribute(DirectoryService.JNDI_KEY); Hashtable<Object, Object> env = new Hashtable<Object, Object>(); env.put(DirectoryService.JNDI_KEY, directoryService); env.put(Context.PROVIDER_URL, ""); env.put(Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class .getName()); env.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system"); env.put(Context.SECURITY_CREDENTIALS, "secret"); env.put(Context.SECURITY_AUTHENTICATION, "simple"); return env; } }

In order to make the servlet available to clients, it has to be declared in the deployment descriptor web.xml, here are the additions (a servlet named RootDseServlet for the class above, and a URL mapping)

xmlweb.xml, extended <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> ... <servlet> <servlet-name>RootDseServlet</servlet-name> <servlet-class> org.apache.directory.samples.embed.webapp.RootDseServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>RootDseServlet</servlet-name> <url-pattern>/RootDse</url-pattern> </servlet-mapping> </web-app>

Redeploy the web application. If you point to your tomcat server with the appropriate URL (http://localhost:8080/ApacheDS/RootDse), you'll see the content of the Root DSE as depicted below:

true

Download the source code

StartStopListener.java (Step 1)
RootDseServlet.java (Step 2)
web.xml
ApacheDSWebApp.zip all sources including a build script for Apache Ant (build.xml)

  • No labels